From 9904838aa091b23ba16815bf7c0ff94895ae0f4b Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Wed, 17 Aug 2022 14:08:32 -0400 Subject: [PATCH] Fix rewriting of components for custom elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit particularly solves elements names that include dashes: custom elements, such as `custom-element-name`. These can be written in JSX like so: ```jsx ``` …which is fine. But to support passing them in, we were previously rewriting such code to a member id, to take a key from an object, like so: ```jsx <_components.custom-element-name> ``` …which crashed. This commit solves that by taking the component from the object in a temporary variable, and using that valid variable name as a single component name. ```js const _component0 = _components['custom-element-name'] <_component0 /> ``` Closes GH-2100. Closes GH-2101. Co-authored-by: Titus Wormer Reviewed-by: Titus Wormer --- packages/mdx/lib/plugin/recma-jsx-rewrite.js | 53 ++++++++++++++++---- packages/mdx/test/compile.js | 4 +- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/packages/mdx/lib/plugin/recma-jsx-rewrite.js b/packages/mdx/lib/plugin/recma-jsx-rewrite.js index d50b550a7..1e82a1e41 100644 --- a/packages/mdx/lib/plugin/recma-jsx-rewrite.js +++ b/packages/mdx/lib/plugin/recma-jsx-rewrite.js @@ -72,6 +72,8 @@ export function recmaJsxRewrite(options = {}) { let createErrorHelper /** @type {Scope|null} */ let currentScope + /** @type {Map} */ + const idToInvalidComponentName = new Map() walk(tree, { enter(_node) { @@ -193,16 +195,24 @@ export function recmaJsxRewrite(options = {}) { fnScope.tags.push(id) } - node.openingElement.name = toJsxIdOrMemberExpression([ - '_components', - id - ]) + /** @type {Array} */ + let jsxIdExpression = ['_components', id] + if (isIdentifierName(id) === false) { + let invalidComponentName = idToInvalidComponentName.get(id) + if (invalidComponentName === undefined) { + invalidComponentName = `_component${idToInvalidComponentName.size}` + idToInvalidComponentName.set(id, invalidComponentName) + } + + jsxIdExpression = [invalidComponentName] + } + + node.openingElement.name = + toJsxIdOrMemberExpression(jsxIdExpression) if (node.closingElement) { - node.closingElement.name = toJsxIdOrMemberExpression([ - '_components', - id - ]) + node.closingElement.name = + toJsxIdOrMemberExpression(jsxIdExpression) } } } @@ -259,7 +269,11 @@ export function recmaJsxRewrite(options = {}) { /** @type {Array} */ const statements = [] - if (defaults.length > 0 || actual.length > 0) { + if ( + defaults.length > 0 || + actual.length > 0 || + idToInvalidComponentName.size > 0 + ) { if (providerImportSource) { importProvider = true parameters.push({ @@ -344,6 +358,27 @@ export function recmaJsxRewrite(options = {}) { componentsInit = {type: 'Identifier', name: '_components'} } + if (isNamedFunction(scope.node, '_createMdxContent')) { + for (const [id, componentName] of idToInvalidComponentName) { + // For JSX IDs that can’t be represented as JavaScript IDs (as in, + // those with dashes, such as `custom-element`), generate a + // separate variable that is a valid JS ID (such as `_component0`), + // and takes it from components: + // `const _component0 = _components['custom-element']` + declarations.push({ + type: 'VariableDeclarator', + id: {type: 'Identifier', name: componentName}, + init: { + type: 'MemberExpression', + object: {type: 'Identifier', name: '_components'}, + property: {type: 'Literal', value: id}, + computed: true, + optional: false + } + }) + } + } + if (componentsPattern) { declarations.push({ type: 'VariableDeclarator', diff --git a/packages/mdx/test/compile.js b/packages/mdx/test/compile.js index 8b6d457a3..d00059758 100644 --- a/packages/mdx/test/compile.js +++ b/packages/mdx/test/compile.js @@ -860,8 +860,8 @@ test('jsx', async () => { 'function _createMdxContent(props) {', ' const _components = Object.assign({', ' "a-b": "a-b"', - ' }, props.components);', - ' return <>{<_components.a-b>};', + ' }, props.components), _component0 = _components["a-b"];', + ' return <>{<_component0>};', '}', 'function MDXContent(props = {}) {', ' const {wrapper: MDXLayout} = props.components || ({});',