Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix to add _missingMdxReference in some more cases #1988

Merged
merged 4 commits into from Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
136 changes: 69 additions & 67 deletions packages/mdx/lib/plugin/recma-jsx-rewrite.js
Expand Up @@ -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<Statement>} */
const statements = []

if (defaults.length > 0 || actual.length > 0) {
if (providerImportSource) {
Expand Down Expand Up @@ -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<Statement>} */
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<Expression>} */
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<Expression>} */
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)
Expand Down
34 changes: 34 additions & 0 deletions packages/mdx/test/compile.js
Expand Up @@ -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<a.b />'))
)
)
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('<a render={(x) => <x.y />} />'))
)
)
assert.unreachable()
} catch (/** @type {unknown} */ error) {
const exception = /** @type {Error} */ (error)
assert.match(
exception.message,
/x is not defined/,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, why is this error thrown here? Is render being called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does. That's exactly the issue I fix in #1986. This line is added:

if (!x) _missingMdxReference("x", false);',

But x is not there, it's defined inside an arrow function.

'should throw if a used member is not defined locally (JSX in a function)'
)
}

try {
renderToStaticMarkup(
React.createElement(await run(compileSync('<X />', {development: true})))
Expand Down