diff --git a/packages/mdx/lib/core.js b/packages/mdx/lib/core.js index 96e34d709..1d3aec731 100644 --- a/packages/mdx/lib/core.js +++ b/packages/mdx/lib/core.js @@ -125,7 +125,7 @@ export function createProcessor(options = {}) { .use(recmaJsxRewrite, {development, providerImportSource, outputFormat}) if (!jsx) { - pipeline.use(recmaJsxBuild, {outputFormat}) + pipeline.use(recmaJsxBuild, {development, outputFormat}) } pipeline.use(recmaStringify, {SourceMapGenerator}).use(recmaPlugins || []) diff --git a/packages/mdx/lib/plugin/recma-jsx-build.js b/packages/mdx/lib/plugin/recma-jsx-build.js index e15e733ed..f7e14b021 100644 --- a/packages/mdx/lib/plugin/recma-jsx-build.js +++ b/packages/mdx/lib/plugin/recma-jsx-build.js @@ -1,5 +1,6 @@ /** * @typedef {import('estree-jsx').Program} Program + * @typedef {import('estree-util-build-jsx').BuildJsxOptions} BuildJsxOptions * * @typedef RecmaJsxBuildOptions * @property {'program'|'function-body'} [outputFormat='program'] @@ -15,13 +16,13 @@ import {toIdOrMemberExpression} from '../util/estree-util-to-id-or-member-expres * A plugin to build JSX into function calls. * `estree-util-build-jsx` does all the work for us! * - * @type {import('unified').Plugin<[RecmaJsxBuildOptions]|[], Program>} + * @type {import('unified').Plugin<[BuildJsxOptions & RecmaJsxBuildOptions?], Program>} */ export function recmaJsxBuild(options = {}) { - const {outputFormat} = options + const {development, outputFormat} = options - return (tree) => { - buildJsx(tree) + return (tree, file) => { + buildJsx(tree, {development, filePath: file.history[0]}) // When compiling to a function body, replace the import that was just // generated, and get `jsx`, `jsxs`, and `Fragment` from `arguments[0]` @@ -31,7 +32,7 @@ export function recmaJsxBuild(options = {}) { tree.body[0] && tree.body[0].type === 'ImportDeclaration' && typeof tree.body[0].source.value === 'string' && - /\/jsx-runtime$/.test(tree.body[0].source.value) + /\/jsx-(dev-)?runtime$/.test(tree.body[0].source.value) ) { tree.body[0] = { type: 'VariableDeclaration', diff --git a/packages/mdx/lib/util/resolve-evaluate-options.js b/packages/mdx/lib/util/resolve-evaluate-options.js index 45cfefdf1..de73f412e 100644 --- a/packages/mdx/lib/util/resolve-evaluate-options.js +++ b/packages/mdx/lib/util/resolve-evaluate-options.js @@ -4,10 +4,12 @@ * @typedef RunnerOptions * @property {*} Fragment * Symbol to use for fragments. - * @property {*} jsx - * Function to generate an element with static children. - * @property {*} jsxs - * Function to generate an element with dynamic children. + * @property {*} [jsx] + * Function to generate an element with static children in production mode. + * @property {*} [jsxs] + * Function to generate an element with dynamic children in production mode. + * @property {*} [jsxDEV] + * Function to generate an element in development mode. * @property {*} [useMDXComponents] * Function to get `MDXComponents` from context. * @@ -23,18 +25,24 @@ * @returns {{compiletime: ProcessorOptions, runtime: RunnerOptions}} */ export function resolveEvaluateOptions(options) { - const {Fragment, jsx, jsxs, useMDXComponents, ...rest} = options || {} + const {development, Fragment, jsx, jsxs, jsxDEV, useMDXComponents, ...rest} = + options || {} if (!Fragment) throw new Error('Expected `Fragment` given to `evaluate`') - if (!jsx) throw new Error('Expected `jsx` given to `evaluate`') - if (!jsxs) throw new Error('Expected `jsxs` given to `evaluate`') + if (development) { + if (!jsxDEV) throw new Error('Expected `jsxDEV` given to `evaluate`') + } else { + if (!jsx) throw new Error('Expected `jsx` given to `evaluate`') + if (!jsxs) throw new Error('Expected `jsxs` given to `evaluate`') + } return { compiletime: { ...rest, + development, outputFormat: 'function-body', providerImportSource: useMDXComponents ? '#' : undefined }, - runtime: {Fragment, jsx, jsxs, useMDXComponents} + runtime: {Fragment, jsx, jsxs, jsxDEV, useMDXComponents} } } diff --git a/packages/mdx/test/compile.js b/packages/mdx/test/compile.js index ea6ae715f..a59f23d49 100644 --- a/packages/mdx/test/compile.js +++ b/packages/mdx/test/compile.js @@ -543,6 +543,22 @@ test('compile', async () => { ) console.log('\nnote: the preceding warning is expected!\n') + const developmentSourceNode = ( + await run( + compileSync( + {value: '
', path: 'path/to/file.js'}, + {development: true} + ).value + ) + )({}) + assert.equal( + // @ts-expect-error React attaches source information on this property, + // but it’s private and untyped. + developmentSourceNode._source, + {fileName: 'path/to/file.js', lineNumber: 1, columnNumber: 1}, + 'should expose source information in the automatic jsx dev runtime' + ) + try { renderToStaticMarkup( React.createElement(await run(compileSync('a