From f88955bc5ec379b384a7b71158cde200546b3bff Mon Sep 17 00:00:00 2001 From: Guyutongxue Date: Sun, 31 Aug 2025 20:01:18 +0800 Subject: [PATCH 1/4] feat(language-service): Make mdx nodes typed with MDX.IntrinsicElements --- packages/language-service/lib/virtual-code.js | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/language-service/lib/virtual-code.js b/packages/language-service/lib/virtual-code.js index bd457bfa..599befce 100644 --- a/packages/language-service/lib/virtual-code.js +++ b/packages/language-service/lib/virtual-code.js @@ -24,6 +24,25 @@ import {isInjectableComponent, isInjectableEstree} from './jsx-utils.js' */ const jsPrefix = (jsxImportSource) => `/* @jsxRuntime automatic @jsxImportSource ${jsxImportSource} */ + +/** + * @internal + * @template T + * @typedef {void extends MDX.IntrinsicElements ? any : T extends keyof MDX.IntrinsicElements ? MDX.IntrinsicElements[T] : any} _MDXProps + */ + +/** + * @internal + * @typedef {void extends MDX.Element ? JSX.Element : MDX.Element} _MDXElement + */ + +/** + * @internal + * @type {{ + * [Name in keyof _MDXNodeNames]: (props: _MDXProps) => _MDXElement + * }} + */ +const _MDXNodes = /** @type {any} */(0); ` /** @@ -247,6 +266,8 @@ function getEmbeddedCodes( let jsxVariables = '' let markdown = '' let nextMarkdownSourceStart = 0 + /** @type {Set} */ + const foundMdxNodeNames = new Set() const plugins = virtualCodePlugins.map((plugin) => plugin()) /** @type {CodeMapping[]} */ @@ -739,7 +760,8 @@ function getEmbeddedCodes( } default: { - jsx += jsxIndent + '<>' + foundMdxNodeNames.add(node.type) + jsx += jsxIndent + `<_MDXNodes.${node.type}>` break } } @@ -789,7 +811,7 @@ function getEmbeddedCodes( } default: { - jsx += jsxIndent + '' + jsx += jsxIndent + `` break } } @@ -820,6 +842,12 @@ function getEmbeddedCodes( esmMapping.lengths.unshift(0) } + esm += `/** + * @typedef {object} _MDXNodeNames +${[...foundMdxNodeNames].map((name) => ` * @property {unknown} ${name}`).join('\n')} + */ +` + updateMarkdownFromOffsets(mdx.length, mdx.length) esm += componentStart(hasAwait, programScope) From f0dc9b8487fb7683b9f0c5ec6f3e3f2e72f32d0d Mon Sep 17 00:00:00 2001 From: Guyutongxue Date: Sun, 7 Sep 2025 15:00:39 +0800 Subject: [PATCH 2/4] fix(language-service): use JSX node name map --- packages/language-service/lib/virtual-code.js | 74 +++++++++++-------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/packages/language-service/lib/virtual-code.js b/packages/language-service/lib/virtual-code.js index 599befce..9f57cfe3 100644 --- a/packages/language-service/lib/virtual-code.js +++ b/packages/language-service/lib/virtual-code.js @@ -24,25 +24,6 @@ import {isInjectableComponent, isInjectableEstree} from './jsx-utils.js' */ const jsPrefix = (jsxImportSource) => `/* @jsxRuntime automatic @jsxImportSource ${jsxImportSource} */ - -/** - * @internal - * @template T - * @typedef {void extends MDX.IntrinsicElements ? any : T extends keyof MDX.IntrinsicElements ? MDX.IntrinsicElements[T] : any} _MDXProps - */ - -/** - * @internal - * @typedef {void extends MDX.Element ? JSX.Element : MDX.Element} _MDXElement - */ - -/** - * @internal - * @type {{ - * [Name in keyof _MDXNodeNames]: (props: _MDXProps) => _MDXElement - * }} - */ -const _MDXNodes = /** @type {any} */(0); ` /** @@ -244,6 +225,48 @@ function padOffsets(mapping, padding) { } } +/** @type {Partial>} */ +const mdastElementNodeNameMap = { + blockquote: 'blockquote', + break: 'br', + delete: 'del', + emphasis: 'em', + html: 'html', + image: 'img', + imageReference: 'img', + inlineCode: 'code', + link: 'a', + linkReference: 'a', + listItem: 'li', + paragraph: 'p', + strong: 'strong', + table: 'table', + tableCell: 'td', + tableRow: 'tr', + thematicBreak: 'hr' +} +/** + * Get the JSX node names for a given AST node. + * @param {Nodes} node + * @returns {string[]} + */ +function getJsxNodeNameForMdast(node) { + if (node.type === 'code') { + return ['pre', 'code'] + } + if (node.type === 'heading') { + return ['h' + node.depth] + } + if (node.type === 'list') { + return node.ordered ? ['ol'] : ['ul'] + } + if (mdastElementNodeNameMap[node.type]) { + return [/** @type string */ (mdastElementNodeNameMap[node.type])] + } + // JSX Fragment + return [''] +} + /** * @param {string} mdx * @param {Root} ast @@ -266,8 +289,6 @@ function getEmbeddedCodes( let jsxVariables = '' let markdown = '' let nextMarkdownSourceStart = 0 - /** @type {Set} */ - const foundMdxNodeNames = new Set() const plugins = virtualCodePlugins.map((plugin) => plugin()) /** @type {CodeMapping[]} */ @@ -760,8 +781,7 @@ function getEmbeddedCodes( } default: { - foundMdxNodeNames.add(node.type) - jsx += jsxIndent + `<_MDXNodes.${node.type}>` + jsx += jsxIndent + getJsxNodeNameForMdast(node).map((name) => `<${name}>`).join('') break } } @@ -811,7 +831,7 @@ function getEmbeddedCodes( } default: { - jsx += jsxIndent + `` + jsx += jsxIndent + getJsxNodeNameForMdast(node).map((name) => ``).join('') break } } @@ -842,12 +862,6 @@ function getEmbeddedCodes( esmMapping.lengths.unshift(0) } - esm += `/** - * @typedef {object} _MDXNodeNames -${[...foundMdxNodeNames].map((name) => ` * @property {unknown} ${name}`).join('\n')} - */ -` - updateMarkdownFromOffsets(mdx.length, mdx.length) esm += componentStart(hasAwait, programScope) From fd5379265b9dbed90110744eecf049e3b4d77cfc Mon Sep 17 00:00:00 2001 From: Guyutongxue Date: Sun, 14 Sep 2025 14:50:25 +0800 Subject: [PATCH 3/4] fix(language-service): close tag bug & tests --- packages/language-service/lib/virtual-code.js | 17 ++++- .../language-service/test/language-plugin.js | 66 +++++++++---------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/packages/language-service/lib/virtual-code.js b/packages/language-service/lib/virtual-code.js index 9f57cfe3..f74adc18 100644 --- a/packages/language-service/lib/virtual-code.js +++ b/packages/language-service/lib/virtual-code.js @@ -254,15 +254,19 @@ function getJsxNodeNameForMdast(node) { if (node.type === 'code') { return ['pre', 'code'] } + if (node.type === 'heading') { return ['h' + node.depth] } + if (node.type === 'list') { return node.ordered ? ['ol'] : ['ul'] } + if (mdastElementNodeNameMap[node.type]) { return [/** @type string */ (mdastElementNodeNameMap[node.type])] } + // JSX Fragment return [''] } @@ -781,7 +785,11 @@ function getEmbeddedCodes( } default: { - jsx += jsxIndent + getJsxNodeNameForMdast(node).map((name) => `<${name}>`).join('') + jsx += + jsxIndent + + getJsxNodeNameForMdast(node) + .map((name) => `<${name}>`) + .join('') break } } @@ -831,7 +839,12 @@ function getEmbeddedCodes( } default: { - jsx += jsxIndent + getJsxNodeNameForMdast(node).map((name) => ``).join('') + jsx += + jsxIndent + + getJsxNodeNameForMdast(node) + .reverse() + .map((name) => ``) + .join('') break } } diff --git a/packages/language-service/test/language-plugin.js b/packages/language-service/test/language-plugin.js index 14a3d1a5..8eb9a776 100644 --- a/packages/language-service/test/language-plugin.js +++ b/packages/language-service/test/language-plugin.js @@ -2042,7 +2042,7 @@ test('create virtual code w/ prefixed JSX expressions in attributes', () => { }, { sourceOffsets: [28, 62, 103, 149, 166, 214], - generatedOffsets: [870, 916, 960, 1018, 1047, 1111], + generatedOffsets: [870, 916, 960, 1018, 1047, 1113], lengths: [34, 39, 46, 17, 35, 6], data: { completion: true, @@ -2054,7 +2054,7 @@ test('create virtual code w/ prefixed JSX expressions in attributes', () => { } }, { - generatedOffsets: [1551, 1574], + generatedOffsets: [1553, 1576], lengths: [8, 8], sourceOffsets: [62, 149], data: { @@ -2099,9 +2099,9 @@ test('create virtual code w/ prefixed JSX expressions in attributes', () => { ' return <>', '
} injected={<_components.Injected />} string="string" boolean />', '
{null}} injected={<_components.Injected>{null}} string="string" boolean>', - ' <>', + '

