methanol 0.0.18 → 0.0.20
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/build-system.js +76 -19
- package/src/components.js +2 -0
- package/src/config.js +41 -0
- package/src/dev-server.js +6 -4
- package/src/feed.js +198 -0
- package/src/main.js +6 -1
- package/src/mdx.js +48 -24
- package/src/pages.js +105 -111
- package/src/reframe.js +19 -2
- package/src/state.js +22 -0
- package/src/templates/atom-feed.jsx +61 -0
- package/src/{error-page.jsx → templates/error-page.jsx} +2 -2
- package/src/templates/rss-feed.jsx +60 -0
- package/src/workers/build-pool.js +10 -1
- package/src/workers/build-worker.js +51 -21
- package/themes/README.md +52 -0
- package/themes/blog/README.md +34 -14
- package/themes/blog/sources/style.css +44 -4
- package/themes/blog/src/layout-categories.jsx +2 -2
- package/themes/blog/src/layout-collections.jsx +2 -2
- package/themes/blog/src/layout-home.jsx +2 -2
- package/themes/blog/src/page.jsx +55 -6
- package/themes/blog/src/post-utils.js +13 -3
- package/themes/default/README.md +26 -0
- package/themes/default/components/ThemeColorSwitch.client.jsx +13 -0
- package/themes/default/sources/style.css +27 -1
- package/themes/default/sources/theme-prepare.js +13 -0
- package/themes/default/src/nav-tree.jsx +70 -55
- package/themes/default/src/page.jsx +25 -0
package/src/pages.js
CHANGED
|
@@ -46,12 +46,15 @@ const cliOverrides = {
|
|
|
46
46
|
CLI_OUTPUT_DIR: cli.CLI_OUTPUT_DIR,
|
|
47
47
|
CLI_CONFIG_PATH: cli.CLI_CONFIG_PATH,
|
|
48
48
|
CLI_SITE_NAME: cli.CLI_SITE_NAME,
|
|
49
|
+
CLI_OWNER: cli.CLI_OWNER,
|
|
49
50
|
CLI_CODE_HIGHLIGHTING: cli.CLI_CODE_HIGHLIGHTING,
|
|
50
51
|
CLI_JOBS: cli.CLI_JOBS,
|
|
51
52
|
CLI_VERBOSE: cli.CLI_VERBOSE,
|
|
52
53
|
CLI_BASE: cli.CLI_BASE,
|
|
53
54
|
CLI_SEARCH: cli.CLI_SEARCH,
|
|
54
55
|
CLI_THEME: cli.CLI_THEME,
|
|
56
|
+
CLI_RSS: cli.CLI_RSS,
|
|
57
|
+
CLI_ATOM: cli.CLI_ATOM,
|
|
55
58
|
CLI_PWA: cli.CLI_PWA
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -348,13 +351,15 @@ const buildPagesTree = (pages, options = {}) => {
|
|
|
348
351
|
const treePages = pages
|
|
349
352
|
.filter((page) => isUnderRoot(page))
|
|
350
353
|
.map((page) => {
|
|
351
|
-
|
|
354
|
+
const originalDir = page.dir
|
|
355
|
+
if (!rootDir) return { ...page, originalDir }
|
|
352
356
|
const relativePath = stripRootPrefix(page.relativePath, rootDir)
|
|
353
357
|
const dir = stripRootPrefix(page.dir, rootDir)
|
|
354
358
|
const segments = page.segments.slice(rootSegments.length)
|
|
355
359
|
const depth = segments.length
|
|
356
360
|
return {
|
|
357
361
|
...page,
|
|
362
|
+
originalDir,
|
|
358
363
|
relativePath,
|
|
359
364
|
dir,
|
|
360
365
|
segments,
|
|
@@ -363,46 +368,6 @@ const buildPagesTree = (pages, options = {}) => {
|
|
|
363
368
|
})
|
|
364
369
|
const root = []
|
|
365
370
|
const dirs = new Map()
|
|
366
|
-
const hiddenDirs = new Set(
|
|
367
|
-
treePages
|
|
368
|
-
.filter((page) => page.isIndex && page.dir && page.hidden && !(includeHiddenRoot && page.routePath === rootPath))
|
|
369
|
-
.map((page) => page.dir)
|
|
370
|
-
)
|
|
371
|
-
const exposedHiddenDirs = new Set()
|
|
372
|
-
if (currentRoutePath && currentRoutePath !== '/' && hiddenDirs.size) {
|
|
373
|
-
for (const hiddenDir of hiddenDirs) {
|
|
374
|
-
const hiddenRoute = `/${hiddenDir}`
|
|
375
|
-
if (
|
|
376
|
-
currentRouteWithinRoot === hiddenRoute ||
|
|
377
|
-
currentRouteWithinRoot.startsWith(`${hiddenRoute}/`)
|
|
378
|
-
) {
|
|
379
|
-
exposedHiddenDirs.add(hiddenDir)
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (includeHiddenRoot && rootDir) {
|
|
384
|
-
for (const hiddenDir of Array.from(hiddenDirs)) {
|
|
385
|
-
if (rootDir === hiddenDir || rootDir.startsWith(`${hiddenDir}/`)) {
|
|
386
|
-
hiddenDirs.delete(hiddenDir)
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (exposedHiddenDirs.size) {
|
|
391
|
-
for (const hiddenDir of exposedHiddenDirs) {
|
|
392
|
-
hiddenDirs.delete(hiddenDir)
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const isUnderHiddenDir = (dir) => {
|
|
396
|
-
if (!dir) return false
|
|
397
|
-
const parts = dir.split('/')
|
|
398
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
399
|
-
const candidate = parts.slice(0, i).join('/')
|
|
400
|
-
if (hiddenDirs.has(candidate)) {
|
|
401
|
-
return true
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return false
|
|
405
|
-
}
|
|
406
371
|
const getDirNode = (path, name, depth) => {
|
|
407
372
|
if (dirs.has(path)) return dirs.get(path)
|
|
408
373
|
const dir = {
|
|
@@ -416,64 +381,41 @@ const buildPagesTree = (pages, options = {}) => {
|
|
|
416
381
|
title: null,
|
|
417
382
|
weight: null,
|
|
418
383
|
date: null,
|
|
419
|
-
isRoot: false
|
|
384
|
+
isRoot: false,
|
|
385
|
+
hidden: false,
|
|
386
|
+
hiddenByFrontmatter: false
|
|
420
387
|
}
|
|
421
388
|
dirs.set(path, dir)
|
|
422
389
|
return dir
|
|
423
390
|
}
|
|
424
|
-
const isUnderExposedHiddenDir = (dir) => {
|
|
425
|
-
if (!dir || !exposedHiddenDirs.size) return false
|
|
426
|
-
for (const hiddenDir of exposedHiddenDirs) {
|
|
427
|
-
if (dir === hiddenDir || dir.startsWith(`${hiddenDir}/`)) {
|
|
428
|
-
return true
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
return false
|
|
432
|
-
}
|
|
433
391
|
for (const page of treePages) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const shouldExposeHidden =
|
|
437
|
-
!isHiddenSpecial &&
|
|
438
|
-
page.hiddenByFrontmatter === true &&
|
|
439
|
-
(
|
|
440
|
-
page.routePath === currentRoutePath ||
|
|
441
|
-
(page.isIndex && page.dir && isUnderExposedHiddenDir(page.dir))
|
|
442
|
-
)
|
|
443
|
-
if (!shouldExposeHidden) {
|
|
444
|
-
continue
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
if (isUnderHiddenDir(page.dir)) {
|
|
448
|
-
continue
|
|
449
|
-
}
|
|
392
|
+
const isRootIndex = page.isRoot && page.isIndex
|
|
393
|
+
const promoteRoot = rootPath === '/' && isRootIndex && page.routePath !== '/'
|
|
450
394
|
const parts = page.relativePath.split('/')
|
|
451
395
|
parts.pop()
|
|
452
396
|
let cursor = root
|
|
453
397
|
let currentPath = ''
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
cursor.push(dir)
|
|
398
|
+
if (!promoteRoot) {
|
|
399
|
+
for (const part of parts) {
|
|
400
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part
|
|
401
|
+
const dir = getDirNode(currentPath, part, currentPath.split('/').length)
|
|
402
|
+
if (!cursor.includes(dir)) {
|
|
403
|
+
cursor.push(dir)
|
|
404
|
+
}
|
|
405
|
+
cursor = dir.children
|
|
463
406
|
}
|
|
464
|
-
cursor = dir.children
|
|
465
|
-
}
|
|
466
|
-
if (!cursor) {
|
|
467
|
-
continue
|
|
468
407
|
}
|
|
469
|
-
|
|
470
|
-
|
|
408
|
+
const dirPath = page.dir || ''
|
|
409
|
+
if (page.isIndex && dirPath && !page.isRoot) {
|
|
410
|
+
const dir = getDirNode(dirPath, dirPath.split('/').pop() || '', dirPath ? dirPath.split('/').length : 0)
|
|
471
411
|
dir.routePath = page.routePath
|
|
472
412
|
dir.routeHref = page.routeHref
|
|
473
413
|
dir.title = page.title
|
|
474
414
|
dir.weight = page.weight ?? null
|
|
475
415
|
dir.date = page.date ?? null
|
|
476
416
|
dir.isRoot = page.isRoot || false
|
|
417
|
+
dir.hidden = page.hidden || false
|
|
418
|
+
dir.hiddenByFrontmatter = page.hiddenByFrontmatter || false
|
|
477
419
|
dir.page = page
|
|
478
420
|
continue
|
|
479
421
|
}
|
|
@@ -771,44 +713,81 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
771
713
|
}
|
|
772
714
|
return pagesByRoute.get(routePath) || null
|
|
773
715
|
}
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
return
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
716
|
+
const filterPagesForRoot = (rootPath) => {
|
|
717
|
+
const normalizedRoot = normalizeRoutePath(rootPath || '/')
|
|
718
|
+
return pages.filter((page) => {
|
|
719
|
+
const resolvedRoot = resolveRootPath(page.routePath, pagesByRoute)
|
|
720
|
+
if (normalizedRoot === '/') {
|
|
721
|
+
return resolvedRoot === '/' || page.routePath === resolvedRoot
|
|
722
|
+
}
|
|
723
|
+
return resolvedRoot === normalizedRoot
|
|
724
|
+
})
|
|
725
|
+
}
|
|
726
|
+
const buildGlobalTree = () =>
|
|
727
|
+
buildPagesTree(filterPagesForRoot('/'), {
|
|
728
|
+
rootPath: '/',
|
|
729
|
+
includeHiddenRoot: false,
|
|
730
|
+
currentRoutePath: '/'
|
|
787
731
|
})
|
|
788
|
-
|
|
789
|
-
|
|
732
|
+
let pagesTreeGlobal = buildGlobalTree()
|
|
733
|
+
const treeByRoot = new Map()
|
|
734
|
+
const navSequenceByRoot = new Map()
|
|
735
|
+
|
|
736
|
+
const getFilteredTreeForRoot = (rootPath) => {
|
|
737
|
+
const normalizedRoot = normalizeRoutePath(rootPath || '/')
|
|
738
|
+
if (treeByRoot.has(normalizedRoot)) return treeByRoot.get(normalizedRoot)
|
|
739
|
+
if (normalizedRoot === '/') {
|
|
740
|
+
treeByRoot.set(normalizedRoot, pagesTreeGlobal)
|
|
741
|
+
return pagesTreeGlobal
|
|
742
|
+
}
|
|
743
|
+
const scoped = buildPagesTree(filterPagesForRoot(normalizedRoot), {
|
|
744
|
+
rootPath: normalizedRoot,
|
|
745
|
+
includeHiddenRoot: false,
|
|
746
|
+
currentRoutePath: normalizedRoot
|
|
747
|
+
})
|
|
748
|
+
treeByRoot.set(normalizedRoot, scoped)
|
|
749
|
+
return scoped
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const getPagesTree = (routePath = '/') => {
|
|
753
|
+
const rootPath = resolveRootPath(routePath, pagesByRoute)
|
|
754
|
+
return getFilteredTreeForRoot(rootPath)
|
|
790
755
|
}
|
|
791
|
-
|
|
756
|
+
|
|
757
|
+
const getNavSequence = (routePath = '/') => {
|
|
792
758
|
const rootPath = resolveRootPath(routePath, pagesByRoute)
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
return navSequenceCache.get(cacheKey)
|
|
759
|
+
const normalizedRoot = normalizeRoutePath(rootPath || '/')
|
|
760
|
+
if (navSequenceByRoot.has(normalizedRoot)) {
|
|
761
|
+
return navSequenceByRoot.get(normalizedRoot)
|
|
797
762
|
}
|
|
798
|
-
const tree =
|
|
763
|
+
const tree = getFilteredTreeForRoot(rootPath)
|
|
799
764
|
const sequence = buildNavSequence(tree, pagesByRoute)
|
|
800
|
-
|
|
765
|
+
navSequenceByRoot.set(normalizedRoot, sequence)
|
|
801
766
|
return sequence
|
|
802
767
|
}
|
|
803
|
-
let pagesTree = getPagesTree('/')
|
|
804
768
|
const notFound = pagesByRoute.get('/404') || null
|
|
805
769
|
const languages = collectLanguagesFromPages(pageList)
|
|
806
770
|
const userSite = state.USER_SITE || {}
|
|
807
771
|
const siteBase = state.VITE_BASE ?? userSite.base ?? null
|
|
772
|
+
const feedPathValue = state.RSS_OPTIONS?.path
|
|
773
|
+
const isAtomFeed = Boolean(state.RSS_OPTIONS?.atom)
|
|
774
|
+
const defaultFeedPath = isAtomFeed ? '/atom.xml' : '/rss.xml'
|
|
775
|
+
const feedPath = typeof feedPathValue === 'string' && feedPathValue.trim()
|
|
776
|
+
? (feedPathValue.trim().startsWith('/') ? feedPathValue.trim() : `/${feedPathValue.trim()}`)
|
|
777
|
+
: defaultFeedPath
|
|
778
|
+
const feed = state.RSS_ENABLED
|
|
779
|
+
? {
|
|
780
|
+
enabled: true,
|
|
781
|
+
atom: isAtomFeed,
|
|
782
|
+
path: feedPath,
|
|
783
|
+
href: withBase(feedPath)
|
|
784
|
+
}
|
|
785
|
+
: { enabled: false }
|
|
808
786
|
const site = {
|
|
809
787
|
...userSite,
|
|
810
788
|
base: siteBase,
|
|
811
789
|
name: state.SITE_NAME,
|
|
790
|
+
owner: state.SITE_OWNER,
|
|
812
791
|
root: state.ROOT_DIR,
|
|
813
792
|
pagesDir: state.PAGES_DIR,
|
|
814
793
|
componentsDir: state.COMPONENTS_DIR,
|
|
@@ -820,6 +799,7 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
820
799
|
options: state.PAGEFIND_OPTIONS || null,
|
|
821
800
|
build: state.PAGEFIND_BUILD || null
|
|
822
801
|
},
|
|
802
|
+
feed,
|
|
823
803
|
generatedAt: new Date().toISOString()
|
|
824
804
|
}
|
|
825
805
|
const excludedDirPaths = new Set(Array.from(dirExcludes).map((dir) => `/${dir}`))
|
|
@@ -827,7 +807,7 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
827
807
|
pages: pageList,
|
|
828
808
|
pagesByRoute,
|
|
829
809
|
getPageByRoute,
|
|
830
|
-
pagesTree,
|
|
810
|
+
pagesTree: pagesTreeGlobal,
|
|
831
811
|
getPagesTree,
|
|
832
812
|
derivedTitleCache: pageDerivedCache,
|
|
833
813
|
setDerivedTitle: (path, title, toc) => {
|
|
@@ -839,9 +819,10 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
839
819
|
pageDerivedCache.delete(path)
|
|
840
820
|
},
|
|
841
821
|
refreshPagesTree: () => {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
822
|
+
pagesTreeGlobal = buildGlobalTree()
|
|
823
|
+
treeByRoot.clear()
|
|
824
|
+
navSequenceByRoot.clear()
|
|
825
|
+
pagesContext.pagesTree = pagesTreeGlobal
|
|
845
826
|
},
|
|
846
827
|
getSiblings: (routePath, path = null) => {
|
|
847
828
|
if (!routePath) return { prev: null, next: null }
|
|
@@ -855,6 +836,19 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
855
836
|
index = sequence.findIndex((entry) => entry.routePath === routePath)
|
|
856
837
|
}
|
|
857
838
|
if (index < 0) return { prev: null, next: null }
|
|
839
|
+
const isVisible = (entry) => {
|
|
840
|
+
if (!entry) return false
|
|
841
|
+
if (entry.isRoot === true && entry.hidden !== false) return false
|
|
842
|
+
return !entry.hidden
|
|
843
|
+
}
|
|
844
|
+
let prevIndex = index - 1
|
|
845
|
+
while (prevIndex >= 0 && !isVisible(sequence[prevIndex])) {
|
|
846
|
+
prevIndex -= 1
|
|
847
|
+
}
|
|
848
|
+
let nextIndex = index + 1
|
|
849
|
+
while (nextIndex < sequence.length && !isVisible(sequence[nextIndex])) {
|
|
850
|
+
nextIndex += 1
|
|
851
|
+
}
|
|
858
852
|
const toNavEntry = (entry) => {
|
|
859
853
|
if (!entry) return null
|
|
860
854
|
return {
|
|
@@ -865,8 +859,8 @@ export const createPagesContextFromPages = ({ pages, excludedRoutes, excludedDir
|
|
|
865
859
|
}
|
|
866
860
|
}
|
|
867
861
|
return {
|
|
868
|
-
prev: toNavEntry(sequence[
|
|
869
|
-
next: toNavEntry(sequence[
|
|
862
|
+
prev: toNavEntry(sequence[prevIndex] || null),
|
|
863
|
+
next: toNavEntry(sequence[nextIndex] || null)
|
|
870
864
|
}
|
|
871
865
|
},
|
|
872
866
|
refreshLanguages: () => {
|
package/src/reframe.js
CHANGED
|
@@ -45,6 +45,7 @@ export function env(parentEnv) {
|
|
|
45
45
|
let parent = parentEnv || null
|
|
46
46
|
|
|
47
47
|
let renderCount = 0
|
|
48
|
+
let hydrationEnabled = true
|
|
48
49
|
|
|
49
50
|
function register(info) {
|
|
50
51
|
const { clientPath, staticPath, staticImportURL, exportName } = info
|
|
@@ -62,9 +63,14 @@ export function env(parentEnv) {
|
|
|
62
63
|
|
|
63
64
|
const component = async ({ children: childrenProp, ...props }, ...children) => {
|
|
64
65
|
const id = renderCount++
|
|
65
|
-
const idStr = id.toString(16)
|
|
66
66
|
|
|
67
67
|
const staticComponent = (await import(staticImportURL)).default
|
|
68
|
+
|
|
69
|
+
if (!getHydrationEnabled()) {
|
|
70
|
+
return (R) => (staticComponent ? R.c(staticComponent, props, ...children) : null)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const idStr = id.toString(16)
|
|
68
74
|
const script = `$$rfrm(${JSON.stringify(key)},${id},${Object.keys(props).length ? JSON5.stringify(props).replace(/<\/script/ig, '<\\/script') : '{}'})`
|
|
69
75
|
|
|
70
76
|
return (R) => {
|
|
@@ -101,9 +107,18 @@ export function env(parentEnv) {
|
|
|
101
107
|
parent = nextParent || null
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
function setHydrationEnabled(value) {
|
|
111
|
+
hydrationEnabled = value !== false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getHydrationEnabled() {
|
|
115
|
+
if (hydrationEnabled === false) return false
|
|
116
|
+
return parent?.getHydrationEnabled?.() !== false
|
|
117
|
+
}
|
|
118
|
+
|
|
104
119
|
function resetRenderCount() {
|
|
105
120
|
renderCount = 0
|
|
106
|
-
parent?.resetRenderCount()
|
|
121
|
+
parent?.resetRenderCount?.()
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
function getMergedRegistry() {
|
|
@@ -123,6 +138,8 @@ export function env(parentEnv) {
|
|
|
123
138
|
genRegistryScript,
|
|
124
139
|
setParent,
|
|
125
140
|
resetRenderCount,
|
|
141
|
+
setHydrationEnabled,
|
|
142
|
+
getHydrationEnabled,
|
|
126
143
|
get registry() {
|
|
127
144
|
return getMergedRegistry()
|
|
128
145
|
}
|
package/src/state.js
CHANGED
|
@@ -75,6 +75,12 @@ const withCommonOptions = (y) =>
|
|
|
75
75
|
requiresArg: true,
|
|
76
76
|
nargs: 1
|
|
77
77
|
})
|
|
78
|
+
.option('owner', {
|
|
79
|
+
describe: 'Site owner override',
|
|
80
|
+
type: 'string',
|
|
81
|
+
requiresArg: true,
|
|
82
|
+
nargs: 1
|
|
83
|
+
})
|
|
78
84
|
.option('port', {
|
|
79
85
|
describe: 'Port for dev/preview',
|
|
80
86
|
type: 'number',
|
|
@@ -136,6 +142,16 @@ const withCommonOptions = (y) =>
|
|
|
136
142
|
requiresArg: true,
|
|
137
143
|
nargs: 1
|
|
138
144
|
})
|
|
145
|
+
.option('rss', {
|
|
146
|
+
describe: 'Enable or disable feed output',
|
|
147
|
+
type: 'boolean',
|
|
148
|
+
default: undefined
|
|
149
|
+
})
|
|
150
|
+
.option('atom', {
|
|
151
|
+
describe: 'Output Atom feed instead of RSS',
|
|
152
|
+
type: 'boolean',
|
|
153
|
+
default: undefined
|
|
154
|
+
})
|
|
139
155
|
.option('pwa', {
|
|
140
156
|
describe: 'Enable or disable PWA support',
|
|
141
157
|
type: 'boolean',
|
|
@@ -168,12 +184,15 @@ export const cli = {
|
|
|
168
184
|
CLI_OUTPUT_DIR: argv.output || null,
|
|
169
185
|
CLI_CONFIG_PATH: argv.config || null,
|
|
170
186
|
CLI_SITE_NAME: argv['site-name'] || null,
|
|
187
|
+
CLI_OWNER: argv.owner || null,
|
|
171
188
|
CLI_CODE_HIGHLIGHTING: typeof argv.highlight === 'boolean' ? argv.highlight : null,
|
|
172
189
|
CLI_JOBS: typeof argv.jobs === 'number' && Number.isFinite(argv.jobs) ? argv.jobs : null,
|
|
173
190
|
CLI_VERBOSE: Boolean(argv.verbose),
|
|
174
191
|
CLI_BASE: argv.base || null,
|
|
175
192
|
CLI_SEARCH: typeof argv.search === 'boolean' ? argv.search : undefined,
|
|
176
193
|
CLI_THEME: argv.theme || null,
|
|
194
|
+
CLI_RSS: typeof argv.rss === 'boolean' ? argv.rss : undefined,
|
|
195
|
+
CLI_ATOM: typeof argv.atom === 'boolean' ? argv.atom : undefined,
|
|
177
196
|
CLI_PWA: typeof argv.pwa === 'boolean' ? argv.pwa : undefined
|
|
178
197
|
}
|
|
179
198
|
|
|
@@ -181,6 +200,7 @@ export const state = {
|
|
|
181
200
|
PROJECT_ROOT,
|
|
182
201
|
ROOT_DIR: PROJECT_ROOT,
|
|
183
202
|
SITE_NAME: 'Methanol Site',
|
|
203
|
+
SITE_OWNER: null,
|
|
184
204
|
SITE_BASE: null,
|
|
185
205
|
VITE_BASE: null,
|
|
186
206
|
PAGES_DIR: resolve(PROJECT_ROOT, 'pages'),
|
|
@@ -204,6 +224,8 @@ export const state = {
|
|
|
204
224
|
PAGEFIND_ENABLED: false,
|
|
205
225
|
PAGEFIND_OPTIONS: null,
|
|
206
226
|
PAGEFIND_BUILD: null,
|
|
227
|
+
RSS_ENABLED: false,
|
|
228
|
+
RSS_OPTIONS: null,
|
|
207
229
|
PWA_ENABLED: false,
|
|
208
230
|
USER_PRE_BUILD_HOOKS: [],
|
|
209
231
|
USER_POST_BUILD_HOOKS: [],
|
|
@@ -0,0 +1,61 @@
|
|
|
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 'methanol'
|
|
22
|
+
|
|
23
|
+
const ATOM_DECLARATION = R.rawHTML('<?xml version="1.0" encoding="UTF-8"?>')
|
|
24
|
+
|
|
25
|
+
const AtomFeed = ({ site, items }) => {
|
|
26
|
+
const title = site?.title || site?.name || 'Methanol Feed'
|
|
27
|
+
const siteUrl = site?.url || null
|
|
28
|
+
const feedUrl = site?.feedUrl || null
|
|
29
|
+
const updated = site?.updated || new Date().toISOString()
|
|
30
|
+
const generator = site?.generator || 'Methanol'
|
|
31
|
+
return [
|
|
32
|
+
ATOM_DECLARATION,
|
|
33
|
+
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
34
|
+
<title>{title}</title>
|
|
35
|
+
{siteUrl ? <link href={siteUrl} /> : null}
|
|
36
|
+
{feedUrl ? <link rel="self" href={feedUrl} /> : null}
|
|
37
|
+
<id>{feedUrl || siteUrl || title}</id>
|
|
38
|
+
<updated>{updated}</updated>
|
|
39
|
+
{generator ? <generator>{generator}</generator> : null}
|
|
40
|
+
{Array.isArray(items)
|
|
41
|
+
? items.map((item) => (
|
|
42
|
+
<entry>
|
|
43
|
+
<title>{item.title}</title>
|
|
44
|
+
<link href={item.link} />
|
|
45
|
+
<id>{item.link}</id>
|
|
46
|
+
{item.description ? <summary>{item.description}</summary> : null}
|
|
47
|
+
{item.content ? <content type="html">{item.content}</content> : null}
|
|
48
|
+
{item.author ? (
|
|
49
|
+
<author>
|
|
50
|
+
<name>{item.author}</name>
|
|
51
|
+
</author>
|
|
52
|
+
) : null}
|
|
53
|
+
{item.updated ? <updated>{item.updated}</updated> : null}
|
|
54
|
+
</entry>
|
|
55
|
+
))
|
|
56
|
+
: null}
|
|
57
|
+
</feed>
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default AtomFeed
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
* under the License.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { DOCTYPE_HTML } from 'methanol'
|
|
22
22
|
|
|
23
23
|
export const DevErrorPage = ({ message = '', basePrefix = ''} = {}) => (
|
|
24
24
|
<>
|
|
25
|
-
{
|
|
25
|
+
{DOCTYPE_HTML}
|
|
26
26
|
<html lang="en">
|
|
27
27
|
<head>
|
|
28
28
|
<meta charset="UTF-8" />
|
|
@@ -0,0 +1,60 @@
|
|
|
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 'methanol'
|
|
22
|
+
|
|
23
|
+
const RSS_DECLARATION = R.rawHTML('<?xml version="1.0" encoding="UTF-8"?>')
|
|
24
|
+
|
|
25
|
+
const RssFeed = ({ site, items }) => {
|
|
26
|
+
const title = site?.title || site?.name || 'Methanol Feed'
|
|
27
|
+
const link = site?.url || null
|
|
28
|
+
const description = site?.description || ''
|
|
29
|
+
const language = site?.language || null
|
|
30
|
+
const generator = site?.generator || 'Methanol'
|
|
31
|
+
const lastBuildDate = site?.lastBuildDate || null
|
|
32
|
+
return [
|
|
33
|
+
RSS_DECLARATION,
|
|
34
|
+
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
|
35
|
+
<channel>
|
|
36
|
+
<title>{title}</title>
|
|
37
|
+
{link ? <link>{link}</link> : null}
|
|
38
|
+
<description>{description}</description>
|
|
39
|
+
{language ? <language>{language}</language> : null}
|
|
40
|
+
{generator ? <generator>{generator}</generator> : null}
|
|
41
|
+
{lastBuildDate ? <lastBuildDate>{lastBuildDate}</lastBuildDate> : null}
|
|
42
|
+
{Array.isArray(items)
|
|
43
|
+
? items.map((item) => (
|
|
44
|
+
<item>
|
|
45
|
+
<title>{item.title}</title>
|
|
46
|
+
<link>{item.link}</link>
|
|
47
|
+
<guid isPermaLink="true">{item.link}</guid>
|
|
48
|
+
{item.description ? <description>{item.description}</description> : null}
|
|
49
|
+
{item.content ? <content:encoded>{item.content}</content:encoded> : null}
|
|
50
|
+
{item.author ? <author>{item.author}</author> : null}
|
|
51
|
+
{item.pubDate ? <pubDate>{item.pubDate}</pubDate> : null}
|
|
52
|
+
</item>
|
|
53
|
+
))
|
|
54
|
+
: null}
|
|
55
|
+
</channel>
|
|
56
|
+
</rss>
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default RssFeed
|
|
@@ -34,12 +34,15 @@ const cliOverrides = {
|
|
|
34
34
|
CLI_OUTPUT_DIR: cli.CLI_OUTPUT_DIR,
|
|
35
35
|
CLI_CONFIG_PATH: cli.CLI_CONFIG_PATH,
|
|
36
36
|
CLI_SITE_NAME: cli.CLI_SITE_NAME,
|
|
37
|
+
CLI_OWNER: cli.CLI_OWNER,
|
|
37
38
|
CLI_CODE_HIGHLIGHTING: cli.CLI_CODE_HIGHLIGHTING,
|
|
38
39
|
CLI_JOBS: cli.CLI_JOBS,
|
|
39
40
|
CLI_VERBOSE: cli.CLI_VERBOSE,
|
|
40
41
|
CLI_BASE: cli.CLI_BASE,
|
|
41
42
|
CLI_SEARCH: cli.CLI_SEARCH,
|
|
42
43
|
CLI_THEME: cli.CLI_THEME,
|
|
44
|
+
CLI_RSS: cli.CLI_RSS,
|
|
45
|
+
CLI_ATOM: cli.CLI_ATOM,
|
|
43
46
|
CLI_PWA: cli.CLI_PWA
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -82,7 +85,7 @@ export const terminateWorkers = async (workers = []) => {
|
|
|
82
85
|
await Promise.all(workers.map((worker) => worker.terminate().catch(() => null)))
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
export const runWorkerStage = async ({ workers, stage, messages, onProgress, collect }) => {
|
|
88
|
+
export const runWorkerStage = async ({ workers, stage, messages, onProgress, collect, onResult }) => {
|
|
86
89
|
return await new Promise((resolve, reject) => {
|
|
87
90
|
let completed = 0
|
|
88
91
|
let doneCount = 0
|
|
@@ -107,6 +110,12 @@ export const runWorkerStage = async ({ workers, stage, messages, onProgress, col
|
|
|
107
110
|
}
|
|
108
111
|
return
|
|
109
112
|
}
|
|
113
|
+
if (message.type === 'result') {
|
|
114
|
+
if (onResult) {
|
|
115
|
+
onResult(message.result)
|
|
116
|
+
}
|
|
117
|
+
return
|
|
118
|
+
}
|
|
110
119
|
if (message.type === 'done') {
|
|
111
120
|
if (collect) {
|
|
112
121
|
const data = collect(message)
|