From 0df684bc343a4b89ecf0a849fe8fefe52233386b Mon Sep 17 00:00:00 2001 From: Vlad Zhukov Date: Tue, 29 Mar 2022 13:16:39 +0300 Subject: [PATCH] Fix to add `_missingMdxReference` in some more cases Related-to GH-1986. Closes GH-1988. Reviewed-by: Titus Wormer --- packages/mdx/lib/plugin/recma-jsx-rewrite.js | 136 ++++++++++--------- packages/mdx/test/compile.js | 34 +++++ 2 files changed, 103 insertions(+), 67 deletions(-) diff --git a/packages/mdx/lib/plugin/recma-jsx-rewrite.js b/packages/mdx/lib/plugin/recma-jsx-rewrite.js index db2e16110..0858a0be5 100644 --- a/packages/mdx/lib/plugin/recma-jsx-rewrite.js +++ b/packages/mdx/lib/plugin/recma-jsx-rewrite.js @@ -243,25 +243,8 @@ export function recmaJsxRewrite(options = {}) { } } - /** @type {string} */ - let key - - // Add partials (so for `x.y.z` it’d generate `x` and `x.y` too). - for (key in scope.references) { - if (own.call(scope.references, key)) { - const parts = key.split('.') - let index = 0 - while (++index < parts.length) { - const partial = parts.slice(0, index).join('.') - if (!own.call(scope.references, partial)) { - scope.references[partial] = { - node: scope.references[key].node, - component: false - } - } - } - } - } + /** @type {Array} */ + const statements = [] if (defaults.length > 0 || actual.length > 0) { if (providerImportSource) { @@ -356,60 +339,79 @@ export function recmaJsxRewrite(options = {}) { }) } - // Arrow functions with an implied return: - if (fn.body.type !== 'BlockStatement') { - fn.body = { - type: 'BlockStatement', - body: [{type: 'ReturnStatement', argument: fn.body}] + statements.push({ + type: 'VariableDeclaration', + kind: 'const', + declarations + }) + } + + /** @type {string} */ + let key + + // Add partials (so for `x.y.z` it’d generate `x` and `x.y` too). + for (key in scope.references) { + if (own.call(scope.references, key)) { + const parts = key.split('.') + let index = 0 + while (++index < parts.length) { + const partial = parts.slice(0, index).join('.') + if (!own.call(scope.references, partial)) { + scope.references[partial] = { + node: scope.references[key].node, + component: false + } + } } } + } - /** @type {Array} */ - const statements = [ - { - type: 'VariableDeclaration', - kind: 'const', - declarations - } + const references = Object.keys(scope.references).sort() + let index = -1 + while (++index < references.length) { + const id = references[index] + const info = scope.references[id] + const place = stringifyPosition(positionFromEstree(info.node)) + /** @type {Array} */ + const parameters = [ + {type: 'Literal', value: id}, + {type: 'Literal', value: info.component} ] - const references = Object.keys(scope.references).sort() - let index = -1 - while (++index < references.length) { - const id = references[index] - const info = scope.references[id] - const place = stringifyPosition(positionFromEstree(info.node)) - /** @type {Array} */ - const parameters = [ - {type: 'Literal', value: id}, - {type: 'Literal', value: info.component} - ] - - createErrorHelper = true - - if (development && place !== '1:1-1:1') { - parameters.push({type: 'Literal', value: place}) - } + createErrorHelper = true - statements.push({ - type: 'IfStatement', - test: { - type: 'UnaryExpression', - operator: '!', - prefix: true, - argument: toIdOrMemberExpression(id.split('.')) - }, - consequent: { - type: 'ExpressionStatement', - expression: { - type: 'CallExpression', - callee: {type: 'Identifier', name: '_missingMdxReference'}, - arguments: parameters, - optional: false - } - }, - alternate: null - }) + if (development && place !== '1:1-1:1') { + parameters.push({type: 'Literal', value: place}) + } + + statements.push({ + type: 'IfStatement', + test: { + type: 'UnaryExpression', + operator: '!', + prefix: true, + argument: toIdOrMemberExpression(id.split('.')) + }, + consequent: { + type: 'ExpressionStatement', + expression: { + type: 'CallExpression', + callee: {type: 'Identifier', name: '_missingMdxReference'}, + arguments: parameters, + optional: false + } + }, + alternate: null + }) + } + + if (statements.length > 0) { + // Arrow functions with an implied return: + if (fn.body.type !== 'BlockStatement') { + fn.body = { + type: 'BlockStatement', + body: [{type: 'ReturnStatement', argument: fn.body}] + } } fn.body.body.unshift(...statements) diff --git a/packages/mdx/test/compile.js b/packages/mdx/test/compile.js index 9b19012a9..b98cf6c16 100644 --- a/packages/mdx/test/compile.js +++ b/packages/mdx/test/compile.js @@ -504,6 +504,40 @@ test('compile', async () => { ) } + // TODO: this is incorrect behavior, will be fixed in GH-1986 + try { + renderToStaticMarkup( + React.createElement( + await run(compileSync('export const a = {}\n\n')) + ) + ) + assert.unreachable() + } catch (/** @type {unknown} */ error) { + const exception = /** @type {Error} */ (error) + assert.match( + exception.message, + /Expected component `a.b` to be defined/, + 'should throw if a used member is not defined locally' + ) + } + + // TODO: this is incorrect behavior, will be fixed in GH-1986 + try { + renderToStaticMarkup( + React.createElement( + await run(compileSync(' } />')) + ) + ) + assert.unreachable() + } catch (/** @type {unknown} */ error) { + const exception = /** @type {Error} */ (error) + assert.match( + exception.message, + /x is not defined/, + 'should throw if a used member is not defined locally (JSX in a function)' + ) + } + try { renderToStaticMarkup( React.createElement(await run(compileSync('', {development: true})))