From d24a339a691ddd1c0483446c9a5709af45368e3e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 23 Apr 2022 15:33:09 +0200 Subject: [PATCH 1/2] Add support for `baseUrl` rewriting `import.meta.url` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MDX can compile *and* run code, at the same time (called “evaluation”). To evaluate imports and exports in that code, it has to know *from where* to do that, which is a location on the users computer that is not the folder that contains `@mdx-js/mdx` in `node_modules`. That’s what the `baseUrl` option is used for. Previously though, the URL given as `baseUrl` was not used to replace `import.meta.url` in MDX code. As they are equivalent, this commit introduces that behavior. --- packages/mdx/lib/plugin/recma-document.js | 39 ++++++++++++++++++++++- packages/mdx/readme.md | 4 +-- packages/mdx/test/evaluate.js | 11 +++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/mdx/lib/plugin/recma-document.js b/packages/mdx/lib/plugin/recma-document.js index e0cedaf68..5f59aaeee 100644 --- a/packages/mdx/lib/plugin/recma-document.js +++ b/packages/mdx/lib/plugin/recma-document.js @@ -26,7 +26,8 @@ * Whether to keep `import` (and `export … from`) statements or compile them * to dynamic `import()` instead. * @property {string} [baseUrl] - * Resolve relative `import` (and `export … from`) relative to this URL. + * Resolve `import`s (and `export … from`, and `import.meta.url`) relative to + * this URL. * @property {string} [pragma='React.createElement'] * Pragma for JSX (used in classic runtime). * @property {string} [pragmaFrag='React.Fragment'] @@ -42,6 +43,7 @@ import {analyze} from 'periscopic' import {stringifyPosition} from 'unist-util-stringify-position' import {positionFromEstree} from 'unist-util-position-from-estree' +import {visit, SKIP} from 'estree-util-visit' import {create} from '../util/estree-util-create.js' import {specifiersToDeclarations} from '../util/estree-util-specifiers-to-declarations.js' import {declarationToExpression} from '../util/estree-util-declaration-to-expression.js' @@ -296,6 +298,41 @@ export function recmaDocument(options = {}) { tree.body = replacement + if (baseUrl) { + visit(tree, (node, field, index, parents) => { + if ( + node.type === 'MemberExpression' && + 'object' in node && + node.object.type === 'MetaProperty' && + node.property.type === 'Identifier' && + node.object.meta.name === 'import' && + node.object.property.name === 'meta' && + node.property.name === 'url' + ) { + /** @type {string|number|null} */ + let prop = field + let context = /** @type {Node|Array} */ ( + parents[parents.length - 1] + ) + + // Remove non-standard `ParenthesizedExpression`. + if (context && prop) { + /* c8 ignore next 5 */ + if (typeof index === 'number') { + // @ts-expect-error: indexable. + context = context[prop] + prop = index + } + + // @ts-expect-error: indexable. + context[prop] = {type: 'Literal', value: baseUrl} + } + + return SKIP + } + }) + } + /** * @param {ExportNamedDeclaration|ExportAllDeclaration} node * @returns {void} diff --git a/packages/mdx/readme.md b/packages/mdx/readme.md index 17448e9d3..8992b2950 100644 --- a/packages/mdx/readme.md +++ b/packages/mdx/readme.md @@ -360,8 +360,8 @@ return {no, default: MDXContent} ###### `options.baseUrl` -Resolve relative `import` (and `export … from`) from this URL (`string?`, -example: `import.meta.url`). +Resolve `import`s (and `export … from`, and `import.meta.url`) from this URL +(`string?`, example: `import.meta.url`). Relative specifiers are non-absolute URLs that start with `/`, `./`, or `../`. For example: `/index.js`, `./folder/file.js`, or `../main.js`. diff --git a/packages/mdx/test/evaluate.js b/packages/mdx/test/evaluate.js index b15aa590b..fa7fbe792 100644 --- a/packages/mdx/test/evaluate.js +++ b/packages/mdx/test/evaluate.js @@ -290,6 +290,17 @@ test('evaluate', async () => { 'should support an `export all from`, but prefer explicit exports, w/ `useDynamicImport`' ) + assert.equal( + ( + await evaluate( + 'export const x = new URL("example.png", import.meta.url).href', + {baseUrl: 'https://example.com', ...runtime} + ) + ).x, + 'https://example.com/example.png', + 'should support rewriting `import.meta.url` w/ `baseUrl`' + ) + assert.throws( () => { evaluateSync('export * from "a"', runtime) From 9506240808a66c48d1cfc3387746a7644f44edac Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 23 Apr 2022 15:43:17 +0200 Subject: [PATCH 2/2] use dep --- packages/mdx/lib/plugin/recma-document.js | 45 ++++++++--------------- packages/mdx/test/compile.js | 2 + 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/packages/mdx/lib/plugin/recma-document.js b/packages/mdx/lib/plugin/recma-document.js index 5f59aaeee..5ffa5af7f 100644 --- a/packages/mdx/lib/plugin/recma-document.js +++ b/packages/mdx/lib/plugin/recma-document.js @@ -43,7 +43,7 @@ import {analyze} from 'periscopic' import {stringifyPosition} from 'unist-util-stringify-position' import {positionFromEstree} from 'unist-util-position-from-estree' -import {visit, SKIP} from 'estree-util-visit' +import {walk} from 'estree-walker' import {create} from '../util/estree-util-create.js' import {specifiersToDeclarations} from '../util/estree-util-specifiers-to-declarations.js' import {declarationToExpression} from '../util/estree-util-declaration-to-expression.js' @@ -299,36 +299,23 @@ export function recmaDocument(options = {}) { tree.body = replacement if (baseUrl) { - visit(tree, (node, field, index, parents) => { - if ( - node.type === 'MemberExpression' && - 'object' in node && - node.object.type === 'MetaProperty' && - node.property.type === 'Identifier' && - node.object.meta.name === 'import' && - node.object.property.name === 'meta' && - node.property.name === 'url' - ) { - /** @type {string|number|null} */ - let prop = field - let context = /** @type {Node|Array} */ ( - parents[parents.length - 1] - ) - - // Remove non-standard `ParenthesizedExpression`. - if (context && prop) { - /* c8 ignore next 5 */ - if (typeof index === 'number') { - // @ts-expect-error: indexable. - context = context[prop] - prop = index - } + walk(tree, { + enter(_node) { + const node = /** @type {Node} */ (_node) - // @ts-expect-error: indexable. - context[prop] = {type: 'Literal', value: baseUrl} + if ( + node.type === 'MemberExpression' && + 'object' in node && + node.object.type === 'MetaProperty' && + node.property.type === 'Identifier' && + node.object.meta.name === 'import' && + node.object.property.name === 'meta' && + node.property.name === 'url' + ) { + /** @type {SimpleLiteral} */ + const replacement = {type: 'Literal', value: baseUrl} + this.replace(replacement) } - - return SKIP } }) } diff --git a/packages/mdx/test/compile.js b/packages/mdx/test/compile.js index 70db8b800..4385ec205 100644 --- a/packages/mdx/test/compile.js +++ b/packages/mdx/test/compile.js @@ -536,6 +536,7 @@ test('compile', async () => { ) } + console.log('\nnote: the following warning is expected!\n') assert.equal( renderToStaticMarkup( React.createElement( @@ -545,6 +546,7 @@ test('compile', async () => { '', 'should render if a used member is defined locally (JSX in a function)' ) + console.log('\nnote: the preceding warning is expected!\n') try { renderToStaticMarkup(