', " {''}", - ' ', + '

', '
', ' ', '}', @@ -2224,7 +2224,7 @@ test('create virtual code w/ mdxJsxFlowElement w/ children', () => { }, { sourceOffsets: [28, 87, 94, 95, 158, 160, 170, 231], - generatedOffsets: [870, 904, 915, 928, 966, 980, 994, 1030], + generatedOffsets: [870, 906, 917, 930, 970, 984, 998, 1036], lengths: [5, 6, 1, 9, 2, 9, 7, 8], data: { completion: true, @@ -2244,7 +2244,7 @@ test('create virtual code w/ mdxJsxFlowElement w/ children', () => { structure: true, verification: true }, - generatedOffsets: [1472], + generatedOffsets: [1478], lengths: [8], sourceOffsets: [95] } @@ -2280,19 +2280,19 @@ test('create virtual code w/ mdxJsxFlowElement w/ children', () => { ' _components', ' return <>', '
', - ' <>', + '

', " {''}", - ' ', + '

', '
', ' <_components.Injected>', - ' <>', + '

', " {''}", - ' ', + '

', ' ', ' ', - ' <>', + '

', " {''}", - ' ', + '

', '
', ' ', '}', @@ -2405,7 +2405,7 @@ test('create virtual code w/ mdxJsxFlowElement w/ blockquote child', () => { }, { sourceOffsets: [0, 8, 15, 21], - generatedOffsets: [806, 831, 849, 868], + generatedOffsets: [806, 851, 870, 889], lengths: [5, 6, 5, 6], data: { completion: true, @@ -2444,14 +2444,14 @@ test('create virtual code w/ mdxJsxFlowElement w/ blockquote child', () => { ' _components', ' return <>', '
', - ' <>', - ' ', + '
', + '
', '
', - ' <>', + '

