methanol 0.0.17 → 0.0.19
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 -0
- package/package.json +2 -2
- package/src/client/sw.js +7 -21
- package/src/components.js +4 -1
- package/src/mdx.js +2 -0
- package/src/reframe.js +8 -2
- package/themes/README.md +52 -0
- package/themes/blog/README.md +34 -14
- package/themes/default/README.md +26 -0
- package/themes/default/components/ThemeColorSwitch.client.jsx +13 -0
- package/themes/default/sources/theme-prepare.js +13 -0
- package/themes/default/src/page.jsx +2 -0
package/README.md
CHANGED
|
@@ -64,6 +64,10 @@ export default () => ({
|
|
|
64
64
|
})
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
## Themes
|
|
68
|
+
|
|
69
|
+
Methanol includes built-in themes (`default`, `blog`). Use `--theme <name>` or set `theme: '<name>'` in config. For local themes inside your project, import the theme entry in `methanol.config.*` and pass the theme object/factory.
|
|
70
|
+
|
|
67
71
|
## CLI notes
|
|
68
72
|
|
|
69
73
|
- `methanol preview` is an alias for `methanol serve`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "methanol",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Static site generator powered by rEFui and MDX",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"hast-util-is-element": "^3.0.0",
|
|
37
37
|
"json5": "^2.2.3",
|
|
38
38
|
"null-prototype-object": "^1.2.5",
|
|
39
|
-
"refui": "^0.
|
|
39
|
+
"refui": "^0.17.1",
|
|
40
40
|
"refurbish": "^0.1.8",
|
|
41
41
|
"rehype-slug": "^6.0.0",
|
|
42
42
|
"rehype-starry-night": "^2.2.0",
|
package/src/client/sw.js
CHANGED
|
@@ -139,7 +139,6 @@ self.addEventListener('install', (event) => {
|
|
|
139
139
|
const assetCache = await openCache(ASSETS_CACHE)
|
|
140
140
|
const manifestEntries = getManifestEntries()
|
|
141
141
|
const manifestMap = buildManifestMap(manifestEntries)
|
|
142
|
-
const previousMap = await loadStoredManifestMap()
|
|
143
142
|
const manifestUrls = manifestEntries.map((entry) => entry.url)
|
|
144
143
|
const [prioritized] = prioritizeManifestUrls(manifestUrls)
|
|
145
144
|
const { failedIndex } = await runConcurrentQueue(prioritized, {
|
|
@@ -150,11 +149,9 @@ self.addEventListener('install', (event) => {
|
|
|
150
149
|
const cached = await matchCache(cacheName, url)
|
|
151
150
|
const key = manifestKey(url)
|
|
152
151
|
const currentRevision = manifestMap.get(key) ?? null
|
|
153
|
-
const previousRevision = previousMap.get(key) ?? null
|
|
154
152
|
const shouldFetch = shouldFetchWithRevision({
|
|
155
153
|
cached,
|
|
156
|
-
currentRevision
|
|
157
|
-
previousRevision
|
|
154
|
+
currentRevision
|
|
158
155
|
})
|
|
159
156
|
if (!shouldFetch) return true
|
|
160
157
|
const cache = isHtml ? pageCache : assetCache
|
|
@@ -272,11 +269,12 @@ async function runConcurrentQueue(list, { concurrency, handler, stopOnError = tr
|
|
|
272
269
|
return { ok: failedIndex === null, failedIndex }
|
|
273
270
|
}
|
|
274
271
|
|
|
275
|
-
function shouldFetchWithRevision({ cached, currentRevision
|
|
272
|
+
function shouldFetchWithRevision({ cached, currentRevision }) {
|
|
276
273
|
if (!cached) return true
|
|
277
274
|
if (currentRevision == null) return false
|
|
278
|
-
|
|
279
|
-
|
|
275
|
+
const cachedRevision = cached.headers?.get?.(REVISION_HEADER)
|
|
276
|
+
if (cachedRevision == null) return true
|
|
277
|
+
return cachedRevision !== String(currentRevision)
|
|
280
278
|
}
|
|
281
279
|
|
|
282
280
|
function shouldRevalidateCached(cached, currentRevision) {
|
|
@@ -550,7 +548,6 @@ const DB_STORE = 'kv'
|
|
|
550
548
|
const KEY_INDEX = 'warmIndex'
|
|
551
549
|
const KEY_LEASE = 'warmLease'
|
|
552
550
|
const KEY_FORCE = 'warmForce'
|
|
553
|
-
const KEY_MANIFEST = 'warmManifest'
|
|
554
551
|
|
|
555
552
|
function idbOpen() {
|
|
556
553
|
return new Promise((resolve, reject) => {
|
|
@@ -611,12 +608,6 @@ function buildManifestMap(entries) {
|
|
|
611
608
|
return map
|
|
612
609
|
}
|
|
613
610
|
|
|
614
|
-
async function loadStoredManifestMap() {
|
|
615
|
-
const stored = await idbGet(KEY_MANIFEST)
|
|
616
|
-
if (!stored) return new Map()
|
|
617
|
-
if (Array.isArray(stored)) return buildManifestMap(stored)
|
|
618
|
-
return new Map()
|
|
619
|
-
}
|
|
620
611
|
|
|
621
612
|
async function tryAcquireLease(leaseMs) {
|
|
622
613
|
const leaseId = randomId()
|
|
@@ -667,7 +658,6 @@ async function warmManifestResumable({ force = false } = {}) {
|
|
|
667
658
|
try {
|
|
668
659
|
const manifestEntries = getManifestEntries()
|
|
669
660
|
const manifestMap = buildManifestMap(manifestEntries)
|
|
670
|
-
const previousMap = await loadStoredManifestMap()
|
|
671
661
|
const [, urls] = prioritizeManifestUrls(manifestEntries.map((entry) => entry.url))
|
|
672
662
|
if (!urls.length) return
|
|
673
663
|
if (index >= urls.length) {
|
|
@@ -685,14 +675,12 @@ async function warmManifestResumable({ force = false } = {}) {
|
|
|
685
675
|
const isHtml = abs.endsWith('.html')
|
|
686
676
|
const key = manifestKey(abs)
|
|
687
677
|
const currentRevision = manifestMap.get(key) ?? null
|
|
688
|
-
const previousRevision = previousMap.get(key) ?? null
|
|
689
678
|
|
|
690
679
|
if (isHtml) {
|
|
691
680
|
const cached = await matchCache(PAGES_CACHE, abs)
|
|
692
681
|
const shouldFetch = shouldFetchWithRevision({
|
|
693
682
|
cached,
|
|
694
|
-
currentRevision
|
|
695
|
-
previousRevision
|
|
683
|
+
currentRevision
|
|
696
684
|
})
|
|
697
685
|
if (!shouldFetch) return true
|
|
698
686
|
|
|
@@ -709,8 +697,7 @@ async function warmManifestResumable({ force = false } = {}) {
|
|
|
709
697
|
const cached = await matchCache(ASSETS_CACHE, abs)
|
|
710
698
|
const shouldFetch = shouldFetchWithRevision({
|
|
711
699
|
cached,
|
|
712
|
-
currentRevision
|
|
713
|
-
previousRevision
|
|
700
|
+
currentRevision
|
|
714
701
|
})
|
|
715
702
|
if (!shouldFetch) return true
|
|
716
703
|
|
|
@@ -738,7 +725,6 @@ async function warmManifestResumable({ force = false } = {}) {
|
|
|
738
725
|
completed = true
|
|
739
726
|
} finally {
|
|
740
727
|
if (completed) {
|
|
741
|
-
await idbSet(KEY_MANIFEST, getManifestEntries())
|
|
742
728
|
await idbSet(KEY_FORCE, 0)
|
|
743
729
|
}
|
|
744
730
|
await releaseLease(lease)
|
package/src/components.js
CHANGED
|
@@ -51,6 +51,7 @@ export const reframeEnv = env()
|
|
|
51
51
|
export const register = reframeEnv.register
|
|
52
52
|
export const invalidateRegistryEntry = reframeEnv.invalidate
|
|
53
53
|
export const genRegistryScript = reframeEnv.genRegistryScript
|
|
54
|
+
export const resetReframeRenderCount = () => reframeEnv.resetRenderCount()
|
|
54
55
|
|
|
55
56
|
const resolveComponentExport = (componentPath, exportName, ext) => {
|
|
56
57
|
const staticCandidate = `${componentPath}.static${ext}`
|
|
@@ -75,7 +76,9 @@ const resolveComponentExport = (componentPath, exportName, ext) => {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
if (ret.staticPath) {
|
|
78
|
-
|
|
79
|
+
const baseUrl = pathToFileURL(ret.staticPath).href
|
|
80
|
+
ret.staticImportURL =
|
|
81
|
+
state.CURRENT_MODE === 'production' ? baseUrl : `${baseUrl}?t=${componentImportNonce}`
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
return ret
|
package/src/mdx.js
CHANGED
|
@@ -38,6 +38,7 @@ import { resolveUserMdxConfig, withBase } from './config.js'
|
|
|
38
38
|
import { methanolCtx } from './rehype-plugins/methanol-ctx.js'
|
|
39
39
|
import { linkResolve } from './rehype-plugins/link-resolve.js'
|
|
40
40
|
import { cached } from './utils.js'
|
|
41
|
+
import { resetReframeRenderCount } from './components.js'
|
|
41
42
|
|
|
42
43
|
// Workaround for Vite: it doesn't support resolving module/virtual modules in script src in dev mode
|
|
43
44
|
const resolveRewindInject = cached(() =>
|
|
@@ -585,6 +586,7 @@ export const compilePageMdx = async (page, pagesContext, options = {}) => {
|
|
|
585
586
|
}
|
|
586
587
|
|
|
587
588
|
export const renderHtml = async ({ routePath, path, components, pagesContext, pageMeta }) => {
|
|
589
|
+
resetReframeRenderCount()
|
|
588
590
|
const ctx = buildPageContext({
|
|
589
591
|
routePath,
|
|
590
592
|
path,
|
package/src/reframe.js
CHANGED
|
@@ -61,10 +61,10 @@ export function env(parentEnv) {
|
|
|
61
61
|
} while (keyPathRegistry[key] && keyPathRegistry[key] !== clientPath)
|
|
62
62
|
|
|
63
63
|
const component = async ({ children: childrenProp, ...props }, ...children) => {
|
|
64
|
-
const staticComponent = (await import(staticImportURL)).default
|
|
65
|
-
|
|
66
64
|
const id = renderCount++
|
|
67
65
|
const idStr = id.toString(16)
|
|
66
|
+
|
|
67
|
+
const staticComponent = (await import(staticImportURL)).default
|
|
68
68
|
const script = `$$rfrm(${JSON.stringify(key)},${id},${Object.keys(props).length ? JSON5.stringify(props).replace(/<\/script/ig, '<\\/script') : '{}'})`
|
|
69
69
|
|
|
70
70
|
return (R) => {
|
|
@@ -101,6 +101,11 @@ export function env(parentEnv) {
|
|
|
101
101
|
parent = nextParent || null
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function resetRenderCount() {
|
|
105
|
+
renderCount = 0
|
|
106
|
+
parent?.resetRenderCount()
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
function getMergedRegistry() {
|
|
105
110
|
return Object.assign({}, parent?.registry, registry)
|
|
106
111
|
}
|
|
@@ -117,6 +122,7 @@ export function env(parentEnv) {
|
|
|
117
122
|
invalidate,
|
|
118
123
|
genRegistryScript,
|
|
119
124
|
setParent,
|
|
125
|
+
resetRenderCount,
|
|
120
126
|
get registry() {
|
|
121
127
|
return getMergedRegistry()
|
|
122
128
|
}
|
package/themes/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Themes
|
|
2
|
+
|
|
3
|
+
Methanol ships with built-in themes under `themes/`.
|
|
4
|
+
|
|
5
|
+
## Built-in themes
|
|
6
|
+
|
|
7
|
+
- `default`: Documentation-style theme (sidebar + ToC).
|
|
8
|
+
- `blog`: Blog theme (post list, tags/categories UI).
|
|
9
|
+
|
|
10
|
+
## Using a theme
|
|
11
|
+
|
|
12
|
+
CLI:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
methanol build --theme blog
|
|
16
|
+
methanol dev --theme default
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Config (`methanol.config.*`):
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
export default () => ({
|
|
23
|
+
theme: 'blog'
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Using a local theme (in your project)
|
|
28
|
+
|
|
29
|
+
Theme name resolution only applies when `theme` is a string (built-in or `methanol-theme-xxx` from `node_modules`).
|
|
30
|
+
If your theme lives inside your project, import it and pass the theme object/factory:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import createTheme from './themes/my-theme/index.js'
|
|
34
|
+
|
|
35
|
+
export default () => ({
|
|
36
|
+
theme: createTheme()
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Publishing a theme
|
|
41
|
+
|
|
42
|
+
If you publish a theme as an npm package named `methanol-theme-xxx`, users can enable it via `--theme xxx` or `theme: 'xxx'`.
|
|
43
|
+
|
|
44
|
+
## Theme structure (convention)
|
|
45
|
+
|
|
46
|
+
- `index.js`: entrypoint that exports a theme object or a factory function (recommended).
|
|
47
|
+
- `src/`: theme runtime/template modules (e.g. `src/page.jsx`).
|
|
48
|
+
- `components/`: theme components (used by MDX).
|
|
49
|
+
- `pages/`: theme-provided pages (e.g. `_404.mdx`, `offline.mdx`).
|
|
50
|
+
- `public/`: theme static assets (merged with user `public/`).
|
|
51
|
+
- `sources/`: extra source mappings exposed via `theme.sources`.
|
|
52
|
+
|
package/themes/blog/README.md
CHANGED
|
@@ -1,26 +1,46 @@
|
|
|
1
1
|
# Blog Theme
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A blog theme for Methanol.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
|
-
|
|
7
|
-
- Post list
|
|
8
|
-
-
|
|
9
|
-
-
|
|
6
|
+
|
|
7
|
+
- Post list and post pages
|
|
8
|
+
- Category/collection views (theme-provided client UI)
|
|
9
|
+
- Responsive layout
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
CLI:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
methanol build --theme blog
|
|
17
|
+
methanol dev --theme blog
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Config (`methanol.config.*`):
|
|
14
21
|
|
|
15
22
|
```js
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// ...
|
|
20
|
-
}
|
|
23
|
+
export default () => ({
|
|
24
|
+
theme: 'blog'
|
|
25
|
+
})
|
|
21
26
|
```
|
|
22
27
|
|
|
23
28
|
## Structure
|
|
24
|
-
|
|
25
|
-
- `
|
|
26
|
-
- `pages/`:
|
|
29
|
+
|
|
30
|
+
- `src/page.jsx`: main layout template
|
|
31
|
+
- `pages/`: theme pages (including special pages like `_404.mdx` and `offline.mdx` when present)
|
|
32
|
+
- `components/`: theme components used by MDX
|
|
33
|
+
- `public/`: theme static assets
|
|
34
|
+
- `sources/`: theme source mappings (used by `theme.sources`)
|
|
35
|
+
|
|
36
|
+
## Local development
|
|
37
|
+
|
|
38
|
+
If you want to use the theme from a local folder (instead of built-in name / npm package), import it in config:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import createBlogTheme from './themes/blog/index.js'
|
|
42
|
+
|
|
43
|
+
export default () => ({
|
|
44
|
+
theme: createBlogTheme()
|
|
45
|
+
})
|
|
46
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Default Theme
|
|
2
|
+
|
|
3
|
+
The default Methanol theme is designed for documentation sites (sidebar navigation + table of contents).
|
|
4
|
+
|
|
5
|
+
## Enable
|
|
6
|
+
|
|
7
|
+
CLI:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
methanol build --theme default
|
|
11
|
+
methanol dev --theme default
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Config:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
export default () => ({
|
|
18
|
+
theme: 'default'
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Notes
|
|
23
|
+
|
|
24
|
+
- User `public/` assets override theme-provided `public/` assets.
|
|
25
|
+
- Theme pages under `pages/` can provide special routes like `_404.mdx` and `offline.mdx`.
|
|
26
|
+
|
|
@@ -75,6 +75,19 @@ export default function () {
|
|
|
75
75
|
localStorage.setItem('methanol-theme', theme.value)
|
|
76
76
|
document.documentElement.classList.toggle('light', theme.value === 'light')
|
|
77
77
|
document.documentElement.classList.toggle('dark', theme.value === 'dark')
|
|
78
|
+
|
|
79
|
+
const metas = document.querySelectorAll('meta[name="theme-color"]')
|
|
80
|
+
let meta = metas[0]
|
|
81
|
+
if (!meta) {
|
|
82
|
+
meta = document.createElement('meta')
|
|
83
|
+
meta.name = 'theme-color'
|
|
84
|
+
document.head.appendChild(meta)
|
|
85
|
+
}
|
|
86
|
+
for (let i = 1; i < metas.length; i++) {
|
|
87
|
+
metas[i].remove()
|
|
88
|
+
}
|
|
89
|
+
meta.content = theme.value === 'light' ? '#ffffff' : '#09090b'
|
|
90
|
+
meta.removeAttribute('media')
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
const CurrentIcon = $(() => {
|
|
@@ -25,6 +25,19 @@
|
|
|
25
25
|
document.documentElement.classList.toggle('light', theme === 'light')
|
|
26
26
|
document.documentElement.classList.toggle('dark', theme === 'dark')
|
|
27
27
|
|
|
28
|
+
const metas = document.querySelectorAll('meta[name="theme-color"]')
|
|
29
|
+
let meta = metas[0]
|
|
30
|
+
if (!meta) {
|
|
31
|
+
meta = document.createElement('meta')
|
|
32
|
+
meta.name = 'theme-color'
|
|
33
|
+
document.head.appendChild(meta)
|
|
34
|
+
}
|
|
35
|
+
for (let i = 1; i < metas.length; i++) {
|
|
36
|
+
metas[i].remove()
|
|
37
|
+
}
|
|
38
|
+
meta.content = theme === 'light' ? '#ffffff' : '#09090b'
|
|
39
|
+
meta.removeAttribute('media')
|
|
40
|
+
|
|
28
41
|
const savedAccent = localStorage.getItem('methanol-accent')
|
|
29
42
|
if (savedAccent && savedAccent !== 'default') {
|
|
30
43
|
document.documentElement.classList.add('accent-' + savedAccent)
|
|
@@ -108,6 +108,8 @@ const PAGE_TEMPLATE = ({ PageContent, ExtraHead, components, ctx }) => {
|
|
|
108
108
|
<head>
|
|
109
109
|
<meta charset="UTF-8" />
|
|
110
110
|
<meta name="viewport" content="width=device-width" />
|
|
111
|
+
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
|
112
|
+
<meta name="theme-color" content="#09090b" media="(prefers-color-scheme: dark)" />
|
|
111
113
|
<title>
|
|
112
114
|
{title} | {siteName}
|
|
113
115
|
</title>
|