Comparing version 2.0.3 to 2.0.4
@@ -18,2 +18,3 @@ /** | ||
> | ||
export type Node = import('estree-jsx').Node | ||
export type Expression = import('estree-jsx').Expression | ||
@@ -32,2 +33,5 @@ export type ESFunction = import('estree-jsx').Function | ||
export type WalkHandler = import('estree-walker').SyncHandler | ||
export type Scope = import('periscopic').Scope & { | ||
node: Node | ||
} | ||
export type RecmaJsxRewriteOptions = { | ||
@@ -47,2 +51,3 @@ /** | ||
tags: Array<string> | ||
node: ESFunction | ||
} |
/** | ||
* @typedef {import('estree-jsx').Node} Node | ||
* @typedef {import('estree-jsx').Expression} Expression | ||
@@ -17,2 +18,4 @@ * @typedef {import('estree-jsx').Function} ESFunction | ||
* | ||
* @typedef {import('periscopic').Scope & {node: Node}} Scope | ||
* | ||
* @typedef RecmaJsxRewriteOptions | ||
@@ -26,2 +29,3 @@ * @property {'program'|'function-body'} [outputFormat='program'] Whether to use an import statement or `arguments[0]` to get the provider | ||
* @property {Array.<string>} tags | ||
* @property {ESFunction} node | ||
*/ | ||
@@ -48,10 +52,14 @@ | ||
// Find everything that’s defined in the top-level scope. | ||
const topScope = analyze(tree).scope.declarations | ||
const scopeInfo = analyze(tree) | ||
/** @type {Array.<StackEntry>} */ | ||
const stack = [] | ||
const fnStack = [] | ||
/** @type {boolean|undefined} */ | ||
let importProvider | ||
/** @type {Scope|null} */ | ||
let currentScope | ||
walk(tree, { | ||
enter(node) { | ||
enter(_node) { | ||
const node = /** @type {Node} */ (_node) | ||
if ( | ||
@@ -62,13 +70,27 @@ node.type === 'FunctionDeclaration' || | ||
) { | ||
stack.push({objects: [], components: [], tags: []}) | ||
fnStack.push({objects: [], components: [], tags: [], node}) | ||
} | ||
if (node.type === 'JSXElement' && stack.length > 0) { | ||
const element = /** @type {JSXElement} */ (node) | ||
// Note: inject into the *top-level* function that contains JSX. | ||
// Yes: we collect info about the stack, but we assume top-level functions | ||
// are components. | ||
const scope = stack[0] | ||
let name = element.openingElement.name | ||
const fnScope = fnStack[0] | ||
if ( | ||
!fnScope || | ||
(!isMdxContent(fnScope.node) && !providerImportSource) | ||
) { | ||
return | ||
} | ||
const newScope = /** @type {Scope|undefined} */ ( | ||
// @ts-expect-error: periscopic doesn’t support JSX. | ||
scopeInfo.map.get(node) | ||
) | ||
if (newScope) { | ||
newScope.node = node | ||
currentScope = newScope | ||
} | ||
if (currentScope && node.type === 'JSXElement') { | ||
let name = node.openingElement.name | ||
// `<x.y>`, `<Foo.Bar>`, `<x.y.z>`. | ||
@@ -79,7 +101,6 @@ if (name.type === 'JSXMemberExpression') { | ||
if ( | ||
!scope.objects.includes(name.name) && | ||
!topScope.has(name.name) | ||
) { | ||
scope.objects.push(name.name) | ||
const id = name.name | ||
if (!fnScope.objects.includes(id) && !inScope(currentScope, id)) { | ||
fnScope.objects.push(id) | ||
} | ||
@@ -96,7 +117,9 @@ } | ||
else if (isIdentifierName(name.name) && !/^[a-z]/.test(name.name)) { | ||
const id = name.name | ||
if ( | ||
!scope.components.includes(name.name) && | ||
!topScope.has(name.name) | ||
!fnScope.components.includes(id) && | ||
!inScope(currentScope, id) | ||
) { | ||
scope.components.push(name.name) | ||
fnScope.components.push(id) | ||
} | ||
@@ -106,3 +129,3 @@ } | ||
// esast. | ||
else if (element.data && element.data._xdmExplicitJsx) { | ||
else if (node.data && node.data._xdmExplicitJsx) { | ||
// Do not turn explicit JSX into components from `_components`. | ||
@@ -112,7 +135,9 @@ // As in, a given `h1` component is used for `# heading` (next case), | ||
} else { | ||
if (!scope.tags.includes(name.name)) { | ||
scope.tags.push(name.name) | ||
const id = name.name | ||
if (!fnScope.tags.includes(id)) { | ||
fnScope.tags.push(id) | ||
} | ||
element.openingElement.name = { | ||
node.openingElement.name = { | ||
type: 'JSXMemberExpression', | ||
@@ -123,7 +148,7 @@ object: {type: 'JSXIdentifier', name: '_components'}, | ||
if (element.closingElement) { | ||
element.closingElement.name = { | ||
if (node.closingElement) { | ||
node.closingElement.name = { | ||
type: 'JSXMemberExpression', | ||
object: {type: 'JSXIdentifier', name: '_components'}, | ||
property: {type: 'JSXIdentifier', name: name.name} | ||
property: {type: 'JSXIdentifier', name: id} | ||
} | ||
@@ -144,2 +169,7 @@ } | ||
if (currentScope && currentScope.node === node) { | ||
// @ts-expect-error: `node`s were patched when entering. | ||
currentScope = currentScope.parent | ||
} | ||
if ( | ||
@@ -151,10 +181,6 @@ node.type === 'FunctionDeclaration' || | ||
const fn = /** @type {ESFunction} */ (node) | ||
const scope = stack.pop() | ||
const scope = fnStack[fnStack.length - 1] | ||
/** @type {string} */ | ||
let name | ||
// Supported for types but our stack is good! | ||
/* c8 ignore next 1 */ | ||
if (!scope) throw new Error('Expected scope on stack') | ||
for (name of scope.tags) { | ||
@@ -196,7 +222,3 @@ defaults.push({ | ||
// Accept `components` as a prop if this is the `MDXContent` function. | ||
if ( | ||
fn.type === 'FunctionDeclaration' && | ||
fn.id && | ||
fn.id.name === 'MDXContent' | ||
) { | ||
if (isMdxContent(scope.node)) { | ||
parameters.push({ | ||
@@ -214,17 +236,14 @@ type: 'MemberExpression', | ||
id: {type: 'Identifier', name: '_components'}, | ||
init: | ||
parameters.length > 1 | ||
? { | ||
type: 'CallExpression', | ||
callee: { | ||
type: 'MemberExpression', | ||
object: {type: 'Identifier', name: 'Object'}, | ||
property: {type: 'Identifier', name: 'assign'}, | ||
computed: false, | ||
optional: false | ||
}, | ||
arguments: parameters, | ||
optional: false | ||
} | ||
: parameters[0] | ||
init: { | ||
type: 'CallExpression', | ||
callee: { | ||
type: 'MemberExpression', | ||
object: {type: 'Identifier', name: 'Object'}, | ||
property: {type: 'Identifier', name: 'assign'}, | ||
computed: false, | ||
optional: false | ||
}, | ||
arguments: parameters, | ||
optional: false | ||
} | ||
}) | ||
@@ -275,2 +294,4 @@ | ||
} | ||
fnStack.pop() | ||
} | ||
@@ -328,1 +349,31 @@ } | ||
} | ||
/** | ||
* @param {ESFunction} [node] | ||
* @returns {boolean} | ||
*/ | ||
function isMdxContent(node) { | ||
return Boolean( | ||
node && 'id' in node && node.id && node.id.name === 'MDXContent' | ||
) | ||
} | ||
/** | ||
* @param {Scope} scope | ||
* @param {string} id | ||
*/ | ||
function inScope(scope, id) { | ||
/** @type {Scope|null} */ | ||
let currentScope = scope | ||
while (currentScope) { | ||
if (currentScope.declarations.has(id)) { | ||
return true | ||
} | ||
// @ts-expect-error: `node`s have been added when entering. | ||
currentScope = currentScope.parent | ||
} | ||
return false | ||
} |
{ | ||
"name": "xdm", | ||
"version": "2.0.3", | ||
"version": "2.0.4", | ||
"description": "an MDX compiler", | ||
@@ -69,3 +69,3 @@ "license": "MIT", | ||
"@mdx-js/react": "2.0.0-next.8", | ||
"@theme-ui/preset-base": "^0.10.0", | ||
"@theme-ui/preset-base": "^0.11.0", | ||
"@types/babel__core": "^7.0.0", | ||
@@ -102,3 +102,3 @@ "@types/loader-utils": "^2.0.0", | ||
"tape": "^5.0.0", | ||
"theme-ui": "^0.10.0", | ||
"theme-ui": "^0.11.0", | ||
"type-coverage": "^2.0.0", | ||
@@ -105,0 +105,0 @@ "typescript": "^4.0.0", |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
169790
2847
2425