methanol 0.0.15 → 0.0.16
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/package.json +1 -1
- package/src/client/virtual-module/assets.js +7 -6
- package/src/dev-server.js +51 -17
- package/src/error-page.jsx +49 -0
- package/src/mdx.js +235 -8
- package/src/vite-plugins.js +7 -31
- package/themes/blog/src/layout-categories.jsx +0 -1
- package/themes/blog/src/layout-collections.jsx +0 -1
- package/themes/blog/src/layout-home.jsx +0 -1
- package/themes/blog/src/layout-post.jsx +0 -2
- package/themes/blog/src/page.jsx +1 -1
- package/themes/default/src/page.jsx +1 -1
package/package.json
CHANGED
|
@@ -21,14 +21,15 @@
|
|
|
21
21
|
import { readFileSync } from 'fs'
|
|
22
22
|
import { fileURLToPath } from 'url'
|
|
23
23
|
import { dirname, resolve } from 'path'
|
|
24
|
-
import { cached } from '../../utils.js'
|
|
24
|
+
import { cached, cachedStr } from '../../utils.js'
|
|
25
25
|
|
|
26
26
|
const __filename = fileURLToPath(import.meta.url)
|
|
27
27
|
const __dirname = dirname(__filename)
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
export const virtualModuleDir = __dirname
|
|
30
|
+
export const createStaticResource = (filePath) => cached(() => readFileSync(resolve(__dirname, filePath), 'utf-8'))
|
|
30
31
|
|
|
31
|
-
export const INJECT_SCRIPT =
|
|
32
|
-
export const LOADER_SCRIPT =
|
|
33
|
-
export const PAGEFIND_LOADER_SCRIPT =
|
|
34
|
-
export const PWA_INJECT_SCRIPT =
|
|
32
|
+
export const INJECT_SCRIPT = createStaticResource('./inject.js')
|
|
33
|
+
export const LOADER_SCRIPT = createStaticResource('./loader.js')
|
|
34
|
+
export const PAGEFIND_LOADER_SCRIPT = createStaticResource('./pagefind-loader.js')
|
|
35
|
+
export const PWA_INJECT_SCRIPT = createStaticResource('./pwa-inject.js')
|
package/src/dev-server.js
CHANGED
|
@@ -38,13 +38,16 @@ import {
|
|
|
38
38
|
} from './components.js'
|
|
39
39
|
import { buildPagesContext, buildPageEntry, routePathFromFile } from './pages.js'
|
|
40
40
|
import { compilePageMdx, renderHtml } from './mdx.js'
|
|
41
|
+
import { DevErrorPage } from './error-page.jsx'
|
|
42
|
+
import { HTMLRenderer } from './renderer.js'
|
|
41
43
|
import { methanolResolverPlugin } from './vite-plugins.js'
|
|
42
44
|
import { preparePublicAssets, updateAsset } from './public-assets.js'
|
|
43
45
|
import { createBuildWorkers, runWorkerStage, terminateWorkers } from './workers/build-pool.js'
|
|
46
|
+
import { virtualModuleDir } from './client/virtual-module/assets.js'
|
|
44
47
|
import { style } from './logger.js'
|
|
45
48
|
|
|
46
49
|
export const runViteDev = async () => {
|
|
47
|
-
const baseFsAllow = [state.ROOT_DIR, state.USER_THEME.root].filter(Boolean)
|
|
50
|
+
const baseFsAllow = [virtualModuleDir, state.ROOT_DIR, state.USER_THEME.root].filter(Boolean)
|
|
48
51
|
if (state.MERGED_ASSETS_DIR) {
|
|
49
52
|
baseFsAllow.push(state.MERGED_ASSETS_DIR)
|
|
50
53
|
}
|
|
@@ -160,6 +163,29 @@ export const runViteDev = async () => {
|
|
|
160
163
|
console.error(style.red(`\n[methanol] ${phase} error in ${target}`))
|
|
161
164
|
console.error(error?.stack || error)
|
|
162
165
|
}
|
|
166
|
+
const formatDevError = (error) => {
|
|
167
|
+
if (!error) return 'Unknown error'
|
|
168
|
+
if (typeof error === 'string') return error
|
|
169
|
+
if (error?.stack) return error.stack
|
|
170
|
+
if (error?.message) return error.message
|
|
171
|
+
return String(error)
|
|
172
|
+
}
|
|
173
|
+
const sendDevError = async (res, error, url = '/') => {
|
|
174
|
+
const message = formatDevError(error)
|
|
175
|
+
const basePrefix = devBasePrefix || ''
|
|
176
|
+
const rawHtml = HTMLRenderer.serialize(
|
|
177
|
+
DevErrorPage({ message, basePrefix })(HTMLRenderer)
|
|
178
|
+
)
|
|
179
|
+
let html = rawHtml
|
|
180
|
+
try {
|
|
181
|
+
html = await server.transformIndexHtml(url, rawHtml)
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error(err)
|
|
184
|
+
}
|
|
185
|
+
res.statusCode = 500
|
|
186
|
+
res.setHeader('Content-Type', 'text/html')
|
|
187
|
+
res.end(html)
|
|
188
|
+
}
|
|
163
189
|
|
|
164
190
|
const _invalidate = (id) => {
|
|
165
191
|
const _module = server.moduleGraph.getModuleById(id)
|
|
@@ -167,20 +193,24 @@ export const runViteDev = async () => {
|
|
|
167
193
|
server.moduleGraph.invalidateModule(_module)
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
|
-
const
|
|
171
|
-
_invalidate('\0/.methanol_virtual_module/registry.js')
|
|
196
|
+
const invalidateReframeInject = () => {
|
|
172
197
|
_invalidate('\0methanol:registry')
|
|
173
|
-
_invalidate('\0/.methanol_virtual_module/inject.js')
|
|
174
198
|
_invalidate('\0methanol:inject')
|
|
199
|
+
_invalidate(resolve(virtualModuleDir, 'inject.js'))
|
|
175
200
|
}
|
|
176
201
|
const invalidatePagesIndex = () => {
|
|
177
|
-
_invalidate('\0/.methanol_virtual_module/pages.js')
|
|
178
202
|
_invalidate('\0methanol:pages')
|
|
179
203
|
}
|
|
180
204
|
|
|
181
205
|
const refreshPagesContext = async () => {
|
|
182
206
|
setPagesContext(await buildPagesContext({ compileAll: false }))
|
|
183
207
|
}
|
|
208
|
+
const resolveStaticCandidate = (baseDir, pathname) => {
|
|
209
|
+
if (!baseDir) return null
|
|
210
|
+
const target = resolve(baseDir, pathname.replace(/^\//, ''))
|
|
211
|
+
if (!target.startsWith(baseDir)) return null
|
|
212
|
+
return target
|
|
213
|
+
}
|
|
184
214
|
|
|
185
215
|
const prebuildHtmlCache = async (token) => {
|
|
186
216
|
if (!pagesContext || token !== pagesContextToken) return
|
|
@@ -402,7 +432,14 @@ export const runViteDev = async () => {
|
|
|
402
432
|
const requestedPath = routePath
|
|
403
433
|
if (pathname.includes('.') && !pathname.endsWith('.html')) {
|
|
404
434
|
if (!pagesContext?.pagesByRoute?.has(requestedPath)) {
|
|
405
|
-
|
|
435
|
+
const pageCandidate = resolveStaticCandidate(state.PAGES_DIR, pathname)
|
|
436
|
+
if (pageCandidate && existsSync(pageCandidate)) {
|
|
437
|
+
return next()
|
|
438
|
+
}
|
|
439
|
+
const staticCandidate = resolveStaticCandidate(state.STATIC_DIR, pathname)
|
|
440
|
+
if (staticCandidate && existsSync(staticCandidate)) {
|
|
441
|
+
return next()
|
|
442
|
+
}
|
|
406
443
|
}
|
|
407
444
|
}
|
|
408
445
|
const isExcludedPath = () => {
|
|
@@ -448,8 +485,7 @@ export const runViteDev = async () => {
|
|
|
448
485
|
return
|
|
449
486
|
} catch (err) {
|
|
450
487
|
console.error(err)
|
|
451
|
-
res
|
|
452
|
-
res.end('Internal Server Error')
|
|
488
|
+
await sendDevError(res, err, req.url)
|
|
453
489
|
return
|
|
454
490
|
}
|
|
455
491
|
}
|
|
@@ -506,8 +542,7 @@ export const runViteDev = async () => {
|
|
|
506
542
|
})
|
|
507
543
|
} catch (err) {
|
|
508
544
|
logMdxError('MDX render', err, pageMeta || { path, routePath: renderRoutePath })
|
|
509
|
-
res
|
|
510
|
-
res.end('Internal Server Error')
|
|
545
|
+
await sendDevError(res, err, req.url)
|
|
511
546
|
return
|
|
512
547
|
}
|
|
513
548
|
if (renderEpoch === htmlCacheEpoch) {
|
|
@@ -523,8 +558,7 @@ export const runViteDev = async () => {
|
|
|
523
558
|
res.end(html)
|
|
524
559
|
} catch (err) {
|
|
525
560
|
logMdxError('MDX render', err, pageMeta || { path, routePath: renderRoutePath })
|
|
526
|
-
res
|
|
527
|
-
res.end('Internal Server Error')
|
|
561
|
+
await sendDevError(res, err, req.url)
|
|
528
562
|
}
|
|
529
563
|
}
|
|
530
564
|
|
|
@@ -748,7 +782,7 @@ export const runViteDev = async () => {
|
|
|
748
782
|
enqueue(async () => {
|
|
749
783
|
const { hasClient } = await updateComponentEntry(path)
|
|
750
784
|
if (hasClient) {
|
|
751
|
-
|
|
785
|
+
invalidateReframeInject()
|
|
752
786
|
}
|
|
753
787
|
invalidateHtmlCache()
|
|
754
788
|
reload()
|
|
@@ -759,7 +793,7 @@ export const runViteDev = async () => {
|
|
|
759
793
|
const { hasClient } = await updateComponentEntry(path)
|
|
760
794
|
invalidateHtmlCache()
|
|
761
795
|
if (hasClient) {
|
|
762
|
-
|
|
796
|
+
invalidateReframeInject()
|
|
763
797
|
}
|
|
764
798
|
reload()
|
|
765
799
|
})
|
|
@@ -780,7 +814,7 @@ export const runViteDev = async () => {
|
|
|
780
814
|
const { hasClient } = await updateComponentEntry(path)
|
|
781
815
|
invalidateHtmlCache()
|
|
782
816
|
if (hasClient) {
|
|
783
|
-
|
|
817
|
+
invalidateReframeInject()
|
|
784
818
|
}
|
|
785
819
|
reload()
|
|
786
820
|
})
|
|
@@ -791,7 +825,7 @@ export const runViteDev = async () => {
|
|
|
791
825
|
if (isClientComponent(path)) {
|
|
792
826
|
enqueue(async () => {
|
|
793
827
|
await updateComponentEntry(path, { fallback: true })
|
|
794
|
-
|
|
828
|
+
invalidateReframeInject()
|
|
795
829
|
invalidateHtmlCache()
|
|
796
830
|
reload()
|
|
797
831
|
})
|
|
@@ -808,7 +842,7 @@ export const runViteDev = async () => {
|
|
|
808
842
|
})
|
|
809
843
|
invalidateHtmlCache()
|
|
810
844
|
if (hasClient) {
|
|
811
|
-
|
|
845
|
+
invalidateReframeInject()
|
|
812
846
|
}
|
|
813
847
|
reload()
|
|
814
848
|
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* Copyright Yukino Song, SudoMaker Ltd.
|
|
2
|
+
*
|
|
3
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
4
|
+
* or more contributor license agreements. See the NOTICE file
|
|
5
|
+
* distributed with this work for additional information
|
|
6
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
7
|
+
* to you under the Apache License, Version 2.0 (the
|
|
8
|
+
* "License"); you may not use this file except in compliance
|
|
9
|
+
* with the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing,
|
|
14
|
+
* software distributed under the License is distributed on an
|
|
15
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
* KIND, either express or implied. See the License for the
|
|
17
|
+
* specific language governing permissions and limitations
|
|
18
|
+
* under the License.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { HTMLRenderer as R } from './renderer.js'
|
|
22
|
+
|
|
23
|
+
export const DevErrorPage = ({ message = '', basePrefix = ''} = {}) => (
|
|
24
|
+
<>
|
|
25
|
+
{R.rawHTML`<!doctype html>`}
|
|
26
|
+
<html lang="en">
|
|
27
|
+
<head>
|
|
28
|
+
<meta charset="UTF-8" />
|
|
29
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
30
|
+
<title>Methanol dev error</title>
|
|
31
|
+
<style>{`
|
|
32
|
+
body { margin: 0; font-family: ui-sans-serif, system-ui, sans-serif; background: #0f1115; color: #e9edf1; }
|
|
33
|
+
.main { padding: 24px; max-width: 960px; }
|
|
34
|
+
h1 { margin: 0 0 12px; font-size: 20px; }
|
|
35
|
+
pre { white-space: pre-wrap; background: #151922; padding: 16px; border-radius: 8px; border: 1px solid #2a2f3a; }
|
|
36
|
+
.note { color: #9aa3ad; font-size: 12px; margin-top: 12px; }
|
|
37
|
+
`}</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<div class="main">
|
|
41
|
+
<h1>Dev server error</h1>
|
|
42
|
+
<pre>{message}</pre>
|
|
43
|
+
<div class="note">Fix the error and save to reload.</div>
|
|
44
|
+
</div>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
47
|
+
</>
|
|
48
|
+
)
|
|
49
|
+
|
package/src/mdx.js
CHANGED
|
@@ -25,6 +25,7 @@ import rehypeSlug from 'rehype-slug'
|
|
|
25
25
|
import extractToc from '@stefanprobst/rehype-extract-toc'
|
|
26
26
|
import withTocExport from '@stefanprobst/rehype-extract-toc/mdx'
|
|
27
27
|
import rehypeStarryNight from 'rehype-starry-night'
|
|
28
|
+
import { createStarryNight } from '@wooorm/starry-night'
|
|
28
29
|
import remarkGfm from 'remark-gfm'
|
|
29
30
|
import { HTMLRenderer } from './renderer.js'
|
|
30
31
|
import { signal, computed, read, Suspense, nextTick } from 'refui'
|
|
@@ -36,10 +37,12 @@ import { state } from './state.js'
|
|
|
36
37
|
import { resolveUserMdxConfig, withBase } from './config.js'
|
|
37
38
|
import { methanolCtx } from './rehype-plugins/methanol-ctx.js'
|
|
38
39
|
import { linkResolve } from './rehype-plugins/link-resolve.js'
|
|
40
|
+
import { cached } from './utils.js'
|
|
39
41
|
|
|
40
42
|
// Workaround for Vite: it doesn't support resolving module/virtual modules in script src in dev mode
|
|
41
|
-
const resolveRewindInject = () =>
|
|
43
|
+
const resolveRewindInject = cached(() =>
|
|
42
44
|
HTMLRenderer.rawHTML(`<script type="module" src="${withBase('/.methanol_virtual_module/inject.js')}"></script>`)
|
|
45
|
+
)
|
|
43
46
|
const RWND_FALLBACK = HTMLRenderer.rawHTML(
|
|
44
47
|
'<script>if(!window.$$rfrm){var l=[];var r=function(k,i,p){l.push([k,i,p,document.currentScript])};r.$$loaded=l;window.$$rfrm=r}</script>'
|
|
45
48
|
)
|
|
@@ -219,16 +222,228 @@ const normalizeStarryNightConfig = (value) => {
|
|
|
219
222
|
const resolveStarryNightForPage = (frontmatter) => {
|
|
220
223
|
const base = {
|
|
221
224
|
enabled: state.STARRY_NIGHT_ENABLED === true,
|
|
222
|
-
options: state.STARRY_NIGHT_OPTIONS || null
|
|
225
|
+
options: state.STARRY_NIGHT_OPTIONS || null,
|
|
226
|
+
explicit: false
|
|
223
227
|
}
|
|
224
228
|
if (!frontmatter || !Object.prototype.hasOwnProperty.call(frontmatter, 'starryNight')) {
|
|
225
229
|
return base
|
|
226
230
|
}
|
|
227
|
-
const
|
|
231
|
+
const overrideValue = frontmatter.starryNight
|
|
232
|
+
if (typeof overrideValue === 'boolean') {
|
|
233
|
+
if (overrideValue === false) {
|
|
234
|
+
return { enabled: false, options: null, explicit: true }
|
|
235
|
+
}
|
|
236
|
+
return { enabled: true, options: base.options, explicit: false }
|
|
237
|
+
}
|
|
238
|
+
if (!overrideValue || typeof overrideValue !== 'object') {
|
|
239
|
+
return base
|
|
240
|
+
}
|
|
241
|
+
const override = normalizeStarryNightConfig(overrideValue)
|
|
228
242
|
if (!override) return base
|
|
229
|
-
if (override.enabled === false) return { enabled: false, options: null }
|
|
243
|
+
if (override.enabled === false) return { enabled: false, options: null, explicit: true }
|
|
230
244
|
const options = override.options != null ? override.options : base.options
|
|
231
|
-
return { enabled: true, options }
|
|
245
|
+
return { enabled: true, options, explicit: true }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const CODE_FENCE_LANG_PATTERN = /(^|\n)\s*(```|~~~)\s*([^\s{]+)/g
|
|
249
|
+
const extractCodeFenceLanguages = (value) => {
|
|
250
|
+
const text = String(value || '')
|
|
251
|
+
const languages = new Set()
|
|
252
|
+
CODE_FENCE_LANG_PATTERN.lastIndex = 0
|
|
253
|
+
let match
|
|
254
|
+
while ((match = CODE_FENCE_LANG_PATTERN.exec(text))) {
|
|
255
|
+
let lang = match[3] ? String(match[3]).trim() : ''
|
|
256
|
+
if (!lang) continue
|
|
257
|
+
const braceIndex = lang.indexOf('{')
|
|
258
|
+
if (braceIndex >= 0) {
|
|
259
|
+
lang = lang.slice(0, braceIndex).trim()
|
|
260
|
+
}
|
|
261
|
+
if (!lang || !/[A-Za-z]/.test(lang)) continue
|
|
262
|
+
languages.add(lang.toLowerCase())
|
|
263
|
+
}
|
|
264
|
+
return languages
|
|
265
|
+
}
|
|
266
|
+
const cleanStarryOptions = (options) => {
|
|
267
|
+
if (!options || typeof options !== 'object') return undefined
|
|
268
|
+
const next = { ...options }
|
|
269
|
+
delete next.grammars
|
|
270
|
+
return next
|
|
271
|
+
}
|
|
272
|
+
let starryNightFuture = null
|
|
273
|
+
const resolvedGrammarCache = new Map()
|
|
274
|
+
const resolvedCustomGrammarCache = new Map()
|
|
275
|
+
const failedLanguageCache = new Set()
|
|
276
|
+
const failedCustomLanguageCache = new Map()
|
|
277
|
+
const loadStarryNight = async (options) => {
|
|
278
|
+
if (!starryNightFuture) {
|
|
279
|
+
starryNightFuture = import('@wooorm/starry-night').then(async (mod) => {
|
|
280
|
+
const grammars = Array.isArray(mod?.all) ? mod.all : []
|
|
281
|
+
const starryNight = await createStarryNight(grammars, cleanStarryOptions(options))
|
|
282
|
+
return {
|
|
283
|
+
starryNight,
|
|
284
|
+
grammars,
|
|
285
|
+
scopeMap: buildScopeGrammarMap(grammars)
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
return starryNightFuture
|
|
290
|
+
}
|
|
291
|
+
const buildScopeGrammarMap = (grammars) => {
|
|
292
|
+
const scopeMap = new Map()
|
|
293
|
+
for (const grammar of grammars || []) {
|
|
294
|
+
const scopeName = grammar?.scopeName
|
|
295
|
+
if (typeof scopeName === 'string' && scopeName) {
|
|
296
|
+
scopeMap.set(scopeName, grammar)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return scopeMap
|
|
300
|
+
}
|
|
301
|
+
const collectExternalScopes = (grammar) => {
|
|
302
|
+
const scopes = new Set()
|
|
303
|
+
const addScope = (value) => {
|
|
304
|
+
if (typeof value !== 'string') return
|
|
305
|
+
if (!value || value.startsWith('#')) return
|
|
306
|
+
scopes.add(value)
|
|
307
|
+
}
|
|
308
|
+
const visit = (node) => {
|
|
309
|
+
if (!node) return
|
|
310
|
+
if (Array.isArray(node)) {
|
|
311
|
+
for (const item of node) {
|
|
312
|
+
visit(item)
|
|
313
|
+
}
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
if (typeof node !== 'object') return
|
|
317
|
+
if (typeof node.include === 'string') {
|
|
318
|
+
addScope(node.include)
|
|
319
|
+
}
|
|
320
|
+
for (const value of Object.values(node)) {
|
|
321
|
+
visit(value)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (Array.isArray(grammar?.dependencies)) {
|
|
325
|
+
for (const dep of grammar.dependencies) {
|
|
326
|
+
addScope(dep)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
visit(grammar?.patterns)
|
|
330
|
+
visit(grammar?.repository)
|
|
331
|
+
return scopes
|
|
332
|
+
}
|
|
333
|
+
const resolveStarryNightGrammars = async (languages, options) => {
|
|
334
|
+
const hasCustomGrammars = Array.isArray(options?.grammars)
|
|
335
|
+
const baseGrammars = hasCustomGrammars ? options.grammars : null
|
|
336
|
+
if (hasCustomGrammars && (!baseGrammars || !baseGrammars.length)) return null
|
|
337
|
+
const getCustomKey = (grammars) =>
|
|
338
|
+
(grammars || [])
|
|
339
|
+
.map((grammar) => grammar?.scopeName)
|
|
340
|
+
.filter(Boolean)
|
|
341
|
+
.sort()
|
|
342
|
+
.join('|')
|
|
343
|
+
const failedSet = hasCustomGrammars
|
|
344
|
+
? (() => {
|
|
345
|
+
const customKey = getCustomKey(baseGrammars)
|
|
346
|
+
let set = failedCustomLanguageCache.get(customKey)
|
|
347
|
+
if (!set) {
|
|
348
|
+
set = new Set()
|
|
349
|
+
failedCustomLanguageCache.set(customKey, set)
|
|
350
|
+
}
|
|
351
|
+
return set
|
|
352
|
+
})()
|
|
353
|
+
: failedLanguageCache
|
|
354
|
+
const filteredLanguages = Array.from(languages || [])
|
|
355
|
+
.map((lang) => String(lang).toLowerCase())
|
|
356
|
+
.filter((lang) => lang && !failedSet.has(lang))
|
|
357
|
+
const languageKey = filteredLanguages.slice().sort().join('|')
|
|
358
|
+
if (!languageKey) return []
|
|
359
|
+
if (hasCustomGrammars) {
|
|
360
|
+
const customKey = getCustomKey(baseGrammars)
|
|
361
|
+
const customCache = resolvedCustomGrammarCache.get(customKey)
|
|
362
|
+
if (customCache?.has(languageKey)) {
|
|
363
|
+
return customCache.get(languageKey)
|
|
364
|
+
}
|
|
365
|
+
} else if (resolvedGrammarCache.has(languageKey)) {
|
|
366
|
+
return resolvedGrammarCache.get(languageKey)
|
|
367
|
+
}
|
|
368
|
+
const selected = new Set()
|
|
369
|
+
const selectedScopes = new Set()
|
|
370
|
+
let scopeMap = baseGrammars ? buildScopeGrammarMap(baseGrammars) : null
|
|
371
|
+
let loadedStarryNight = null
|
|
372
|
+
const ensureAll = async () => {
|
|
373
|
+
if (!loadedStarryNight && !hasCustomGrammars) {
|
|
374
|
+
loadedStarryNight = await loadStarryNight(options)
|
|
375
|
+
if (!scopeMap) {
|
|
376
|
+
scopeMap = loadedStarryNight?.scopeMap || null
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
let flagToScope = null
|
|
381
|
+
if (hasCustomGrammars) {
|
|
382
|
+
const customStarryNight = await createStarryNight(baseGrammars, cleanStarryOptions(options))
|
|
383
|
+
flagToScope = (lang) => customStarryNight.flagToScope(String(lang))
|
|
384
|
+
} else {
|
|
385
|
+
try {
|
|
386
|
+
await ensureAll()
|
|
387
|
+
} catch {
|
|
388
|
+
for (const lang of filteredLanguages) {
|
|
389
|
+
failedSet.add(lang)
|
|
390
|
+
}
|
|
391
|
+
return []
|
|
392
|
+
}
|
|
393
|
+
flagToScope = (lang) => loadedStarryNight?.starryNight.flagToScope(String(lang))
|
|
394
|
+
}
|
|
395
|
+
if (!scopeMap) return null
|
|
396
|
+
const addGrammar = (grammar) => {
|
|
397
|
+
if (!grammar) return false
|
|
398
|
+
const scopeName = grammar.scopeName
|
|
399
|
+
if (!scopeName || selectedScopes.has(scopeName)) return false
|
|
400
|
+
selectedScopes.add(scopeName)
|
|
401
|
+
selected.add(grammar)
|
|
402
|
+
return true
|
|
403
|
+
}
|
|
404
|
+
for (const lang of filteredLanguages) {
|
|
405
|
+
let scope
|
|
406
|
+
try {
|
|
407
|
+
scope = flagToScope ? flagToScope(String(lang)) : undefined
|
|
408
|
+
} catch {
|
|
409
|
+
scope = undefined
|
|
410
|
+
}
|
|
411
|
+
const grammar = scope && scopeMap ? scopeMap.get(scope) : null
|
|
412
|
+
if (!grammar) {
|
|
413
|
+
failedSet.add(lang)
|
|
414
|
+
}
|
|
415
|
+
addGrammar(grammar)
|
|
416
|
+
}
|
|
417
|
+
if (!selected.size) return []
|
|
418
|
+
const queue = Array.from(selected)
|
|
419
|
+
while (queue.length) {
|
|
420
|
+
const grammar = queue.pop()
|
|
421
|
+
const scopes = collectExternalScopes(grammar)
|
|
422
|
+
for (const scope of scopes) {
|
|
423
|
+
if (selectedScopes.has(scope)) continue
|
|
424
|
+
let depGrammar = scopeMap ? scopeMap.get(scope) : null
|
|
425
|
+
if (!depGrammar && !hasCustomGrammars) {
|
|
426
|
+
await ensureAll()
|
|
427
|
+
depGrammar = scopeMap ? scopeMap.get(scope) : null
|
|
428
|
+
}
|
|
429
|
+
if (addGrammar(depGrammar)) {
|
|
430
|
+
queue.push(depGrammar)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const result = selected.size ? Array.from(selected) : []
|
|
435
|
+
if (hasCustomGrammars) {
|
|
436
|
+
const customKey = getCustomKey(baseGrammars)
|
|
437
|
+
let customCache = resolvedCustomGrammarCache.get(customKey)
|
|
438
|
+
if (!customCache) {
|
|
439
|
+
customCache = new Map()
|
|
440
|
+
resolvedCustomGrammarCache.set(customKey, customCache)
|
|
441
|
+
}
|
|
442
|
+
customCache.set(languageKey, result)
|
|
443
|
+
} else {
|
|
444
|
+
resolvedGrammarCache.set(languageKey, result)
|
|
445
|
+
}
|
|
446
|
+
return result
|
|
232
447
|
}
|
|
233
448
|
|
|
234
449
|
const resolveBaseMdxConfig = async () => {
|
|
@@ -262,7 +477,7 @@ const resolveBaseMdxConfig = async () => {
|
|
|
262
477
|
return (cachedMdxConfig = mdxConfig)
|
|
263
478
|
}
|
|
264
479
|
|
|
265
|
-
const resolveMdxConfigForPage = async (frontmatter) => {
|
|
480
|
+
const resolveMdxConfigForPage = async (frontmatter, content = '') => {
|
|
266
481
|
const baseConfig = await resolveBaseMdxConfig()
|
|
267
482
|
const mdxConfig = {
|
|
268
483
|
...baseConfig,
|
|
@@ -270,7 +485,19 @@ const resolveMdxConfigForPage = async (frontmatter) => {
|
|
|
270
485
|
}
|
|
271
486
|
const starryNightConfig = resolveStarryNightForPage(frontmatter)
|
|
272
487
|
if (!starryNightConfig.enabled) return mdxConfig
|
|
273
|
-
|
|
488
|
+
let options = starryNightConfig.options
|
|
489
|
+
if (!starryNightConfig.explicit) {
|
|
490
|
+
const languages = extractCodeFenceLanguages(content)
|
|
491
|
+
if (!languages.size) {
|
|
492
|
+
return mdxConfig
|
|
493
|
+
}
|
|
494
|
+
const grammars = await resolveStarryNightGrammars(languages, options)
|
|
495
|
+
if (!grammars || !grammars.length) {
|
|
496
|
+
return mdxConfig
|
|
497
|
+
}
|
|
498
|
+
options = { ...(options || {}), grammars }
|
|
499
|
+
}
|
|
500
|
+
const plugin = options ? [rehypeStarryNight, options] : [rehypeStarryNight]
|
|
274
501
|
const insertIndex = mdxConfig.rehypePlugins.indexOf(linkResolve)
|
|
275
502
|
if (insertIndex >= 0) {
|
|
276
503
|
mdxConfig.rehypePlugins.splice(insertIndex, 0, plugin)
|
|
@@ -281,7 +508,7 @@ const resolveMdxConfigForPage = async (frontmatter) => {
|
|
|
281
508
|
}
|
|
282
509
|
|
|
283
510
|
export const compileMdxSource = async ({ content, path, frontmatter }) => {
|
|
284
|
-
const mdxConfig = await resolveMdxConfigForPage(frontmatter)
|
|
511
|
+
const mdxConfig = await resolveMdxConfigForPage(frontmatter, content)
|
|
285
512
|
const compiled = await compile({ value: content, path: path }, mdxConfig)
|
|
286
513
|
const code = String(compiled.value ?? compiled)
|
|
287
514
|
return { code, development: Boolean(mdxConfig.development) }
|
package/src/vite-plugins.js
CHANGED
|
@@ -27,7 +27,7 @@ import { state } from './state.js'
|
|
|
27
27
|
import { resolveBasePrefix } from './config.js'
|
|
28
28
|
import { genRegistryScript } from './components.js'
|
|
29
29
|
import { serializePagesIndex } from './pages-index.js'
|
|
30
|
-
import { INJECT_SCRIPT, LOADER_SCRIPT, PAGEFIND_LOADER_SCRIPT, PWA_INJECT_SCRIPT } from './client/virtual-module/assets.js'
|
|
30
|
+
import { virtualModuleDir, INJECT_SCRIPT, LOADER_SCRIPT, PAGEFIND_LOADER_SCRIPT, PWA_INJECT_SCRIPT } from './client/virtual-module/assets.js'
|
|
31
31
|
import { projectRequire } from './node-loader.js'
|
|
32
32
|
|
|
33
33
|
const require = createRequire(import.meta.url)
|
|
@@ -127,13 +127,10 @@ const virtualModuleMap = {
|
|
|
127
127
|
get registry() {
|
|
128
128
|
return `export const registry = ${genRegistryScript()}`
|
|
129
129
|
},
|
|
130
|
-
get 'registry.js'() {
|
|
131
|
-
return `export const registry = ${genRegistryScript()}`
|
|
132
|
-
},
|
|
133
130
|
get loader() {
|
|
134
131
|
return LOADER_SCRIPT()
|
|
135
132
|
},
|
|
136
|
-
get 'inject
|
|
133
|
+
get 'inject'() {
|
|
137
134
|
return INJECT_SCRIPT()
|
|
138
135
|
},
|
|
139
136
|
get 'pagefind-loader'() {
|
|
@@ -149,10 +146,6 @@ const virtualModuleMap = {
|
|
|
149
146
|
get pages() {
|
|
150
147
|
const pages = state.PAGES_CONTEXT?.pages || []
|
|
151
148
|
return `export const pages = ${serializePagesIndex(pages)}\nexport default pages`
|
|
152
|
-
},
|
|
153
|
-
get 'pages.js'() {
|
|
154
|
-
const pages = state.PAGES_CONTEXT?.pages || []
|
|
155
|
-
return `export const pages = ${serializePagesIndex(pages)}\nexport default pages`
|
|
156
149
|
}
|
|
157
150
|
}
|
|
158
151
|
|
|
@@ -165,20 +158,6 @@ const getSchemeModuleKey = (id) => {
|
|
|
165
158
|
return id.slice(virtualModuleScheme.length)
|
|
166
159
|
}
|
|
167
160
|
|
|
168
|
-
const resolveVirtualModuleId = (id) => {
|
|
169
|
-
if (id.startsWith(virtualModulePrefix)) {
|
|
170
|
-
return id
|
|
171
|
-
}
|
|
172
|
-
const basePrefix = resolveBasePrefix()
|
|
173
|
-
if (basePrefix) {
|
|
174
|
-
const prefixed = `${basePrefix}${virtualModulePrefix}`
|
|
175
|
-
if (id.startsWith(prefixed)) {
|
|
176
|
-
return id.slice(basePrefix.length)
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return null
|
|
180
|
-
}
|
|
181
|
-
|
|
182
161
|
export const methanolResolverPlugin = () => {
|
|
183
162
|
return {
|
|
184
163
|
name: 'methanol-resolver',
|
|
@@ -195,19 +174,16 @@ export const methanolResolverPlugin = () => {
|
|
|
195
174
|
return require.resolve(id)
|
|
196
175
|
}
|
|
197
176
|
|
|
177
|
+
// Very weird workaround for Vite
|
|
178
|
+
if (id.startsWith(virtualModulePrefix)) {
|
|
179
|
+
return resolve(virtualModuleDir, id.slice(virtualModulePrefix.length))
|
|
180
|
+
}
|
|
181
|
+
|
|
198
182
|
const schemeKey = getSchemeModuleKey(id)
|
|
199
183
|
if (schemeKey && Object.prototype.hasOwnProperty.call(virtualModuleMap, schemeKey)) {
|
|
200
184
|
return '\0' + id
|
|
201
185
|
}
|
|
202
186
|
|
|
203
|
-
const virtualId = resolveVirtualModuleId(id)
|
|
204
|
-
if (virtualId) {
|
|
205
|
-
const _moduleId = getModuleIdSegment(virtualId, virtualModulePrefix.length)
|
|
206
|
-
if (Object.prototype.hasOwnProperty.call(virtualModuleMap, _moduleId)) {
|
|
207
|
-
return '\0' + virtualId
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
187
|
if (state.SOURCES.length) {
|
|
212
188
|
const { pathname, search } = new URL(id, 'http://methanol')
|
|
213
189
|
for (const entry of state.SOURCES) {
|
package/themes/blog/src/page.jsx
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* under the License.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { DOCTYPE_HTML } from 'methanol'
|
|
22
22
|
import { LayoutHome } from './layout-home.jsx'
|
|
23
23
|
import { LayoutCategories } from './layout-categories.jsx'
|
|
24
24
|
import { LayoutCollections } from './layout-collections.jsx'
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* under the License.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { DOCTYPE_HTML } from 'methanol'
|
|
22
22
|
import { renderToc } from '../components/ThemeToCContainer.static.jsx'
|
|
23
23
|
import { renderNavTree } from './nav-tree.jsx'
|
|
24
24
|
|