', '

', " {''}", '
', - ' ', + '

', ' ', '}', '', @@ -2706,7 +2706,7 @@ test('create virtual code w/ mdxJsxTextElement', () => { }, { sourceOffsets: [30, 41, 42, 56], - generatedOffsets: [886, 907, 920, 945], + generatedOffsets: [887, 908, 921, 946], lengths: [7, 1, 11, 9], data: { completion: true, @@ -2718,7 +2718,7 @@ test('create virtual code w/ mdxJsxTextElement', () => { } }, { - generatedOffsets: [1396], + generatedOffsets: [1398], lengths: [8], sourceOffsets: [42], data: { @@ -2761,14 +2761,14 @@ test('create virtual code w/ mdxJsxTextElement', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", '

', " {''}", ' <_components.Injected />', " {''}", ' ', - ' ', + '

', ' ', '}', '', @@ -2867,7 +2867,7 @@ test('create virtual code w/ mdxTextExpression', () => { } }, { - generatedOffsets: [822], + generatedOffsets: [823], sourceOffsets: [4], lengths: [9], data: { @@ -2906,11 +2906,11 @@ test('create virtual code w/ mdxTextExpression', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", ' {Math.PI}', " {''}", - ' ', + '

', ' ', '}', '', @@ -3003,7 +3003,7 @@ test('create virtual code w/ async mdxTextExpression', () => { } }, { - generatedOffsets: [828], + generatedOffsets: [829], sourceOffsets: [4], lengths: [32], data: { @@ -3042,11 +3042,11 @@ test('create virtual code w/ async mdxTextExpression', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", ' {await Promise.resolve(Math.PI)}', " {''}", - ' ', + '

', ' ', '}', '', @@ -3476,9 +3476,9 @@ test('create virtual code w/ dedented markdown content', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", - ' ', + '

', ' ', '}', '', @@ -3830,9 +3830,9 @@ test('update virtual code', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", - ' ', + '

', ' ', '}', '', @@ -5242,9 +5242,9 @@ test('rehype-mdx-title matching rank', () => { ' }', ' _components', ' return <>', - ' <>', + '

', " {''}", - ' ', + '

', ' ', '}', '', From cc9043d71f3e2844e4aca4a1b9449f7b7d782d19 Mon Sep 17 00:00:00 2001 From: Guyutongxue Date: Tue, 16 Sep 2025 11:41:10 +0800 Subject: [PATCH 4/4] chore(pr): add changeset --- .changeset/shaggy-memes-post.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/shaggy-memes-post.md diff --git a/.changeset/shaggy-memes-post.md b/.changeset/shaggy-memes-post.md new file mode 100644 index 00000000..3646f146 --- /dev/null +++ b/.changeset/shaggy-memes-post.md @@ -0,0 +1,8 @@ +--- +'@mdx-js/language-service': patch +'@mdx-js/language-server': patch +'@mdx-js/typescript-plugin': patch +'vscode-mdx': patch +--- + +Make Markdown syntax nodes typed with `JSX.IntrinsicElements`.