From 45c33232ad620989b7a373ba3750d7920fd78586 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:24:26 +0700 Subject: [PATCH 01/13] fix --- .changeset/selfish-toys-clap.md | 7 ++ .../rehype-extract-toc-content.test.ts | 10 +-- .../__tests__/remark-remove-imports.test.ts | 6 +- packages/nextra/src/server/constants.ts | 2 - .../server/recma-plugins/recma-rewrite-jsx.ts | 68 +------------------ 5 files changed, 14 insertions(+), 79 deletions(-) create mode 100644 .changeset/selfish-toys-clap.md diff --git a/.changeset/selfish-toys-clap.md b/.changeset/selfish-toys-clap.md new file mode 100644 index 0000000000..99ea508ef3 --- /dev/null +++ b/.changeset/selfish-toys-clap.md @@ -0,0 +1,7 @@ +--- +'nextra-theme-blog': minor +'nextra-theme-docs': minor +'nextra': minor +--- + +move TOC logic from `recma-rewrite-jsx` plugin to `rehype-extract-toc-content` plugin diff --git a/packages/nextra/src/server/__tests__/rehype-extract-toc-content.test.ts b/packages/nextra/src/server/__tests__/rehype-extract-toc-content.test.ts index b34a8f149d..c9e45e8648 100644 --- a/packages/nextra/src/server/__tests__/rehype-extract-toc-content.test.ts +++ b/packages/nextra/src/server/__tests__/rehype-extract-toc-content.test.ts @@ -408,16 +408,16 @@ export const myVar = 123 return _jsxs(_Fragment, { children: [ _jsx(_components.h2, { - id: 'bar', - children: 'bar' + id: toc[0].id, + children: toc[0].value }), '\\n', _jsx(Foo, {}), '\\n', '\\n', - _jsxs(_components.h3, { - id: '123-myvar', - children: ['123 ', myVar] + _jsx(_components.h3, { + id: toc[1].id, + children: toc[1].value }) ] }) diff --git a/packages/nextra/src/server/__tests__/remark-remove-imports.test.ts b/packages/nextra/src/server/__tests__/remark-remove-imports.test.ts index 28b956acba..c741452111 100644 --- a/packages/nextra/src/server/__tests__/remark-remove-imports.test.ts +++ b/packages/nextra/src/server/__tests__/remark-remove-imports.test.ts @@ -42,11 +42,7 @@ export const Test = ({value}) => value h2: 'h2', ...props.components } - return ( - <_components.h2 id="-myvar"> - {myVar} - - ) + return <_components.h2 id={toc[0].id}>{toc[0].value} } return { metadata, diff --git a/packages/nextra/src/server/constants.ts b/packages/nextra/src/server/constants.ts index dbca5498e8..7bd4e725df 100644 --- a/packages/nextra/src/server/constants.ts +++ b/packages/nextra/src/server/constants.ts @@ -22,6 +22,4 @@ export const DEFAULT_PROPERTY_PROPS = { computed: false } satisfies Omit -export const TOC_HEADING_RE = /^h[2-6]$/ - export const METADATA_ONLY_RQ = '?metadata' diff --git a/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts b/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts index c7336496db..915cbba7d7 100644 --- a/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts +++ b/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts @@ -1,8 +1,5 @@ import type { FunctionDeclaration, Program } from 'estree' -import type { JsxAttribute } from 'estree-util-to-js/lib/jsx' import type { Plugin } from 'unified' -import { visit } from 'unist-util-visit' -import { TOC_HEADING_RE } from '../constants.js' export const recmaRewriteJsx: Plugin<[], Program> = () => (ast: Program, file) => { @@ -29,67 +26,4 @@ export const recmaRewriteJsx: Plugin<[], Program> = } createMdxContent.id.name = 'MDXLayout' - - const tocProperties = file.data.toc as ( - | { properties: { id: string } } - | string - )[] - - // Do not add `const toc = [` - if (!tocProperties.length) return - - const returnStatement = createMdxContent.body.body.find( - o => o.type === 'ReturnStatement' - )! - - const { argument } = returnStatement as any - - // if return statements doesn't wrap in fragment children will be [] - const returnBody = argument.children.length ? argument.children : [argument] - - visit({ children: returnBody }, 'JSXElement', (heading: any) => { - const { openingElement } = heading - const name = openingElement?.name.property?.name - const isHeading = name && TOC_HEADING_RE.test(name) - if (!isHeading) return - - const idNode = openingElement.attributes.find( - (attr: JsxAttribute) => attr.name.name === 'id' - ) - if (!idNode) return - - const id = idNode.value.value - - const foundIndex = tocProperties.findIndex(node => { - if (typeof node === 'string') return - return node.properties.id === id - }) - - if (foundIndex === -1) return - idNode.value = { - type: 'JSXExpressionContainer', - expression: { - type: 'Identifier', - name: `toc[${foundIndex}].id` - } - } - - delete openingElement.selfClosing - heading.children = [ - { - type: 'JSXExpressionContainer', - expression: { - type: 'Identifier', - name: `toc[${foundIndex}].value` - } - } - ] - heading.closingElement = { - ...openingElement, - type: 'JSXClosingElement', - attributes: [] - } - }) - - createMdxContent.params = [{ type: 'Identifier', name: 'props' }] - } + } From fa937baa0f0255ab3589974d440344d6234c9e4b Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:32:59 +0700 Subject: [PATCH 02/13] fix --- packages/nextra/__test__/compile.test.ts | 12 +- .../rehype-extract-toc-content.ts | 185 +++++++++++------- 2 files changed, 123 insertions(+), 74 deletions(-) diff --git a/packages/nextra/__test__/compile.test.ts b/packages/nextra/__test__/compile.test.ts index 96a5716f89..364735b64a 100644 --- a/packages/nextra/__test__/compile.test.ts +++ b/packages/nextra/__test__/compile.test.ts @@ -335,7 +335,7 @@ import Last from './three.mdx' {'\\n'} {'\\n'} - <_components.h2 id={toc[1].id}>{toc[1].value} + <_components.h2 id={toc[2].id}>{toc[2].value} {'\\n'} {'\\n'} @@ -344,15 +344,15 @@ import Last from './three.mdx' {'\\n'} {'\\n'} - <_components.h2 id={toc[2].id}>{toc[2].value} + <_components.h2 id={toc[5].id}>{toc[5].value} {'\\n'} - <_components.h2 id={toc[3].id}>{toc[3].value} + <_components.h2 id={toc[6].id}>{toc[6].value} {'\\n'} - <_components.h2 id={toc[4].id}>{toc[4].value} + <_components.h2 id={toc[7].id}>{toc[7].value} {'\\n'} - <_components.h2 id={toc[5].id}>{toc[5].value} + <_components.h2 id={toc[8].id}>{toc[8].value} {'\\n'} - <_components.h2 id={toc[6].id}>{toc[6].value} + <_components.h2 id={toc[9].id}>{toc[9].value} ) } diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index 9aad48c836..0fecb237f2 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -1,90 +1,139 @@ import type { JsxAttribute } from 'estree-util-to-js/lib/jsx' import type { Element, Root } from 'hast' import { toEstree } from 'hast-util-to-estree' +import type { MdxjsEsm } from 'hast-util-to-estree/lib/handlers/mdxjs-esm' import type { Plugin } from 'unified' import { visit } from 'unist-util-visit' import type { Heading } from '../../types.js' -import { TOC_HEADING_RE } from '../constants.js' import { createAstExportConst, createAstObject } from '../utils.js' -export const rehypeExtractTocContent: Plugin< - [{ isRemoteContent?: boolean }], - Root -> = - ({ - // todo rethink this - isRemoteContent - }) => - (ast, file) => { - const toc: any[] = [] - const idSet = new Set((file.data.toc as Heading[]).map(({ id }) => id)) +const TOC_HEADING_RE = /^h[2-6]$/ - visit(ast, 'element', (node: Element) => { - if (!TOC_HEADING_RE.test(node.tagName)) return +export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { + const toc: any[] = [] + const idSet = new Set((file.data.toc as Heading[]).map(({ id }) => id)) - const { id } = node.properties - if (typeof id === 'string' && idSet.has(id)) { - toc.push(structuredClone(node)) - if (!isRemoteContent) { - node.children = [] - } - } - }) + visit(ast, 'element', (node: Element) => { + if (!TOC_HEADING_RE.test(node.tagName)) return - const TocToExpression = Object.fromEntries( - toc.map(node => - // @ts-expect-error - [node.properties.id, toEstree(node).body[0].expression] - ) - ) + const { id } = node.properties + if (typeof id === 'string' && idSet.has(id)) { + toc.push(node) + } + }) - // @ts-expect-error - const elements = file.data.toc.map(n => { - if (typeof n === 'string') { - return { - type: 'SpreadElement', - argument: { type: 'Identifier', name: n } - } + const TocToExpression = Object.fromEntries( + toc.map(node => [node.properties.id, node]) + ) + + // @ts-expect-error + const elements = file.data.toc.map((n, index) => { + if (typeof n === 'string') { + return { + type: 'SpreadElement', + argument: { type: 'Identifier', name: n } } + } - const node = TocToExpression[n.id] + const originalNode = TocToExpression[n.id] + // @ts-expect-error + const node = toEstree(originalNode).body[0].expression - const isText = node.children.every( - // @ts-expect-error - child => - child.type === 'JSXExpressionContainer' && - child.expression.type === 'Literal' - ) + const isText = node.children.every( + // @ts-expect-error + child => + child.type === 'JSXExpressionContainer' && + child.expression.type === 'Literal' + ) - const result = isText - ? // @ts-expect-error - node.children.map(n => n.expression)[0] - : { - type: 'JSXFragment', - openingFragment: { type: 'JSXOpeningFragment' }, - closingFragment: { type: 'JSXClosingFragment' }, - children: node.children - } + const result = isText + ? // @ts-expect-error + node.children.map(n => n.expression)[0] + : { + type: 'JSXFragment', + openingFragment: { type: 'JSXOpeningFragment' }, + closingFragment: { type: 'JSXClosingFragment' }, + children: node.children + } - return createAstObject({ - value: result, - id: node.openingElement.attributes.find( - (attr: JsxAttribute) => attr.name.name === 'id' - ).value.value, - depth: Number(node.openingElement.name.name[1]) - }) + const ast = createAstObject({ + value: result, + id: node.openingElement.attributes.find( + (attr: JsxAttribute) => attr.name.name === 'id' + ).value.value, + depth: Number(node.openingElement.name.name[1]) }) - ast.children.push({ - type: 'mdxjsEsm', - data: { - estree: { - body: [ - createAstExportConst('toc', { type: 'ArrayExpression', elements }) - ] + Object.assign(originalNode, { + type: 'mdxJsxFlowElement', + name: originalNode.tagName, + attributes: [ + { + type: 'mdxJsxAttribute', + name: 'id', + value: { + type: 'mdxJsxAttributeValueExpression', + data: { + estree: { + body: [ + { + type: 'ExpressionStatement', + expression: { + type: 'MemberExpression', + property: { type: 'Identifier', name: 'id' }, + object: { + type: 'MemberExpression', + object: { type: 'Identifier', name: 'toc' }, + property: { type: 'Literal', value: index }, + computed: true + } + } + } + ] + } + } + } + } + ], + children: [ + { + type: 'mdxFlowExpression', + data: { + estree: { + body: [ + { + type: 'ExpressionStatement', + expression: { + type: 'MemberExpression', + property: { type: 'Identifier', name: 'value' }, + object: { + type: 'MemberExpression', + object: { type: 'Identifier', name: 'toc' }, + property: { type: 'Literal', value: index }, + computed: true + } + } + } + ] + } + } } + ] + }) + + return ast + }) + + ast.children.push({ + type: 'mdxjsEsm', + data: { + estree: { + body: [ + createAstExportConst('toc', { type: 'ArrayExpression', elements }) + ] } - } as any) + } + } as MdxjsEsm) - file.data.toc = toc - } + file.data.toc = toc +} From f4afba88e0c972311be71e3fe17a9851b4e5f70a Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:46:12 +0700 Subject: [PATCH 03/13] fix --- packages/nextra/src/server/compile.ts | 2 +- .../rehype-extract-toc-content.ts | 55 ++++++++----------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/packages/nextra/src/server/compile.ts b/packages/nextra/src/server/compile.ts index 17eedce859..1e4b1c0e56 100644 --- a/packages/nextra/src/server/compile.ts +++ b/packages/nextra/src/server/compile.ts @@ -206,7 +206,7 @@ export async function compileMdx( rehypeTwoslashPopup, [rehypeAttachCodeMeta, { search }] ]), - [rehypeExtractTocContent, { isRemoteContent }] + rehypeExtractTocContent ].filter(v => !!v), recmaPlugins: [ ...(recmaPlugins || []), diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index 0fecb237f2..c1aab42191 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -1,4 +1,3 @@ -import type { JsxAttribute } from 'estree-util-to-js/lib/jsx' import type { Element, Root } from 'hast' import { toEstree } from 'hast-util-to-estree' import type { MdxjsEsm } from 'hast-util-to-estree/lib/handlers/mdxjs-esm' @@ -10,36 +9,32 @@ import { createAstExportConst, createAstObject } from '../utils.js' const TOC_HEADING_RE = /^h[2-6]$/ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { - const toc: any[] = [] - const idSet = new Set((file.data.toc as Heading[]).map(({ id }) => id)) + const TocMap: Record = {} + const toc = file.data.toc as Heading[] + const idSet = new Set(toc.map(({ id }) => id)) visit(ast, 'element', (node: Element) => { if (!TOC_HEADING_RE.test(node.tagName)) return - const { id } = node.properties - if (typeof id === 'string' && idSet.has(id)) { - toc.push(node) + const id = node.properties.id as string + if (idSet.has(id)) { + TocMap[id] = node } }) - - const TocToExpression = Object.fromEntries( - toc.map(node => [node.properties.id, node]) - ) - - // @ts-expect-error - const elements = file.data.toc.map((n, index) => { - if (typeof n === 'string') { + + const elements = toc.map((name, index) => { + if (typeof name === 'string') { return { type: 'SpreadElement', - argument: { type: 'Identifier', name: n } + argument: { type: 'Identifier', name } } } - const originalNode = TocToExpression[n.id] + const node = TocMap[name.id] // @ts-expect-error - const node = toEstree(originalNode).body[0].expression + const { children } = toEstree(node).body[0].expression - const isText = node.children.every( + const isText = children.every( // @ts-expect-error child => child.type === 'JSXExpressionContainer' && @@ -48,25 +43,17 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { const result = isText ? // @ts-expect-error - node.children.map(n => n.expression)[0] + children.map(n => n.expression)[0] : { type: 'JSXFragment', openingFragment: { type: 'JSXOpeningFragment' }, closingFragment: { type: 'JSXClosingFragment' }, - children: node.children + children } - const ast = createAstObject({ - value: result, - id: node.openingElement.attributes.find( - (attr: JsxAttribute) => attr.name.name === 'id' - ).value.value, - depth: Number(node.openingElement.name.name[1]) - }) - - Object.assign(originalNode, { + Object.assign(node, { type: 'mdxJsxFlowElement', - name: originalNode.tagName, + name: node.tagName, attributes: [ { type: 'mdxJsxAttribute', @@ -121,7 +108,11 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { ] }) - return ast + return createAstObject({ + value: result, + id: node.properties.id as string, + depth: Number(node.tagName[1]) + }) }) ast.children.push({ @@ -135,5 +126,5 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { } } as MdxjsEsm) - file.data.toc = toc + file.data.toc = TocMap } From 55d92cb431e5e5f9a8f92d11fbebf043e21b2638 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:46:22 +0700 Subject: [PATCH 04/13] fix --- .../src/server/rehype-plugins/rehype-extract-toc-content.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index c1aab42191..c28d0cd351 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -21,7 +21,7 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { TocMap[id] = node } }) - + const elements = toc.map((name, index) => { if (typeof name === 'string') { return { From b82b2b3da0fceed92a87147c9d721f32a76398a5 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:48:13 +0700 Subject: [PATCH 05/13] fix --- .../src/server/rehype-plugins/rehype-extract-toc-content.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index c28d0cd351..5d1f3cd14c 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -1,3 +1,4 @@ +import type { SpreadElement } from 'estree' import type { Element, Root } from 'hast' import { toEstree } from 'hast-util-to-estree' import type { MdxjsEsm } from 'hast-util-to-estree/lib/handlers/mdxjs-esm' @@ -27,7 +28,7 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { return { type: 'SpreadElement', argument: { type: 'Identifier', name } - } + } satisfies SpreadElement } const node = TocMap[name.id] From e172986e2cd4044289cf692116521a72c980afbe Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:58:45 +0700 Subject: [PATCH 06/13] fix --- .../rehype-extract-toc-content.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index 5d1f3cd14c..34762c26d7 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -1,5 +1,5 @@ import type { SpreadElement } from 'estree' -import type { Element, Root } from 'hast' +import type { Element, Root, Text } from 'hast' import { toEstree } from 'hast-util-to-estree' import type { MdxjsEsm } from 'hast-util-to-estree/lib/handlers/mdxjs-esm' import type { Plugin } from 'unified' @@ -32,25 +32,17 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { } const node = TocMap[name.id] - // @ts-expect-error - const { children } = toEstree(node).body[0].expression - const isText = children.every( - // @ts-expect-error - child => - child.type === 'JSXExpressionContainer' && - child.expression.type === 'Literal' - ) + const isText = node.children.every(child => child.type === 'text') const result = isText - ? // @ts-expect-error - children.map(n => n.expression)[0] - : { + ? node.children.map(n => (n as Text).value).join('') + : // @ts-expect-error + Object.assign(toEstree(node).body[0].expression, { type: 'JSXFragment', openingFragment: { type: 'JSXOpeningFragment' }, - closingFragment: { type: 'JSXClosingFragment' }, - children - } + closingFragment: { type: 'JSXClosingFragment' } + }) Object.assign(node, { type: 'mdxJsxFlowElement', From ac47e01308ef01c51651936cfca6aa2960c7c385 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 02:59:04 +0700 Subject: [PATCH 07/13] dd --- packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts b/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts index 915cbba7d7..327bdd845b 100644 --- a/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts +++ b/packages/nextra/src/server/recma-plugins/recma-rewrite-jsx.ts @@ -26,4 +26,4 @@ export const recmaRewriteJsx: Plugin<[], Program> = } createMdxContent.id.name = 'MDXLayout' - } + } From c303d29f337deb44d6cca0865198ad71663a1496 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:05:29 +0700 Subject: [PATCH 08/13] move --- packages/nextra/src/server/compile.ts | 27 ++----------------- .../nextra/src/server/recma-plugins/index.ts | 1 + .../src/server/recma-plugins/recma-rewrite.ts | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+), 25 deletions(-) create mode 100644 packages/nextra/src/server/recma-plugins/recma-rewrite.ts diff --git a/packages/nextra/src/server/compile.ts b/packages/nextra/src/server/compile.ts index 1e4b1c0e56..1d6b645143 100644 --- a/packages/nextra/src/server/compile.ts +++ b/packages/nextra/src/server/compile.ts @@ -3,7 +3,6 @@ import type { ProcessorOptions } from '@mdx-js/mdx' import { createProcessor } from '@mdx-js/mdx' import { remarkMermaid } from '@theguild/remark-mermaid' import { remarkNpm2Yarn } from '@theguild/remark-npm2yarn' -import type { Program } from 'estree' import rehypeKatex from 'rehype-katex' import rehypePrettyCode from 'rehype-pretty-code' import rehypeRaw from 'rehype-raw' @@ -16,6 +15,7 @@ import type { Pluggable, Plugin } from 'unified' import type { LoaderOptions, NextraConfig } from '../types.js' import { CWD, MARKDOWN_URL_EXTENSION_RE } from './constants.js' import { + recmaRewrite, recmaRewriteFunctionBody, recmaRewriteJsx } from './recma-plugins/index.js' @@ -210,30 +210,7 @@ export async function compileMdx( ].filter(v => !!v), recmaPlugins: [ ...(recmaPlugins || []), - (() => (ast: Program, file) => { - const mdxContentIndex = ast.body.findIndex(node => { - if (node.type === 'ExportDefaultDeclaration') { - return (node.declaration as any).id.name === 'MDXContent' - } - if (node.type === 'FunctionDeclaration') { - return node.id.name === 'MDXContent' - } - }) - - // Remove `MDXContent` since we use custom HOC_MDXContent - let [mdxContent] = ast.body.splice(mdxContentIndex, 1) as any - - // In MDX3 MDXContent is directly exported as export default when `outputFormat: 'program'` is specified - if (mdxContent.type === 'ExportDefaultDeclaration') { - mdxContent = mdxContent.declaration - } - - const mdxContentArgument = mdxContent.body.body[0].argument - - file.data.hasMdxLayout = - !!mdxContentArgument && - mdxContentArgument.openingElement.name.name === 'MDXLayout' - }) satisfies Plugin<[], Program>, + recmaRewrite, isRemoteContent ? recmaRewriteFunctionBody : recmaRewriteJsx ].filter(v => !!v) }) diff --git a/packages/nextra/src/server/recma-plugins/index.ts b/packages/nextra/src/server/recma-plugins/index.ts index c0d9d43049..9219b020a3 100644 --- a/packages/nextra/src/server/recma-plugins/index.ts +++ b/packages/nextra/src/server/recma-plugins/index.ts @@ -1,2 +1,3 @@ export { recmaRewriteJsx } from './recma-rewrite-jsx.js' export { recmaRewriteFunctionBody } from './recma-rewrite-function-body.js' +export { recmaRewrite } from './recma-rewrite.js' diff --git a/packages/nextra/src/server/recma-plugins/recma-rewrite.ts b/packages/nextra/src/server/recma-plugins/recma-rewrite.ts new file mode 100644 index 0000000000..873ad49fab --- /dev/null +++ b/packages/nextra/src/server/recma-plugins/recma-rewrite.ts @@ -0,0 +1,27 @@ +import type { Program } from 'estree' +import type { Plugin } from 'unified' + +export const recmaRewrite: Plugin<[], Program> = () => (ast, file) => { + const mdxContentIndex = ast.body.findIndex(node => { + if (node.type === 'ExportDefaultDeclaration') { + return (node.declaration as any).id.name === 'MDXContent' + } + if (node.type === 'FunctionDeclaration') { + return node.id.name === 'MDXContent' + } + }) + + // Remove `MDXContent` since we use custom HOC_MDXContent + let [mdxContent] = ast.body.splice(mdxContentIndex, 1) as any + + // In MDX3 MDXContent is directly exported as export default when `outputFormat: 'program'` is specified + if (mdxContent.type === 'ExportDefaultDeclaration') { + mdxContent = mdxContent.declaration + } + + const mdxContentArgument = mdxContent.body.body[0].argument + + file.data.hasMdxLayout = + !!mdxContentArgument && + mdxContentArgument.openingElement.name.name === 'MDXLayout' +} From 752b850ae399d08a669aa517c1f68d35300974e8 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:15:40 +0700 Subject: [PATCH 09/13] refactor --- .../rehype-extract-toc-content.ts | 85 ++++++++----------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index 34762c26d7..f84fbca0c3 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -33,9 +33,9 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { const node = TocMap[name.id] - const isText = node.children.every(child => child.type === 'text') + const isTextOnly = node.children.every(child => child.type === 'text') - const result = isText + const result = isTextOnly ? node.children.map(n => (n as Text).value).join('') : // @ts-expect-error Object.assign(toEstree(node).body[0].expression, { @@ -51,54 +51,14 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { { type: 'mdxJsxAttribute', name: 'id', - value: { - type: 'mdxJsxAttributeValueExpression', - data: { - estree: { - body: [ - { - type: 'ExpressionStatement', - expression: { - type: 'MemberExpression', - property: { type: 'Identifier', name: 'id' }, - object: { - type: 'MemberExpression', - object: { type: 'Identifier', name: 'toc' }, - property: { type: 'Literal', value: index }, - computed: true - } - } - } - ] - } - } - } + value: createComputedKey( + 'mdxJsxAttributeValueExpression', + index, + 'id' + ) } ], - children: [ - { - type: 'mdxFlowExpression', - data: { - estree: { - body: [ - { - type: 'ExpressionStatement', - expression: { - type: 'MemberExpression', - property: { type: 'Identifier', name: 'value' }, - object: { - type: 'MemberExpression', - object: { type: 'Identifier', name: 'toc' }, - property: { type: 'Literal', value: index }, - computed: true - } - } - } - ] - } - } - } - ] + children: [createComputedKey('mdxFlowExpression', index, 'value')] }) return createAstObject({ @@ -121,3 +81,32 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { file.data.toc = TocMap } + +function createComputedKey( + type: 'mdxFlowExpression' | 'mdxJsxAttributeValueExpression', + index: number, + key: string +) { + return { + type, + data: { + estree: { + body: [ + { + type: 'ExpressionStatement', + expression: { + type: 'MemberExpression', + property: { type: 'Identifier', name: key }, + object: { + type: 'MemberExpression', + object: { type: 'Identifier', name: 'toc' }, + property: { type: 'Literal', value: index }, + computed: true + } + } + } + ] + } + } + } +} From ebba1412ba042f35ab90653e3b492160bb38bc8a Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:17:18 +0700 Subject: [PATCH 10/13] refactor --- packages/nextra/src/server/recma-plugins/recma-rewrite.ts | 2 +- .../src/server/rehype-plugins/rehype-extract-toc-content.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/nextra/src/server/recma-plugins/recma-rewrite.ts b/packages/nextra/src/server/recma-plugins/recma-rewrite.ts index 873ad49fab..c5aeff1e4f 100644 --- a/packages/nextra/src/server/recma-plugins/recma-rewrite.ts +++ b/packages/nextra/src/server/recma-plugins/recma-rewrite.ts @@ -1,7 +1,7 @@ import type { Program } from 'estree' import type { Plugin } from 'unified' -export const recmaRewrite: Plugin<[], Program> = () => (ast, file) => { +export const recmaRewrite: Plugin<[], Program> = () => (ast: Program, file) => { const mdxContentIndex = ast.body.findIndex(node => { if (node.type === 'ExportDefaultDeclaration') { return (node.declaration as any).id.name === 'MDXContent' diff --git a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts index f84fbca0c3..b228b515cd 100644 --- a/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts +++ b/packages/nextra/src/server/rehype-plugins/rehype-extract-toc-content.ts @@ -78,8 +78,6 @@ export const rehypeExtractTocContent: Plugin<[], Root> = () => (ast, file) => { } } } as MdxjsEsm) - - file.data.toc = TocMap } function createComputedKey( From b64ec2d0f84a33adfc8bc8641ae521c80f30e80f Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:22:04 +0700 Subject: [PATCH 11/13] lint --- packages/nextra/src/server/compile.ts | 2 +- packages/nextra/src/server/loader.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nextra/src/server/compile.ts b/packages/nextra/src/server/compile.ts index 1d6b645143..2c422e6ced 100644 --- a/packages/nextra/src/server/compile.ts +++ b/packages/nextra/src/server/compile.ts @@ -11,7 +11,7 @@ import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' import remarkReadingTime from 'remark-reading-time' import remarkSmartypants from 'remark-smartypants' -import type { Pluggable, Plugin } from 'unified' +import type { Pluggable } from 'unified' import type { LoaderOptions, NextraConfig } from '../types.js' import { CWD, MARKDOWN_URL_EXTENSION_RE } from './constants.js' import { diff --git a/packages/nextra/src/server/loader.ts b/packages/nextra/src/server/loader.ts index 98e6ebcd13..e0068dddd9 100644 --- a/packages/nextra/src/server/loader.ts +++ b/packages/nextra/src/server/loader.ts @@ -197,16 +197,16 @@ ${locales async function getLastCommitTime( filePath: string ): Promise { + const relativePath = path.relative(GIT_ROOT, filePath) try { if (!repository) { throw new Error('Init git repository failed') } - const relativePath = path.relative(GIT_ROOT, filePath) return await repository.getFileLatestModifiedDateAsync(relativePath) } catch { logger.warn( 'Failed to get the last modified timestamp from Git for the file', - filePath + relativePath ) } } From f4f46b16cd48fd8fdfbd698664d9c468914039b7 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:33:09 +0700 Subject: [PATCH 12/13] aa --- packages/nextra/src/server/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/nextra/src/server/index.ts b/packages/nextra/src/server/index.ts index fbc7648c53..9bb1d38eda 100644 --- a/packages/nextra/src/server/index.ts +++ b/packages/nextra/src/server/index.ts @@ -143,8 +143,10 @@ const nextra: Nextra = nextraConfig => { ) } } - config.resolve.alias['private-next-content-dir'] = - `private-next-root-dir/${contentDir}` + config.resolve.alias['private-next-content-dir'] = [ + 'private-next-root-dir/content', + 'private-next-root-dir/src/content' + ] config.resolve.alias['next-mdx-import-source-file'] = [ 'private-next-root-dir/mdx-components', 'private-next-root-dir/src/mdx-components' From 1eccc1e27ae3850ffeb846f445747bdece2a6fa8 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Wed, 13 Nov 2024 03:34:18 +0700 Subject: [PATCH 13/13] remove dirname --- packages/nextra/src/server/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/nextra/src/server/index.ts b/packages/nextra/src/server/index.ts index 9bb1d38eda..95edfa55d2 100644 --- a/packages/nextra/src/server/index.ts +++ b/packages/nextra/src/server/index.ts @@ -15,9 +15,7 @@ const DEFAULT_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx'] as const const FILENAME = fileURLToPath(import.meta.url) -const DIRNAME = path.dirname(FILENAME) - -const LOADER_PATH = path.join(DIRNAME, '..', '..', 'loader.cjs') +const LOADER_PATH = path.join(FILENAME, '..', '..', '..', 'loader.cjs') const SEP = path.sep === '/' ? '/' : '\\\\'