methanol 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +1 -1
- package/src/build-system.js +116 -41
- package/src/config.js +13 -2
- package/src/dev-server.js +155 -24
- package/src/logger.js +15 -3
- package/src/mdx.js +68 -39
- package/src/pages.js +224 -57
- package/src/state.js +18 -14
- package/src/workers/build-pool.js +156 -0
- package/src/workers/build-worker.js +203 -0
- package/src/workers/mdx-compile-worker.js +60 -0
- package/themes/default/src/nav-tree.jsx +8 -7
package/README.md
CHANGED
|
@@ -41,9 +41,12 @@ export default () => ({
|
|
|
41
41
|
enabled: true
|
|
42
42
|
},
|
|
43
43
|
|
|
44
|
-
// optional: code highlighting (Starry Night)
|
|
44
|
+
// optional: code highlighting (Starry Night, default: enabled)
|
|
45
45
|
starryNight: false,
|
|
46
46
|
|
|
47
|
+
// optional: worker thread count (0 = auto)
|
|
48
|
+
jobs: 0,
|
|
49
|
+
|
|
47
50
|
// optional: pwa support
|
|
48
51
|
pwa: true,
|
|
49
52
|
|
package/package.json
CHANGED
package/src/build-system.js
CHANGED
|
@@ -26,8 +26,8 @@ import { VitePWA } from 'vite-plugin-pwa'
|
|
|
26
26
|
import { state, cli } from './state.js'
|
|
27
27
|
import { resolveUserViteConfig } from './config.js'
|
|
28
28
|
import { buildPagesContext } from './pages.js'
|
|
29
|
-
import { renderHtml } from './mdx.js'
|
|
30
29
|
import { buildComponentRegistry } from './components.js'
|
|
30
|
+
import { createBuildWorkers, runWorkerStage, terminateWorkers } from './workers/build-pool.js'
|
|
31
31
|
import { methanolVirtualHtmlPlugin, methanolResolverPlugin } from './vite-plugins.js'
|
|
32
32
|
import { createStageLogger } from './stage-logger.js'
|
|
33
33
|
import { preparePublicAssets } from './public-assets.js'
|
|
@@ -40,7 +40,6 @@ const ensureDir = async (dir) => {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const isHtmlFile = (name) => name.endsWith('.html')
|
|
43
|
-
|
|
44
43
|
const collectHtmlFiles = async (dir, basePath = '') => {
|
|
45
44
|
const entries = await readdir(dir)
|
|
46
45
|
const files = []
|
|
@@ -76,22 +75,14 @@ export const buildHtmlEntries = async () => {
|
|
|
76
75
|
const stageLogger = createStageLogger(logEnabled)
|
|
77
76
|
const themeComponentsDir = state.THEME_COMPONENTS_DIR
|
|
78
77
|
const themeEnv = state.THEME_ENV
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
: { components: {} }
|
|
85
|
-
const themeComponents = {
|
|
86
|
-
...(themeRegistry.components || {}),
|
|
87
|
-
...(state.USER_THEME.components || {})
|
|
88
|
-
}
|
|
89
|
-
const { components } = await buildComponentRegistry()
|
|
90
|
-
const mergedComponents = {
|
|
91
|
-
...themeComponents,
|
|
92
|
-
...components
|
|
78
|
+
if (themeComponentsDir) {
|
|
79
|
+
await buildComponentRegistry({
|
|
80
|
+
componentsDir: themeComponentsDir,
|
|
81
|
+
client: themeEnv.client
|
|
82
|
+
})
|
|
93
83
|
}
|
|
94
|
-
|
|
84
|
+
await buildComponentRegistry()
|
|
85
|
+
const pagesContext = await buildPagesContext({ compileAll: false })
|
|
95
86
|
const entry = {}
|
|
96
87
|
const htmlCache = new Map()
|
|
97
88
|
const resolveOutputName = (page) => {
|
|
@@ -102,39 +93,123 @@ export const buildHtmlEntries = async () => {
|
|
|
102
93
|
return page.routePath.slice(1)
|
|
103
94
|
}
|
|
104
95
|
|
|
105
|
-
const
|
|
106
|
-
const totalPages =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
96
|
+
const pages = pagesContext.pages || []
|
|
97
|
+
const totalPages = pages.length
|
|
98
|
+
const { workers, assignments } = createBuildWorkers(totalPages)
|
|
99
|
+
const excludedRoutes = Array.from(pagesContext.excludedRoutes || [])
|
|
100
|
+
const excludedDirs = Array.from(pagesContext.excludedDirs || [])
|
|
101
|
+
try {
|
|
102
|
+
await runWorkerStage({
|
|
103
|
+
workers,
|
|
104
|
+
stage: 'setPages',
|
|
105
|
+
messages: workers.map((worker) => ({
|
|
106
|
+
worker,
|
|
107
|
+
message: {
|
|
108
|
+
type: 'setPages',
|
|
109
|
+
stage: 'setPages',
|
|
110
|
+
pages,
|
|
111
|
+
excludedRoutes,
|
|
112
|
+
excludedDirs
|
|
113
|
+
}
|
|
114
|
+
}))
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const compileToken = stageLogger.start('Compiling MDX')
|
|
118
|
+
let completed = 0
|
|
119
|
+
const updates = await runWorkerStage({
|
|
120
|
+
workers,
|
|
121
|
+
stage: 'compile',
|
|
122
|
+
messages: workers.map((worker, index) => ({
|
|
123
|
+
worker,
|
|
124
|
+
message: {
|
|
125
|
+
type: 'compile',
|
|
126
|
+
stage: 'compile',
|
|
127
|
+
ids: assignments[index]
|
|
128
|
+
}
|
|
129
|
+
})),
|
|
130
|
+
onProgress: (count) => {
|
|
131
|
+
if (!logEnabled) return
|
|
132
|
+
completed = count
|
|
133
|
+
stageLogger.update(compileToken, `Compiling MDX [${completed}/${totalPages}]`)
|
|
134
|
+
},
|
|
135
|
+
collect: (message) => message.updates || []
|
|
136
|
+
})
|
|
137
|
+
stageLogger.end(compileToken)
|
|
138
|
+
|
|
139
|
+
for (const update of updates) {
|
|
140
|
+
const page = pages[update.id]
|
|
141
|
+
if (!page) continue
|
|
142
|
+
if (update.title !== undefined) page.title = update.title
|
|
143
|
+
if (update.toc !== undefined) page.toc = update.toc
|
|
144
|
+
if (typeof pagesContext.setDerivedTitle === 'function') {
|
|
145
|
+
const shouldUseTocTitle = page.frontmatter?.title == null
|
|
146
|
+
pagesContext.setDerivedTitle(page.path, shouldUseTocTitle ? page.title : null, page.toc || null)
|
|
147
|
+
}
|
|
111
148
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
149
|
+
pagesContext.refreshPagesTree?.()
|
|
150
|
+
|
|
151
|
+
const titleSnapshot = pages.map((page) => page.title)
|
|
152
|
+
await runWorkerStage({
|
|
153
|
+
workers,
|
|
154
|
+
stage: 'sync',
|
|
155
|
+
messages: workers.map((worker) => ({
|
|
156
|
+
worker,
|
|
157
|
+
message: {
|
|
158
|
+
type: 'sync',
|
|
159
|
+
stage: 'sync',
|
|
160
|
+
updates,
|
|
161
|
+
titles: titleSnapshot
|
|
162
|
+
}
|
|
163
|
+
}))
|
|
118
164
|
})
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
165
|
+
|
|
166
|
+
const renderToken = stageLogger.start('Rendering pages')
|
|
167
|
+
completed = 0
|
|
168
|
+
const rendered = await runWorkerStage({
|
|
169
|
+
workers,
|
|
170
|
+
stage: 'render',
|
|
171
|
+
messages: workers.map((worker, index) => ({
|
|
172
|
+
worker,
|
|
173
|
+
message: {
|
|
174
|
+
type: 'render',
|
|
175
|
+
stage: 'render',
|
|
176
|
+
ids: assignments[index]
|
|
177
|
+
}
|
|
178
|
+
})),
|
|
179
|
+
onProgress: (count) => {
|
|
180
|
+
if (!logEnabled) return
|
|
181
|
+
completed = count
|
|
182
|
+
stageLogger.update(renderToken, `Rendering pages [${completed}/${totalPages}]`)
|
|
183
|
+
},
|
|
184
|
+
collect: (message) => message.results || []
|
|
185
|
+
})
|
|
186
|
+
stageLogger.end(renderToken)
|
|
187
|
+
|
|
188
|
+
for (const item of rendered) {
|
|
189
|
+
const page = pages[item.id]
|
|
190
|
+
if (!page) continue
|
|
191
|
+
const html = item.html
|
|
192
|
+
const name = resolveOutputName(page)
|
|
193
|
+
const id = normalizePath(resolve(state.VIRTUAL_HTML_OUTPUT_ROOT, `${name}.html`))
|
|
194
|
+
entry[name] = id
|
|
195
|
+
htmlCache.set(id, html)
|
|
196
|
+
if (state.INTERMEDIATE_DIR) {
|
|
197
|
+
const outPath = resolve(state.INTERMEDIATE_DIR, `${name}.html`)
|
|
198
|
+
await ensureDir(dirname(outPath))
|
|
199
|
+
await writeFile(outPath, html)
|
|
200
|
+
}
|
|
127
201
|
}
|
|
202
|
+
} finally {
|
|
203
|
+
await terminateWorkers(workers)
|
|
128
204
|
}
|
|
129
|
-
stageLogger.end(renderToken)
|
|
130
205
|
|
|
131
206
|
const htmlFiles = await collectHtmlFiles(state.PAGES_DIR)
|
|
132
|
-
const
|
|
207
|
+
const htmlExcludedDirs = pagesContext.excludedDirs || new Set()
|
|
133
208
|
const isHtmlExcluded = (relativePath) => {
|
|
134
|
-
if (!
|
|
209
|
+
if (!htmlExcludedDirs.size) return false
|
|
135
210
|
const dir = relativePath.split('/').slice(0, -1).join('/')
|
|
136
211
|
if (!dir) return false
|
|
137
|
-
for (const excludedDir of
|
|
212
|
+
for (const excludedDir of htmlExcludedDirs) {
|
|
138
213
|
if (!excludedDir) return true
|
|
139
214
|
if (dir === excludedDir || dir.startsWith(`${excludedDir}/`)) {
|
|
140
215
|
return true
|
package/src/config.js
CHANGED
|
@@ -191,12 +191,12 @@ const resolvePagefindBuild = (config) => {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
const resolveStarryNightConfig = (value) => {
|
|
194
|
-
if (value == null) return { enabled:
|
|
194
|
+
if (value == null) return { enabled: true, options: null }
|
|
195
195
|
if (typeof value === 'boolean') {
|
|
196
196
|
return { enabled: value, options: null }
|
|
197
197
|
}
|
|
198
198
|
if (typeof value !== 'object') {
|
|
199
|
-
return { enabled:
|
|
199
|
+
return { enabled: true, options: null }
|
|
200
200
|
}
|
|
201
201
|
const { enabled, options, ...rest } = value
|
|
202
202
|
if (enabled === false) return { enabled: false, options: null }
|
|
@@ -216,6 +216,14 @@ const normalizeHooks = (value) => {
|
|
|
216
216
|
return []
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
const normalizeJobs = (value) => {
|
|
220
|
+
if (value == null) return null
|
|
221
|
+
const parsed = Number(value)
|
|
222
|
+
if (!Number.isFinite(parsed)) return null
|
|
223
|
+
if (parsed <= 0) return 0
|
|
224
|
+
return Math.floor(parsed)
|
|
225
|
+
}
|
|
226
|
+
|
|
219
227
|
const loadConfigModule = async (filePath) => {
|
|
220
228
|
return import(`${pathToFileURL(filePath).href}?t=${Date.now()}`)
|
|
221
229
|
}
|
|
@@ -425,6 +433,9 @@ export const applyConfig = async (config, mode) => {
|
|
|
425
433
|
state.STARRY_NIGHT_OPTIONS = starryNight.enabled ? starryNight.options : null
|
|
426
434
|
}
|
|
427
435
|
|
|
436
|
+
const jobsValue = cli.CLI_JOBS != null ? cli.CLI_JOBS : config.jobs
|
|
437
|
+
state.WORKER_JOBS = normalizeJobs(jobsValue) ?? 0
|
|
438
|
+
|
|
428
439
|
if (cli.CLI_INTERMEDIATE_DIR) {
|
|
429
440
|
state.INTERMEDIATE_DIR = resolveFromRoot(root, cli.CLI_INTERMEDIATE_DIR, 'build')
|
|
430
441
|
} else if (config.intermediateDir) {
|
package/src/dev-server.js
CHANGED
|
@@ -40,6 +40,8 @@ import { buildPagesContext, buildPageEntry, routePathFromFile } from './pages.js
|
|
|
40
40
|
import { compilePageMdx, renderHtml } from './mdx.js'
|
|
41
41
|
import { methanolResolverPlugin } from './vite-plugins.js'
|
|
42
42
|
import { preparePublicAssets, updateAsset } from './public-assets.js'
|
|
43
|
+
import { createBuildWorkers, runWorkerStage, terminateWorkers } from './workers/build-pool.js'
|
|
44
|
+
import { style } from './logger.js'
|
|
43
45
|
|
|
44
46
|
export const runViteDev = async () => {
|
|
45
47
|
const baseFsAllow = [state.ROOT_DIR, state.USER_THEME.root].filter(Boolean)
|
|
@@ -152,19 +154,128 @@ export const runViteDev = async () => {
|
|
|
152
154
|
htmlCache.clear()
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
const logMdxError = (phase, error, page = null) => {
|
|
158
|
+
const target = page?.path || page?.routePath || 'unknown file'
|
|
159
|
+
console.error(style.red(`\n[methanol] ${phase} error in ${target}`))
|
|
160
|
+
console.error(error?.stack || error)
|
|
161
|
+
}
|
|
162
|
+
|
|
155
163
|
const refreshPagesContext = async () => {
|
|
156
164
|
setPagesContext(await buildPagesContext({ compileAll: false }))
|
|
157
165
|
}
|
|
158
166
|
|
|
167
|
+
const prebuildHtmlCache = async (token) => {
|
|
168
|
+
if (!pagesContext || token !== pagesContextToken) return
|
|
169
|
+
const pages = pagesContext.pages || []
|
|
170
|
+
if (!pages.length) return
|
|
171
|
+
const { workers, assignments } = createBuildWorkers(pages.length, { command: 'serve' })
|
|
172
|
+
const excludedRoutes = Array.from(pagesContext.excludedRoutes || [])
|
|
173
|
+
const excludedDirs = Array.from(pagesContext.excludedDirs || [])
|
|
174
|
+
try {
|
|
175
|
+
await runWorkerStage({
|
|
176
|
+
workers,
|
|
177
|
+
stage: 'setPages',
|
|
178
|
+
messages: workers.map((worker) => ({
|
|
179
|
+
worker,
|
|
180
|
+
message: {
|
|
181
|
+
type: 'setPages',
|
|
182
|
+
stage: 'setPages',
|
|
183
|
+
pages,
|
|
184
|
+
excludedRoutes,
|
|
185
|
+
excludedDirs
|
|
186
|
+
}
|
|
187
|
+
}))
|
|
188
|
+
})
|
|
189
|
+
if (token !== pagesContextToken) return
|
|
190
|
+
|
|
191
|
+
const updates = await runWorkerStage({
|
|
192
|
+
workers,
|
|
193
|
+
stage: 'compile',
|
|
194
|
+
messages: workers.map((worker, index) => ({
|
|
195
|
+
worker,
|
|
196
|
+
message: {
|
|
197
|
+
type: 'compile',
|
|
198
|
+
stage: 'compile',
|
|
199
|
+
ids: assignments[index]
|
|
200
|
+
}
|
|
201
|
+
})),
|
|
202
|
+
collect: (message) => message.updates || []
|
|
203
|
+
})
|
|
204
|
+
if (token !== pagesContextToken) return
|
|
205
|
+
|
|
206
|
+
for (const update of updates) {
|
|
207
|
+
const page = pages[update.id]
|
|
208
|
+
if (!page) continue
|
|
209
|
+
if (update.title !== undefined) page.title = update.title
|
|
210
|
+
if (update.toc !== undefined) page.toc = update.toc
|
|
211
|
+
if (typeof pagesContext.setDerivedTitle === 'function') {
|
|
212
|
+
const shouldUseTocTitle = page.frontmatter?.title == null
|
|
213
|
+
pagesContext.setDerivedTitle(page.path, shouldUseTocTitle ? page.title : null, page.toc || null)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
pagesContext.refreshPagesTree?.()
|
|
217
|
+
invalidateHtmlCache()
|
|
218
|
+
const renderEpoch = htmlCacheEpoch
|
|
219
|
+
|
|
220
|
+
const titleSnapshot = pages.map((page) => page.title)
|
|
221
|
+
await runWorkerStage({
|
|
222
|
+
workers,
|
|
223
|
+
stage: 'sync',
|
|
224
|
+
messages: workers.map((worker) => ({
|
|
225
|
+
worker,
|
|
226
|
+
message: {
|
|
227
|
+
type: 'sync',
|
|
228
|
+
stage: 'sync',
|
|
229
|
+
updates,
|
|
230
|
+
titles: titleSnapshot
|
|
231
|
+
}
|
|
232
|
+
}))
|
|
233
|
+
})
|
|
234
|
+
if (token !== pagesContextToken) return
|
|
235
|
+
|
|
236
|
+
const rendered = await runWorkerStage({
|
|
237
|
+
workers,
|
|
238
|
+
stage: 'render',
|
|
239
|
+
messages: workers.map((worker, index) => ({
|
|
240
|
+
worker,
|
|
241
|
+
message: {
|
|
242
|
+
type: 'render',
|
|
243
|
+
stage: 'render',
|
|
244
|
+
ids: assignments[index]
|
|
245
|
+
}
|
|
246
|
+
})),
|
|
247
|
+
collect: (message) => message.results || []
|
|
248
|
+
})
|
|
249
|
+
if (token !== pagesContextToken || renderEpoch !== htmlCacheEpoch) return
|
|
250
|
+
for (const item of rendered) {
|
|
251
|
+
const page = pages[item.id]
|
|
252
|
+
if (!page) continue
|
|
253
|
+
htmlCache.set(page.routePath, {
|
|
254
|
+
html: item.html,
|
|
255
|
+
path: page.path,
|
|
256
|
+
epoch: renderEpoch,
|
|
257
|
+
token
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
} finally {
|
|
261
|
+
await terminateWorkers(workers)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
159
265
|
const runInitialCompile = async () => {
|
|
160
266
|
const token = pagesContextToken
|
|
161
267
|
try {
|
|
162
|
-
const nextContext = await buildPagesContext({ compileAll:
|
|
268
|
+
const nextContext = await buildPagesContext({ compileAll: false })
|
|
163
269
|
if (token !== pagesContextToken) {
|
|
164
270
|
return
|
|
165
271
|
}
|
|
166
272
|
setPagesContext(nextContext)
|
|
273
|
+
const nextToken = pagesContextToken
|
|
167
274
|
invalidateHtmlCache()
|
|
275
|
+
await prebuildHtmlCache(nextToken)
|
|
276
|
+
if (nextToken !== pagesContextToken) {
|
|
277
|
+
return
|
|
278
|
+
}
|
|
168
279
|
reload()
|
|
169
280
|
} catch (err) {
|
|
170
281
|
console.error(err)
|
|
@@ -257,10 +368,6 @@ export const runViteDev = async () => {
|
|
|
257
368
|
return next()
|
|
258
369
|
}
|
|
259
370
|
|
|
260
|
-
if (pathname.includes('.') && !pathname.endsWith('.html')) {
|
|
261
|
-
return next()
|
|
262
|
-
}
|
|
263
|
-
|
|
264
371
|
const accept = req.headers.accept || ''
|
|
265
372
|
if (!pathname.endsWith('.html') && !accept.includes('text/html')) {
|
|
266
373
|
return next()
|
|
@@ -274,6 +381,11 @@ export const runViteDev = async () => {
|
|
|
274
381
|
}
|
|
275
382
|
}
|
|
276
383
|
const requestedPath = routePath
|
|
384
|
+
if (pathname.includes('.') && !pathname.endsWith('.html')) {
|
|
385
|
+
if (!pagesContext?.pagesByRoute?.has(requestedPath)) {
|
|
386
|
+
return next()
|
|
387
|
+
}
|
|
388
|
+
}
|
|
277
389
|
const isExcludedPath = () => {
|
|
278
390
|
const excludedRoutes = pagesContext.excludedRoutes
|
|
279
391
|
if (excludedRoutes?.has(requestedPath)) return true
|
|
@@ -347,7 +459,12 @@ export const runViteDev = async () => {
|
|
|
347
459
|
try {
|
|
348
460
|
const renderEpoch = htmlCacheEpoch
|
|
349
461
|
const cacheEntry = htmlCache.get(renderRoutePath)
|
|
350
|
-
if (
|
|
462
|
+
if (
|
|
463
|
+
cacheEntry &&
|
|
464
|
+
cacheEntry.path === path &&
|
|
465
|
+
cacheEntry.epoch === htmlCacheEpoch &&
|
|
466
|
+
cacheEntry.token === pagesContextToken
|
|
467
|
+
) {
|
|
351
468
|
res.statusCode = status
|
|
352
469
|
res.setHeader('Content-Type', 'text/html')
|
|
353
470
|
res.end(cacheEntry.html)
|
|
@@ -356,28 +473,37 @@ export const runViteDev = async () => {
|
|
|
356
473
|
|
|
357
474
|
pageMeta ??= pagesContext.getPageByRoute(renderRoutePath, { path })
|
|
358
475
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
476
|
+
let html = ''
|
|
477
|
+
try {
|
|
478
|
+
html = await renderHtml({
|
|
479
|
+
routePath: renderRoutePath,
|
|
480
|
+
path,
|
|
481
|
+
components: {
|
|
482
|
+
...themeComponents,
|
|
483
|
+
...components
|
|
484
|
+
},
|
|
485
|
+
pagesContext,
|
|
486
|
+
pageMeta
|
|
487
|
+
})
|
|
488
|
+
} catch (err) {
|
|
489
|
+
logMdxError('MDX render', err, pageMeta || { path, routePath: renderRoutePath })
|
|
490
|
+
res.statusCode = 500
|
|
491
|
+
res.end('Internal Server Error')
|
|
492
|
+
return
|
|
493
|
+
}
|
|
369
494
|
if (renderEpoch === htmlCacheEpoch) {
|
|
370
495
|
htmlCache.set(renderRoutePath, {
|
|
371
496
|
html,
|
|
372
497
|
path,
|
|
373
|
-
epoch: renderEpoch
|
|
498
|
+
epoch: renderEpoch,
|
|
499
|
+
token: pagesContextToken
|
|
374
500
|
})
|
|
375
501
|
}
|
|
376
502
|
res.statusCode = status
|
|
377
503
|
res.setHeader('Content-Type', 'text/html')
|
|
378
504
|
res.end(html)
|
|
379
505
|
} catch (err) {
|
|
380
|
-
|
|
506
|
+
logMdxError('MDX render', err, pageMeta || { path, routePath: renderRoutePath })
|
|
381
507
|
res.statusCode = 500
|
|
382
508
|
res.end('Internal Server Error')
|
|
383
509
|
}
|
|
@@ -532,12 +658,17 @@ export const runViteDev = async () => {
|
|
|
532
658
|
pagesContext.refreshPagesTree?.()
|
|
533
659
|
pagesContext.refreshLanguages?.()
|
|
534
660
|
if (prevEntry.content && prevEntry.content.trim().length) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
661
|
+
try {
|
|
662
|
+
await compilePageMdx(prevEntry, pagesContext, {
|
|
663
|
+
lazyPagesTree: true,
|
|
664
|
+
refreshPagesTree: false
|
|
665
|
+
})
|
|
666
|
+
// Avoid caching a potentially stale render; recompile on request.
|
|
667
|
+
prevEntry.mdxComponent = null
|
|
668
|
+
} catch (err) {
|
|
669
|
+
logMdxError('MDX compile', err, prevEntry)
|
|
670
|
+
prevEntry.mdxComponent = null
|
|
671
|
+
}
|
|
541
672
|
}
|
|
542
673
|
return true
|
|
543
674
|
}
|
package/src/logger.js
CHANGED
|
@@ -18,10 +18,22 @@
|
|
|
18
18
|
* under the License.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
const resolveSupportColor = (stream) => {
|
|
22
|
+
if (typeof process === 'undefined') return false
|
|
23
|
+
const env = process.env || {}
|
|
24
|
+
if (env.NO_COLOR != null) return false
|
|
25
|
+
const force = env.FORCE_COLOR
|
|
26
|
+
if (force != null) {
|
|
27
|
+
return force !== '0'
|
|
28
|
+
}
|
|
29
|
+
if (stream && stream.isTTY) return true
|
|
30
|
+
const term = env.TERM || ''
|
|
31
|
+
if (!term || term.toLowerCase() === 'dumb') return false
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
const supportColor =
|
|
22
|
-
|
|
23
|
-
process.stdout &&
|
|
24
|
-
(process.stdout.isTTY || process.env.FORCE_COLOR)
|
|
36
|
+
resolveSupportColor(process.stdout) || resolveSupportColor(process.stderr)
|
|
25
37
|
|
|
26
38
|
const formatter = (open, close, replace = open) =>
|
|
27
39
|
supportColor
|
package/src/mdx.js
CHANGED
|
@@ -131,9 +131,7 @@ const resolvePageHeadAssets = (page) => {
|
|
|
131
131
|
export const buildPageContext = ({ routePath, path, pageMeta, pagesContext, lazyPagesTree = false }) => {
|
|
132
132
|
const page = pageMeta
|
|
133
133
|
const language = pagesContext.getLanguageForRoute ? pagesContext.getLanguageForRoute(routePath) : null
|
|
134
|
-
const getSiblings = pagesContext.getSiblings
|
|
135
|
-
? () => pagesContext.getSiblings(routePath, page.path || path)
|
|
136
|
-
: null
|
|
134
|
+
const getSiblings = pagesContext.getSiblings ? () => pagesContext.getSiblings(routePath, page.path || path) : null
|
|
137
135
|
if (page && getSiblings && page.getSiblings !== getSiblings) {
|
|
138
136
|
page.getSiblings = getSiblings
|
|
139
137
|
}
|
|
@@ -215,7 +213,7 @@ const normalizeStarryNightConfig = (value) => {
|
|
|
215
213
|
}
|
|
216
214
|
if (typeof value !== 'object') return null
|
|
217
215
|
const { enabled, options, ...rest } = value
|
|
218
|
-
if (enabled === false) return { enabled: false, options
|
|
216
|
+
if (enabled === false) return { enabled: false, options }
|
|
219
217
|
if (options && typeof options === 'object') {
|
|
220
218
|
return { enabled: true, options: { ...options } }
|
|
221
219
|
}
|
|
@@ -289,12 +287,17 @@ const resolveMdxConfigForPage = async (frontmatter) => {
|
|
|
289
287
|
return mdxConfig
|
|
290
288
|
}
|
|
291
289
|
|
|
292
|
-
export const
|
|
293
|
-
const mdxConfig = await resolveMdxConfigForPage(
|
|
294
|
-
const runtimeFactory = mdxConfig.development ? JSXDevFactory : JSXFactory
|
|
290
|
+
export const compileMdxSource = async ({ content, path, frontmatter }) => {
|
|
291
|
+
const mdxConfig = await resolveMdxConfigForPage(frontmatter)
|
|
295
292
|
const compiled = await compile({ value: content, path: path }, mdxConfig)
|
|
293
|
+
const code = String(compiled.value ?? compiled)
|
|
294
|
+
return { code, development: Boolean(mdxConfig.development) }
|
|
295
|
+
}
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
export const runMdxSource = async ({ code, path, ctx, development = null }) => {
|
|
298
|
+
const isDev = development == null ? state.CURRENT_MODE !== 'production' : development
|
|
299
|
+
const runtimeFactory = isDev ? JSXDevFactory : JSXFactory
|
|
300
|
+
return await run(code, {
|
|
298
301
|
...runtimeFactory,
|
|
299
302
|
baseUrl: pathToFileURL(path).href,
|
|
300
303
|
ctx,
|
|
@@ -302,9 +305,23 @@ export const compileMdx = async ({ content, path, ctx }) => {
|
|
|
302
305
|
})
|
|
303
306
|
}
|
|
304
307
|
|
|
308
|
+
export const compileMdx = async ({ content, path, ctx }) => {
|
|
309
|
+
const result = await compileMdxSource({
|
|
310
|
+
content,
|
|
311
|
+
path,
|
|
312
|
+
frontmatter: ctx?.page?.frontmatter || null
|
|
313
|
+
})
|
|
314
|
+
return await runMdxSource({
|
|
315
|
+
code: result.code,
|
|
316
|
+
path,
|
|
317
|
+
ctx,
|
|
318
|
+
development: result.development
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
305
322
|
export const compilePageMdx = async (page, pagesContext, options = {}) => {
|
|
306
323
|
if (!page || page.content == null || page.mdxComponent) return
|
|
307
|
-
const { ctx = null, lazyPagesTree = false, refreshPagesTree = true } = options || {}
|
|
324
|
+
const { ctx = null, lazyPagesTree = false, refreshPagesTree = true, compiled = null } = options || {}
|
|
308
325
|
const activeCtx =
|
|
309
326
|
ctx ||
|
|
310
327
|
buildPageContext({
|
|
@@ -314,11 +331,18 @@ export const compilePageMdx = async (page, pagesContext, options = {}) => {
|
|
|
314
331
|
pagesContext,
|
|
315
332
|
lazyPagesTree
|
|
316
333
|
})
|
|
317
|
-
const mdxModule =
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
const mdxModule = compiled?.code
|
|
335
|
+
? await runMdxSource({
|
|
336
|
+
code: compiled.code,
|
|
337
|
+
path: page.path,
|
|
338
|
+
ctx: activeCtx,
|
|
339
|
+
development: compiled.development
|
|
340
|
+
})
|
|
341
|
+
: await compileMdx({
|
|
342
|
+
content: page.content,
|
|
343
|
+
path: page.path,
|
|
344
|
+
ctx: activeCtx
|
|
345
|
+
})
|
|
322
346
|
page.mdxComponent = mdxModule.default
|
|
323
347
|
page.toc = mdxModule.toc
|
|
324
348
|
const shouldUseTocTitle = page.frontmatter?.title == null
|
|
@@ -346,41 +370,46 @@ export const renderHtml = async ({ routePath, path, components, pagesContext, pa
|
|
|
346
370
|
pageMeta,
|
|
347
371
|
pagesContext
|
|
348
372
|
})
|
|
349
|
-
await compilePageMdx(pageMeta, pagesContext, { ctx })
|
|
350
373
|
|
|
351
|
-
|
|
352
|
-
const ExtraHead = () => {
|
|
353
|
-
return [
|
|
354
|
-
resolveRewindInject(),
|
|
355
|
-
...resolveUserHeadAssets(),
|
|
356
|
-
...resolvePageHeadAssets(pageMeta),
|
|
357
|
-
Outlet(),
|
|
358
|
-
RWND_FALLBACK
|
|
359
|
-
]
|
|
360
|
-
}
|
|
374
|
+
await compilePageMdx(pageMeta, pagesContext, { ctx })
|
|
361
375
|
|
|
376
|
+
const template = state.USER_THEME.template
|
|
362
377
|
const mdxComponent = pageMeta.mdxComponent
|
|
363
378
|
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
...
|
|
370
|
-
...
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
379
|
+
const renderResult = await new Promise((resolve, reject) => {
|
|
380
|
+
const [Head, Outlet] = createPortal()
|
|
381
|
+
const ExtraHead = () => {
|
|
382
|
+
return [
|
|
383
|
+
resolveRewindInject(),
|
|
384
|
+
...resolveUserHeadAssets(),
|
|
385
|
+
...resolvePageHeadAssets(pageMeta),
|
|
386
|
+
Outlet(),
|
|
387
|
+
RWND_FALLBACK
|
|
388
|
+
]
|
|
389
|
+
}
|
|
375
390
|
|
|
376
|
-
|
|
391
|
+
const PageContent = ({ components: extraComponents, ...props }, ...children) => {
|
|
392
|
+
try {
|
|
393
|
+
return mdxComponent({
|
|
394
|
+
children,
|
|
395
|
+
...props,
|
|
396
|
+
components: {
|
|
397
|
+
...components,
|
|
398
|
+
...extraComponents,
|
|
399
|
+
head: Head,
|
|
400
|
+
Head
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
} catch (e) {
|
|
404
|
+
reject(e)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
377
407
|
|
|
378
|
-
const renderResult = await new Promise((r) => {
|
|
379
408
|
const result = HTMLRenderer.c(
|
|
380
409
|
Suspense,
|
|
381
410
|
{
|
|
382
411
|
onLoad() {
|
|
383
|
-
nextTick(() =>
|
|
412
|
+
nextTick(() => resolve(result))
|
|
384
413
|
}
|
|
385
414
|
},
|
|
386
415
|
() =>
|