Skip to content

Commit 80e11e0

Browse files
author
Dimitri POSTOLOV
authoredOct 19, 2023
[v3] refactor mdx plugins project structure (#2463)
1 parent 4ec7089 commit 80e11e0

36 files changed

+185
-155
lines changed
 

‎.changeset/warm-llamas-refuse.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'nextra': patch
3+
---
4+
5+
move `resolvePageMap` to `nextra/page-map-dynamic`

‎packages/nextra/__test__/collect-catch-all.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createCatchAllMeta } from '../src/client/catch-all.js'
2-
import { collectCatchAllRoutes } from '../src/client/setup-page.js'
2+
import { collectCatchAllRoutes } from '../src/server/page-map-dynamic.js'
33

44
describe('collectCatchAllRoutes', () => {
55
it('should collect', () => {

‎packages/nextra/__test__/fixture/page-maps/display-hidden-for-mobile/generated-page-map.js ‎packages/nextra/__test__/fixture/page-maps/display-hidden-for-mobile/chunks/generated-page-map.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import test_fixture_page_maps_display_hidden_for_mobile_meta from "./_meta.ts";
1+
import test_fixture_page_maps_display_hidden_for_mobile_meta from "../_meta.ts";
22
export const pageMap = [{
33
data: test_fixture_page_maps_display_hidden_for_mobile_meta
44
}, {

‎packages/nextra/__test__/normalize-page.spec.ts

+18-17
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import fs from 'fs/promises'
22
import path from 'node:path'
33
import { normalizePages } from '../src/client/normalize-pages.js'
4-
import { collectPageMap } from '../src/server/page-map.js'
54
import { cnPageMap, usPageMap } from './fixture/page-maps/pageMap.js'
65

7-
vi.mock('next/dist/lib/find-pages-dir.js', () => ({
8-
findPagesDir: () => ({
9-
pagesDir: 'update me in related test'
10-
})
11-
}))
12-
136
describe('normalize-page', () => {
147
it('zh-CN home', () => {
158
const result = normalizePages({
@@ -192,18 +185,26 @@ describe('normalize-page', () => {
192185
'page-maps',
193186
'display-hidden-for-mobile'
194187
)
195-
let rawJs = await collectPageMap({ dir })
196-
// TODO: quick fix, found better approach
197-
rawJs = rawJs.replace(
198-
/.*/,
199-
'import test_fixture_page_maps_display_hidden_for_mobile_meta from "./_meta.ts";'
200-
)
188+
const pageMapPath = path.join(dir, 'chunks', 'generated-page-map.js')
201189

202-
await fs.writeFile(path.join(dir, 'generated-page-map.js'), rawJs)
190+
vi.doMock('next/dist/lib/find-pages-dir', () => ({
191+
findPagesDir: () => ({ pagesDir: '#' })
192+
}))
203193

204-
const { pageMap } = await import(
205-
'./fixture/page-maps/display-hidden-for-mobile/generated-page-map.js'
206-
)
194+
vi.doMock('../src/server/constants', async () => {
195+
const actual = await vi.importActual<object>('../src/server/constants')
196+
return {
197+
...actual,
198+
CHUNKS_DIR: path.dirname(pageMapPath)
199+
}
200+
})
201+
202+
const { collectPageMap } = await import('../src/server/page-map.js')
203+
204+
const rawJs = await collectPageMap({ dir })
205+
await fs.writeFile(pageMapPath, rawJs)
206+
207+
const { pageMap } = await import(pageMapPath)
207208
const result = normalizePages({ list: pageMap, route: '/' })
208209
expect(result).toMatchInlineSnapshot(`
209210
{

‎packages/nextra/__test__/page-map.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ describe('collectPageMap', () => {
415415
\\"/en/remote/graphql-yoga\\": examples_swr_site_pages_en_remote_graphql_yoga_meta
416416
};
417417
418-
import { resolvePageMap } from 'nextra/setup-page'
418+
import { resolvePageMap } from 'nextra/page-map-dynamic'
419419
420420
if (typeof window === 'undefined') {
421421
globalThis.__nextra_resolvePageMap ||= Object.create(null)
@@ -481,7 +481,7 @@ describe('Page Process', () => {
481481
\\"/my-dir\\": test_fixture_page_maps_dynamic_route_my_dir_meta
482482
};
483483
484-
import { resolvePageMap } from 'nextra/setup-page'
484+
import { resolvePageMap } from 'nextra/page-map-dynamic'
485485
486486
if (typeof window === 'undefined') {
487487
globalThis.__nextra_resolvePageMap ||= Object.create(null)

‎packages/nextra/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
"import": "./dist/server/fetch-filepaths-from-github.js",
4545
"types": "./dist/server/fetch-filepaths-from-github.d.ts"
4646
},
47+
"./page-map-dynamic": {
48+
"import": "./dist/server/page-map-dynamic.js",
49+
"types": "./dist/server/page-map-dynamic.d.ts"
50+
},
4751
"./*": {
4852
"import": "./dist/client/*.js",
4953
"types": "./dist/client/*.d.ts"

‎packages/nextra/src/client/jsx-runtime.cjs

-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
/* eslint-env node */
2-
/**
3-
* Copyright (c) HashiCorp, Inc.
4-
* SPDX-License-Identifier: MPL-2.0
5-
*/
62

73
/**
84
* Allow jsx-runtime to be successfully imported from either React 17 or React 18.

‎packages/nextra/src/client/setup-page.tsx

+1-110
Original file line numberDiff line numberDiff line change
@@ -5,126 +5,17 @@
55

66
import type { ReactElement, ReactNode } from 'react'
77
import { NEXTRA_INTERNAL } from '../constants.js'
8-
import { normalizePageRoute, pageTitleFromFilename } from '../server/utils.js'
98
import type {
10-
DynamicFolder,
11-
DynamicMeta,
12-
DynamicMetaDescriptor,
13-
DynamicMetaItem,
14-
DynamicMetaJsonFile,
15-
Folder,
169
Heading,
1710
NextraInternalGlobal,
1811
NextraMDXContent,
19-
PageMapItem,
2012
PageOpts
2113
} from '../types'
14+
import { findFolder } from '../utils.js'
2215
import { DataProvider } from './data.js'
2316
import { useRouter } from './hooks/index.js'
2417
import { useMDXComponents } from './mdx.js'
2518

26-
function isFolder(value: DynamicMetaItem): value is DynamicFolder {
27-
return !!value && typeof value === 'object' && value.type === 'folder'
28-
}
29-
30-
function normalizeMetaData(obj: DynamicMeta): DynamicMeta {
31-
return Object.fromEntries(
32-
Object.entries(obj).map(([key, value]) => {
33-
if (isFolder(value)) {
34-
const keyWithoutSlash = key.replace('/', '')
35-
return [
36-
keyWithoutSlash,
37-
value.title || pageTitleFromFilename(keyWithoutSlash)
38-
]
39-
}
40-
return [key, value || pageTitleFromFilename(key)]
41-
})
42-
)
43-
}
44-
45-
export function collectCatchAllRoutes(
46-
parent: Folder,
47-
meta: DynamicMetaJsonFile,
48-
isRootFolder = true
49-
): Folder {
50-
if (isRootFolder) {
51-
const folder = collectCatchAllRoutes(parent, meta, false)
52-
53-
return {
54-
...folder,
55-
children: [{ data: normalizeMetaData(meta.data) }, ...folder.children]
56-
}
57-
}
58-
const result = []
59-
60-
for (const [key, value] of Object.entries(meta.data)) {
61-
if (!isFolder(value)) {
62-
if (key === '*') {
63-
continue
64-
}
65-
result.push({
66-
name: key,
67-
route: normalizePageRoute(parent.route, key)
68-
})
69-
continue
70-
}
71-
const routeWithoutSlashes = key.replace('/', '')
72-
const newParent: Folder = {
73-
name: routeWithoutSlashes,
74-
route: `${parent.route}/${routeWithoutSlashes}`,
75-
children: [{ data: normalizeMetaData(value.items) }]
76-
}
77-
newParent.children.push(
78-
...collectCatchAllRoutes(newParent, { data: value.items }, false).children
79-
)
80-
result.push(newParent)
81-
}
82-
83-
return {
84-
route: parent.route,
85-
name: parent.name,
86-
children: result
87-
}
88-
}
89-
90-
const cachedResolvedPageMap: Record<string, PageMapItem[]> = Object.create(null)
91-
92-
function findFolder(pageMap: PageMapItem[], [path, ...paths]: string[]): any {
93-
for (const item of pageMap) {
94-
if ('children' in item && path === item.name) {
95-
return paths.length ? findFolder(item.children, paths) : item
96-
}
97-
}
98-
}
99-
100-
export const resolvePageMap =
101-
(locale: string, dynamicMetaModules: DynamicMetaDescriptor) => async () => {
102-
const __nextra_internal__ = (globalThis as NextraInternalGlobal)[
103-
NEXTRA_INTERNAL
104-
]
105-
if (
106-
process.env.NODE_ENV === 'production' &&
107-
cachedResolvedPageMap[locale]
108-
) {
109-
return cachedResolvedPageMap[locale]
110-
}
111-
const { pageMap } = locale
112-
? Object.entries(__nextra_internal__.context)
113-
// Fix race condition. Find a better way to get pageMap?
114-
.find(([route]) => route.startsWith(`/${locale}/`))![1].pageOpts
115-
: __nextra_internal__
116-
const result = await Promise.all(
117-
Object.entries(dynamicMetaModules).map(async ([route, metaFunction]) => {
118-
const paths = route.split('/').slice(locale ? 2 : 1)
119-
const folder = findFolder(pageMap, paths)
120-
const metaData = await metaFunction()
121-
return collectCatchAllRoutes(folder, { data: metaData })
122-
})
123-
)
124-
125-
return (cachedResolvedPageMap[locale] = result)
126-
}
127-
12819
export function HOC_MDXWrapper(
12920
MDXContent: NextraMDXContent,
13021
route: string,

‎packages/nextra/src/server/compile.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,17 @@ import {
3030
MARKDOWN_URL_EXTENSION_REGEX
3131
} from './constants.js'
3232
import {
33-
attachMeta,
34-
parseMeta,
3533
recmaRewriteFunctionBody,
36-
recmaRewriteJsx,
34+
recmaRewriteJsx
35+
} from './recma-plugins/index.js'
36+
import {
37+
rehypeAttachCodeMeta,
38+
rehypeBetterReactMathjax,
39+
rehypeExtractTocContent,
3740
rehypeIcon,
41+
rehypeParseCodeMeta
42+
} from './rehype-plugins/index.js'
43+
import {
3844
remarkCustomHeadingId,
3945
remarkHeadings,
4046
remarkLinkRewrite,
@@ -44,9 +50,7 @@ import {
4450
remarkRemoveImports,
4551
remarkStaticImage,
4652
remarkStructurize
47-
} from './mdx-plugins/index.js'
48-
import { rehypeBetterReactMathjax } from './mdx-plugins/rehype-better-react-mathjax.js'
49-
import { rehypeExtractTocContent } from './mdx-plugins/rehype-extract-toc-content.js'
53+
} from './remark-plugins/index.js'
5054
import { logger, truthy } from './utils.js'
5155

5256
export const DEFAULT_REHYPE_PRETTY_CODE_OPTIONS: RehypePrettyCodeOptions = {
@@ -300,7 +304,7 @@ export async function compileMdx(
300304
passThrough: ['mdxjsEsm', 'mdxJsxFlowElement', 'mdxTextExpression']
301305
}
302306
],
303-
[parseMeta, { defaultShowCopyCode }],
307+
[rehypeParseCodeMeta, { defaultShowCopyCode }],
304308
// Should be before `rehypePrettyCode`
305309
latex &&
306310
(typeof latex === 'object'
@@ -313,7 +317,7 @@ export async function compileMdx(
313317
: [
314318
[rehypePrettyCode, DEFAULT_REHYPE_PRETTY_CODE_OPTIONS] as any,
315319
!isRemoteContent && rehypeIcon,
316-
attachMeta
320+
rehypeAttachCodeMeta
317321
]),
318322
[rehypeExtractTocContent, { isRemoteContent }]
319323
].filter(truthy),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { NEXTRA_INTERNAL } from '../constants.js'
2+
import type {
3+
DynamicFolder,
4+
DynamicMeta,
5+
DynamicMetaDescriptor,
6+
DynamicMetaItem,
7+
DynamicMetaJsonFile,
8+
Folder,
9+
NextraInternalGlobal,
10+
PageMapItem
11+
} from '../types'
12+
import { findFolder } from '../utils.js'
13+
import { normalizePageRoute, pageTitleFromFilename } from './utils.js'
14+
15+
const cachedResolvedPageMap: Record<string, PageMapItem[]> = Object.create(null)
16+
17+
function isFolder(value: DynamicMetaItem): value is DynamicFolder {
18+
return !!value && typeof value === 'object' && value.type === 'folder'
19+
}
20+
21+
function normalizeMetaData(obj: DynamicMeta): DynamicMeta {
22+
return Object.fromEntries(
23+
Object.entries(obj).map(([key, value]) => {
24+
if (isFolder(value)) {
25+
const keyWithoutSlash = key.replace('/', '')
26+
return [
27+
keyWithoutSlash,
28+
value.title || pageTitleFromFilename(keyWithoutSlash)
29+
]
30+
}
31+
return [key, value || pageTitleFromFilename(key)]
32+
})
33+
)
34+
}
35+
36+
export function collectCatchAllRoutes(
37+
parent: Folder,
38+
meta: DynamicMetaJsonFile,
39+
isRootFolder = true
40+
): Folder {
41+
if (isRootFolder) {
42+
const folder = collectCatchAllRoutes(parent, meta, false)
43+
44+
return {
45+
...folder,
46+
children: [{ data: normalizeMetaData(meta.data) }, ...folder.children]
47+
}
48+
}
49+
const result = []
50+
51+
for (const [key, value] of Object.entries(meta.data)) {
52+
if (!isFolder(value)) {
53+
if (key === '*') {
54+
continue
55+
}
56+
result.push({
57+
name: key,
58+
route: normalizePageRoute(parent.route, key)
59+
})
60+
continue
61+
}
62+
const routeWithoutSlashes = key.replace('/', '')
63+
const newParent: Folder = {
64+
name: routeWithoutSlashes,
65+
route: `${parent.route}/${routeWithoutSlashes}`,
66+
children: [{ data: normalizeMetaData(value.items) }]
67+
}
68+
newParent.children.push(
69+
...collectCatchAllRoutes(newParent, { data: value.items }, false).children
70+
)
71+
result.push(newParent)
72+
}
73+
74+
return {
75+
route: parent.route,
76+
name: parent.name,
77+
children: result
78+
}
79+
}
80+
81+
export const resolvePageMap =
82+
(locale: string, dynamicMetaModules: DynamicMetaDescriptor) => async () => {
83+
const __nextra_internal__ = (globalThis as NextraInternalGlobal)[
84+
NEXTRA_INTERNAL
85+
]
86+
if (
87+
process.env.NODE_ENV === 'production' &&
88+
cachedResolvedPageMap[locale]
89+
) {
90+
return cachedResolvedPageMap[locale]
91+
}
92+
const { pageMap } = locale
93+
? Object.entries(__nextra_internal__.context)
94+
// Fix race condition. Find a better way to get pageMap?
95+
.find(([route]) => route.startsWith(`/${locale}/`))![1].pageOpts
96+
: __nextra_internal__
97+
const result = await Promise.all(
98+
Object.entries(dynamicMetaModules).map(async ([route, metaFunction]) => {
99+
const paths = route.split('/').slice(locale ? 2 : 1)
100+
const folder = findFolder(pageMap, paths)
101+
const metaData = await metaFunction()
102+
return collectCatchAllRoutes(folder, { data: metaData })
103+
})
104+
)
105+
106+
return (cachedResolvedPageMap[locale] = result)
107+
}

0 commit comments

Comments
 (0)
Please sign in to comment.