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/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
- if (!rootDir) return page
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
- if (page.hidden && !(includeHiddenRoot && page.routePath === rootPath)) {
435
- const isHiddenSpecial = page.routePath === '/404' || page.routePath === '/offline'
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
- for (const part of parts) {
455
- currentPath = currentPath ? `${currentPath}/${part}` : part
456
- if (hiddenDirs.has(currentPath)) {
457
- cursor = null
458
- break
459
- }
460
- const dir = getDirNode(currentPath, part, currentPath.split('/').length)
461
- if (!cursor.includes(dir)) {
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
- if (page.isIndex && page.dir) {
470
- const dir = getDirNode(page.dir, page.dir.split('/').pop(), page.depth)
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 pagesTreeCache = new Map()
775
- const navSequenceCache = new Map()
776
- const getPagesTree = (routePath) => {
777
- const rootPath = resolveRootPath(routePath, pagesByRoute)
778
- const normalizedRoute = normalizeRoutePath(routePath || '/')
779
- const cacheKey = `${rootPath}::${normalizedRoute}`
780
- if (pagesTreeCache.has(cacheKey)) {
781
- return pagesTreeCache.get(cacheKey)
782
- }
783
- const tree = buildPagesTree(pages, {
784
- rootPath,
785
- includeHiddenRoot: rootPath !== '/',
786
- currentRoutePath: normalizedRoute
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
- pagesTreeCache.set(cacheKey, tree)
789
- return tree
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
- const getNavSequence = (routePath) => {
756
+
757
+ const getNavSequence = (routePath = '/') => {
792
758
  const rootPath = resolveRootPath(routePath, pagesByRoute)
793
- const normalizedRoute = normalizeRoutePath(routePath || '/')
794
- const cacheKey = `${rootPath}::${normalizedRoute}`
795
- if (navSequenceCache.has(cacheKey)) {
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 = getPagesTree(routePath)
763
+ const tree = getFilteredTreeForRoot(rootPath)
799
764
  const sequence = buildNavSequence(tree, pagesByRoute)
800
- navSequenceCache.set(cacheKey, sequence)
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
- pagesTreeCache.clear()
843
- navSequenceCache.clear()
844
- pagesContext.pagesTree = getPagesTree('/')
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[index - 1] || null),
869
- next: toNavEntry(sequence[index + 1] || null)
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 { HTMLRenderer as R } from './renderer.js'
21
+ import { DOCTYPE_HTML } from 'methanol'
22
22
 
23
23
  export const DevErrorPage = ({ message = '', basePrefix = ''} = {}) => (
24
24
  <>
25
- {R.rawHTML`<!doctype html>`}
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)