polen 0.9.0-next.6 → 0.9.0-next.8

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.
Files changed (89) hide show
  1. package/README.md +66 -4
  2. package/build/api/builder/builder.d.ts +1 -0
  3. package/build/api/builder/builder.d.ts.map +1 -1
  4. package/build/api/builder/builder.js +1 -0
  5. package/build/api/builder/builder.js.map +1 -1
  6. package/build/api/config/configurator.d.ts +21 -5
  7. package/build/api/config/configurator.d.ts.map +1 -1
  8. package/build/api/config/configurator.js +12 -0
  9. package/build/api/config/configurator.js.map +1 -1
  10. package/build/api/config-resolver/resolve.d.ts +1 -1
  11. package/build/api/config-resolver/resolve.js +1 -1
  12. package/build/api/config-resolver/vite.d.ts.map +1 -1
  13. package/build/api/config-resolver/vite.js +1 -0
  14. package/build/api/config-resolver/vite.js.map +1 -1
  15. package/build/api/schema/read.d.ts +1 -1
  16. package/build/api/singletons/markdown/markdown.js +4 -4
  17. package/build/api/singletons/markdown/markdown.js.map +1 -1
  18. package/build/api/utils/asset-url/asset-url.d.ts +20 -0
  19. package/build/api/utils/asset-url/asset-url.d.ts.map +1 -0
  20. package/build/api/utils/asset-url/asset-url.js +35 -0
  21. package/build/api/utils/asset-url/asset-url.js.map +1 -0
  22. package/build/api/utils/asset-url/index.d.ts +2 -0
  23. package/build/api/utils/asset-url/index.d.ts.map +1 -0
  24. package/build/api/utils/asset-url/index.js +2 -0
  25. package/build/api/utils/asset-url/index.js.map +1 -0
  26. package/build/api/vite/plugins/core.d.ts.map +1 -1
  27. package/build/api/vite/plugins/core.js +10 -4
  28. package/build/api/vite/plugins/core.js.map +1 -1
  29. package/build/api/vite/plugins/pages.d.ts.map +1 -1
  30. package/build/api/vite/plugins/pages.js +11 -3
  31. package/build/api/vite/plugins/pages.js.map +1 -1
  32. package/build/cli/commands/build.js +2 -0
  33. package/build/cli/commands/build.js.map +1 -1
  34. package/build/cli/commands/dev.js +2 -0
  35. package/build/cli/commands/dev.js.map +1 -1
  36. package/build/lib/shiki/shiki.d.ts +2 -2
  37. package/build/lib/shiki/shiki.d.ts.map +1 -1
  38. package/build/lib/shiki/shiki.js +3 -3
  39. package/build/lib/shiki/shiki.js.map +1 -1
  40. package/build/lib/vite-virtual/identifier.d.ts +2 -2
  41. package/build/project-data.d.ts +1 -0
  42. package/build/project-data.d.ts.map +1 -1
  43. package/build/template/components/Link.d.ts.map +1 -1
  44. package/build/template/components/Link.jsx +2 -1
  45. package/build/template/components/Link.jsx.map +1 -1
  46. package/build/template/entry.client.jsx +2 -0
  47. package/build/template/entry.client.jsx.map +1 -1
  48. package/build/template/routes/root.d.ts.map +1 -1
  49. package/build/template/routes/root.jsx +7 -4
  50. package/build/template/routes/root.jsx.map +1 -1
  51. package/build/template/server/manifest.d.ts +1 -1
  52. package/build/template/server/manifest.d.ts.map +1 -1
  53. package/build/template/server/manifest.js +6 -5
  54. package/build/template/server/manifest.js.map +1 -1
  55. package/build/template/server/render-page.d.ts.map +1 -1
  56. package/build/template/server/render-page.jsx +2 -1
  57. package/build/template/server/render-page.jsx.map +1 -1
  58. package/build/template/server/ssg/generate.d.ts.map +1 -1
  59. package/build/template/server/ssg/generate.js +50 -7
  60. package/build/template/server/ssg/generate.js.map +1 -1
  61. package/build/template/server/view.d.ts.map +1 -1
  62. package/build/template/server/view.js +4 -1
  63. package/build/template/server/view.js.map +1 -1
  64. package/package.json +10 -6
  65. package/src/api/builder/builder.ts +2 -0
  66. package/src/api/config/configurator.ts +34 -5
  67. package/src/api/config-resolver/resolve.ts +1 -1
  68. package/src/api/config-resolver/vite.ts +1 -0
  69. package/src/api/schema/read.ts +1 -1
  70. package/src/api/singletons/markdown/markdown.test.ts +7 -7
  71. package/src/api/singletons/markdown/markdown.ts +4 -4
  72. package/src/api/utils/asset-url/asset-url.test.ts +47 -0
  73. package/src/api/utils/asset-url/asset-url.ts +38 -0
  74. package/src/api/utils/asset-url/index.ts +1 -0
  75. package/src/api/vite/plugins/core.ts +10 -4
  76. package/src/api/vite/plugins/pages.ts +11 -3
  77. package/src/cli/commands/build.ts +5 -0
  78. package/src/cli/commands/dev.ts +5 -0
  79. package/src/lib/shiki/index.ts +1 -1
  80. package/src/lib/shiki/shiki.ts +7 -7
  81. package/src/lib/vite-virtual/identifier.ts +2 -2
  82. package/src/project-data.ts +1 -0
  83. package/src/template/components/Link.tsx +4 -3
  84. package/src/template/entry.client.tsx +2 -0
  85. package/src/template/routes/root.tsx +7 -4
  86. package/src/template/server/manifest.ts +6 -3
  87. package/src/template/server/render-page.tsx +2 -1
  88. package/src/template/server/ssg/generate.ts +70 -7
  89. package/src/template/server/view.ts +4 -1
@@ -6,7 +6,7 @@ export type DataSourceType = `file` | `directory` | `memory` | `data`
6
6
 
7
7
  export interface Config {
8
8
  /**
9
- * @defaultValue `true`
9
+ * @default `true`
10
10
  */
11
11
  enabled?: boolean
12
12
  useDataSources?: Arr.Maybe<DataSourceType>
@@ -12,7 +12,7 @@ console.log(x)
12
12
  \`\`\`
13
13
  `
14
14
  const result = await parse(markdown)
15
-
15
+
16
16
  expect(result).toContain(`<h1>Hello</h1>`)
17
17
  expect(result).toContain(`<pre`)
18
18
  expect(result).toContain(`shiki`)
@@ -32,7 +32,7 @@ type Query {
32
32
  \`\`\`
33
33
  `
34
34
  const result = await parse(markdown)
35
-
35
+
36
36
  expect(result).toContain(`type`)
37
37
  expect(result).toContain(`Query`)
38
38
  // Check that both ID and ! are present (they may be in separate spans)
@@ -43,7 +43,7 @@ type Query {
43
43
  test(`parse handles inline code`, async () => {
44
44
  const markdown = `This is \`inline code\` in a sentence.`
45
45
  const result = await parse(markdown)
46
-
46
+
47
47
  expect(result).toContain(`<code>inline code</code>`)
48
48
  })
49
49
 
@@ -57,7 +57,7 @@ type Query {
57
57
  - [ ] Task 2
58
58
  `
59
59
  const result = await parse(markdown)
60
-
60
+
61
61
  expect(result).toContain(`<table>`)
62
62
  expect(result).toContain(`<input`)
63
63
  expect(result).toContain(`checked`)
@@ -70,7 +70,7 @@ plain text without language
70
70
  \`\`\`
71
71
  `
72
72
  const result = await parse(markdown)
73
-
73
+
74
74
  expect(result).toContain(`<pre`)
75
75
  expect(result).toContain(`plain text without language`)
76
76
  })
@@ -82,8 +82,8 @@ const theme = "light"
82
82
  \`\`\`
83
83
  `
84
84
  const result = await parse(markdown)
85
-
85
+
86
86
  expect(result).toContain(`--shiki-light`)
87
87
  expect(result).toContain(`--shiki-dark`)
88
88
  })
89
- })
89
+ })
@@ -1,9 +1,9 @@
1
- import { unified } from 'unified'
2
- import remarkParse from 'remark-parse'
3
- import remarkGfm from 'remark-gfm'
4
- import remarkRehype from 'remark-rehype'
5
1
  import rehypeShiki from '@shikijs/rehype'
6
2
  import rehypeStringify from 'rehype-stringify'
3
+ import remarkGfm from 'remark-gfm'
4
+ import remarkParse from 'remark-parse'
5
+ import remarkRehype from 'remark-rehype'
6
+ import { unified } from 'unified'
7
7
 
8
8
  // Create a processor with Shiki for syntax highlighting (async only)
9
9
  const createProcessorWithShiki = () => {
@@ -0,0 +1,47 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { assetUrl, faviconUrl, joinPaths, pageUrl } from './asset-url.js'
3
+
4
+ describe('asset-url helpers', () => {
5
+ describe('joinPaths', () => {
6
+ test('joins base path with asset path', () => {
7
+ expect(joinPaths('/', 'assets/style.css')).toBe('/assets/style.css')
8
+ expect(joinPaths('/my-app/', 'assets/style.css')).toBe('/my-app/assets/style.css')
9
+ expect(joinPaths('/my-app/', '/assets/style.css')).toBe('/my-app/assets/style.css')
10
+ })
11
+
12
+ test('handles trailing slash in base', () => {
13
+ expect(joinPaths('/base/', 'path')).toBe('/base/path')
14
+ })
15
+
16
+ test('handles leading slash in path', () => {
17
+ expect(joinPaths('/base/', '/path')).toBe('/base/path')
18
+ })
19
+ })
20
+
21
+ describe('assetUrl', () => {
22
+ test('creates asset URLs with base path', () => {
23
+ expect(assetUrl('assets/app.js', '/')).toBe('/assets/app.js')
24
+ expect(assetUrl('assets/app.js', '/my-app/')).toBe('/my-app/assets/app.js')
25
+ expect(assetUrl('/assets/app.js', '/my-app/')).toBe('/my-app/assets/app.js')
26
+ })
27
+ })
28
+
29
+ describe('faviconUrl', () => {
30
+ test('creates favicon URLs with base path', () => {
31
+ expect(faviconUrl('logo.ico', '/')).toBe('/logo.ico')
32
+ expect(faviconUrl('logo.ico', '/my-app/')).toBe('/my-app/logo.ico')
33
+ expect(faviconUrl('/logo.ico', '/my-app/')).toBe('/my-app/logo.ico')
34
+ })
35
+ })
36
+
37
+ describe('pageUrl', () => {
38
+ test('creates page URLs with base path', () => {
39
+ expect(pageUrl('/', '/')).toBe('/')
40
+ expect(pageUrl('', '/')).toBe('/')
41
+ expect(pageUrl('/', '/my-app/')).toBe('/my-app/')
42
+ expect(pageUrl('', '/my-app/')).toBe('/my-app/')
43
+ expect(pageUrl('about', '/my-app/')).toBe('/my-app/about')
44
+ expect(pageUrl('/about', '/my-app/')).toBe('/my-app/about')
45
+ })
46
+ })
47
+ })
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Helper functions for generating asset URLs that respect the base path configuration.
3
+ */
4
+
5
+ /**
6
+ * Join a base path with an asset path, handling leading/trailing slashes correctly.
7
+ */
8
+ export const joinPaths = (base: string, path: string): string => {
9
+ // Remove leading slash from path if present
10
+ const cleanPath = path.startsWith(`/`) ? path.slice(1) : path
11
+ // Base already has trailing slash by validation
12
+ return base + cleanPath
13
+ }
14
+
15
+ /**
16
+ * Create an asset URL from a path, respecting the base configuration.
17
+ */
18
+ export const assetUrl = (path: string, base: string): string => {
19
+ return joinPaths(base, path)
20
+ }
21
+
22
+ /**
23
+ * Create a favicon URL from a path, respecting the base configuration.
24
+ */
25
+ export const faviconUrl = (path: string, base: string): string => {
26
+ return assetUrl(path, base)
27
+ }
28
+
29
+ /**
30
+ * Create a page URL from a path, respecting the base configuration.
31
+ */
32
+ export const pageUrl = (path: string, base: string): string => {
33
+ // For pages, we want to ensure no double slashes
34
+ if (path === `/` || path === ``) {
35
+ return base
36
+ }
37
+ return joinPaths(base, path)
38
+ }
@@ -0,0 +1 @@
1
+ export * from './asset-url.ts'
@@ -190,9 +190,13 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
190
190
  const schemaNavbar = navbarData.get('schema')
191
191
  schemaNavbar.length = 0 // Clear existing
192
192
  if (schema) {
193
- schemaNavbar.push({ pathExp: `reference`, title: `Reference` })
193
+ // IMPORTANT: Always ensure paths start with '/' for React Router compatibility.
194
+ // Without the leading slash, React Router treats paths as relative, which causes
195
+ // hydration mismatches between SSR (where base path is prepended) and client
196
+ // (where basename is configured). This ensures consistent behavior.
197
+ schemaNavbar.push({ pathExp: `/reference`, title: `Reference` })
194
198
  if (schema.versions.length > 1) {
195
- schemaNavbar.push({ pathExp: `changelog`, title: `Changelog` })
199
+ schemaNavbar.push({ pathExp: `/changelog`, title: `Changelog` })
196
200
  }
197
201
  }
198
202
 
@@ -203,6 +207,7 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
203
207
  const projectData: ProjectData = {
204
208
  schema,
205
209
  faviconPath: `/logo.svg`,
210
+ basePath: config.build.base,
206
211
  paths: config.paths.project,
207
212
  server: {
208
213
  static: {
@@ -210,8 +215,9 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
210
215
  // relative from CWD of process that boots n1ode server
211
216
  // can easily break! Use path relative in server??
212
217
  directory: `./` + config.paths.project.relative.build.root,
213
- // Uses Hono route syntax.
214
- route: `/` + config.paths.project.relative.build.relative.assets + `/*`,
218
+ // Uses Hono route syntax - includes base path
219
+ route: config.build.base.slice(0, -1) + `/` + config.paths.project.relative.build.relative.assets
220
+ + `/*`,
215
221
  },
216
222
  },
217
223
  }
@@ -8,9 +8,9 @@ import { Tree } from '#lib/tree/index'
8
8
  import { debug } from '#singletons/debug'
9
9
  import { superjson } from '#singletons/superjson'
10
10
  import mdx from '@mdx-js/rollup'
11
+ import rehypeShiki from '@shikijs/rehype'
11
12
  import { Path, Str } from '@wollybeard/kit'
12
13
  import remarkGfm from 'remark-gfm'
13
- import rehypeShiki from '@shikijs/rehype'
14
14
 
15
15
  const _debug = debug.sub(`vite-plugin-pages`)
16
16
 
@@ -257,7 +257,11 @@ export const Pages = ({
257
257
  const pathExp = FileRouter.pathToExpression([child.value.name])
258
258
  const title = Str.titlizeSlug(child.value.name)
259
259
  navbarPages.push({
260
- pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
260
+ // IMPORTANT: Always ensure paths start with '/' for React Router compatibility.
261
+ // Without the leading slash, React Router treats paths as relative, which causes
262
+ // hydration mismatches between SSR (where base path is prepended) and client
263
+ // (where basename is configured). This ensures consistent behavior.
264
+ pathExp: pathExp.startsWith('/') ? pathExp : '/' + pathExp,
261
265
  title,
262
266
  })
263
267
  }
@@ -265,7 +269,11 @@ export const Pages = ({
265
269
  const pathExp = FileRouter.pathToExpression([child.value.name])
266
270
  const title = Str.titlizeSlug(child.value.name)
267
271
  navbarPages.push({
268
- pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
272
+ // IMPORTANT: Always ensure paths start with '/' for React Router compatibility.
273
+ // Without the leading slash, React Router treats paths as relative, which causes
274
+ // hydration mismatches between SSR (where base path is prepended) and client
275
+ // (where basename is configured). This ensures consistent behavior.
276
+ pathExp: pathExp.startsWith('/') ? pathExp : '/' + pathExp,
269
277
  title,
270
278
  })
271
279
  }
@@ -11,6 +11,10 @@ const args = Command.create()
11
11
  `--architecture -a`,
12
12
  Api.Config.BuildArchitecture.default('ssg').describe('Which kind of application architecture to output.'),
13
13
  )
14
+ .parameter(
15
+ `--base -b`,
16
+ z.string().optional().describe('Base public path for deployment (e.g., /my-project/)'),
17
+ )
14
18
  .settings({
15
19
  parameters: {
16
20
  environment: {
@@ -33,4 +37,5 @@ const args = Command.create()
33
37
  await Api.Builder.build({
34
38
  ...(args.debug === false ? {} : { debug: args.debug }),
35
39
  ...(args.architecture === 'ssg' ? {} : { architecture: args.architecture }),
40
+ ...(args.base ? { base: args.base } : {}),
36
41
  })
@@ -14,6 +14,10 @@ const args = Command.create()
14
14
  // @ts-expect-error
15
15
  z.string().optional().describe(`The path to the project directory. Default is CWD (current working directory).`),
16
16
  )
17
+ .parameter(
18
+ `--base -b`,
19
+ z.string().optional().describe('Base public path for deployment (e.g., /my-project/)'),
20
+ )
17
21
  .settings({
18
22
  parameters: {
19
23
  environment: {
@@ -32,6 +36,7 @@ const dir = ensureOptionalAbsoluteWithCwd(args.project) as string
32
36
  const viteUserConfig = await Api.ConfigResolver.fromFile({
33
37
  dir,
34
38
  overrides: {
39
+ ...(args.base ? { build: { base: args.base } } : {}),
35
40
  advanced: {
36
41
  debug: args.debug,
37
42
  },
@@ -1 +1 @@
1
- export * from './shiki.ts'
1
+ export * from './shiki.ts'
@@ -1,10 +1,10 @@
1
- import { createHighlighter, type Highlighter, type BundledTheme, type BundledLanguage } from 'shiki'
2
1
  import {
3
- transformerNotationHighlight,
4
2
  transformerNotationDiff,
5
3
  transformerNotationFocus,
4
+ transformerNotationHighlight,
6
5
  transformerRenderWhitespace,
7
6
  } from '@shikijs/transformers'
7
+ import { type BundledLanguage, type BundledTheme, createHighlighter, type Highlighter } from 'shiki'
8
8
 
9
9
  export interface ShikiOptions {
10
10
  themes?: {
@@ -88,7 +88,7 @@ export interface CodeHighlightOptions {
88
88
  theme?: 'light' | 'dark'
89
89
  showLineNumbers?: boolean
90
90
  highlightLines?: number[]
91
- diffLines?: { add: number[], remove: number[] }
91
+ diffLines?: { add: number[]; remove: number[] }
92
92
  focusLines?: number[]
93
93
  showInvisibles?: boolean
94
94
  }
@@ -104,7 +104,7 @@ export async function highlightCode({
104
104
  showInvisibles = false,
105
105
  }: CodeHighlightOptions): Promise<string> {
106
106
  const highlighter = await getHighlighter()
107
-
107
+
108
108
  const themes = {
109
109
  light: DEFAULT_THEMES.light,
110
110
  dark: DEFAULT_THEMES.dark,
@@ -119,7 +119,7 @@ export async function highlightCode({
119
119
  name: `line-numbers`,
120
120
  pre(node: any) {
121
121
  node.properties[`data-line-numbers`] = `true`
122
- }
122
+ },
123
123
  })
124
124
  }
125
125
 
@@ -131,7 +131,7 @@ export async function highlightCode({
131
131
  if (highlightLines.includes(line)) {
132
132
  node.properties[`data-highlighted`] = `true`
133
133
  }
134
- }
134
+ },
135
135
  })
136
136
  }
137
137
 
@@ -158,4 +158,4 @@ export async function highlightCode({
158
158
  }
159
159
 
160
160
  // Re-export types
161
- export type { Highlighter, BundledTheme, BundledLanguage } from 'shiki'
161
+ export type { BundledLanguage, BundledTheme, Highlighter } from 'shiki'
@@ -2,7 +2,7 @@ import { createId, markNoPlugins, normalizeId } from './id.ts'
2
2
 
3
3
  export interface Options {
4
4
  /**
5
- * @defaultValue '/'
5
+ * @default '/'
6
6
  */
7
7
  separator?: string
8
8
  /**
@@ -10,7 +10,7 @@ export interface Options {
10
10
 
11
11
  * @see https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
12
12
 
13
- * @defaultValue false
13
+ * @default false
14
14
  */
15
15
  allowPluginProcessing?: boolean
16
16
  }
@@ -4,6 +4,7 @@ import type { Schema } from './api/schema/index.ts'
4
4
  export interface ProjectData {
5
5
  schema: null | Schema.Schema
6
6
  faviconPath: string
7
+ basePath: string
7
8
  paths: Config.Config[`paths`][`project`]
8
9
  server: {
9
10
  static: {
@@ -52,11 +52,12 @@ export const getPathActiveReport = (
52
52
  // Normalize both paths for comparison
53
53
  const normalizedPath = pathExp.startsWith('/') ? pathExp.slice(1) : pathExp
54
54
  const normalizedCurrentPath = currentPathExp.startsWith('/') ? currentPathExp.slice(1) : currentPathExp
55
-
55
+
56
56
  const isDirect = normalizedCurrentPath === normalizedPath
57
- const isdescendant = normalizedCurrentPath.startsWith(normalizedPath + '/') && normalizedCurrentPath !== normalizedPath
57
+ const isdescendant = normalizedCurrentPath.startsWith(normalizedPath + '/')
58
+ && normalizedCurrentPath !== normalizedPath
58
59
  const is = isDirect || isdescendant
59
-
60
+
60
61
  return {
61
62
  is,
62
63
  isDirect,
@@ -6,6 +6,7 @@ import '@radix-ui/themes/styles.css'
6
6
  import { ReactDomClient } from '#dep/react-dom-client/index'
7
7
  import { StrictMode } from 'react'
8
8
  import { createBrowserRouter, RouterProvider } from 'react-router'
9
+ import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper'
9
10
  import { routes } from './routes.jsx'
10
11
 
11
12
  // SPA
@@ -20,6 +21,7 @@ import { routes } from './routes.jsx'
20
21
 
21
22
  const router = createBrowserRouter(routes, {
22
23
  hydrationData: window.__staticRouterHydrationData,
24
+ basename: PROJECT_DATA.basePath === `/` ? undefined : PROJECT_DATA.basePath.slice(0, -1), // Remove trailing slash for React Router
23
25
  })
24
26
 
25
27
  ReactDomClient.hydrateRoot(
@@ -1,3 +1,4 @@
1
+ import { assetUrl, faviconUrl } from '#api/utils/asset-url/index'
1
2
  import type { ReactRouter } from '#dep/react-router/index'
2
3
  import { createRoute } from '#lib/react-router-aid/react-router-aid'
3
4
  import { GitHubLogoIcon } from '@radix-ui/react-icons'
@@ -40,12 +41,12 @@ export const Component = () => {
40
41
  {import.meta.env.DEV && <link rel='stylesheet' href={radixStylesUrl} />}
41
42
  <link
42
43
  rel='icon'
43
- href={PROJECT_DATA.faviconPath.replace(`.svg`, `.ico`) + `?v=1`}
44
+ href={faviconUrl(PROJECT_DATA.faviconPath.replace(`.svg`, `.ico`) + `?v=1`, PROJECT_DATA.basePath)}
44
45
  sizes='256 x 256'
45
46
  />
46
47
  <link
47
48
  rel='icon'
48
- href={PROJECT_DATA.faviconPath + `?v=1`}
49
+ href={faviconUrl(PROJECT_DATA.faviconPath + `?v=1`, PROJECT_DATA.basePath)}
49
50
  sizes='any'
50
51
  type='image/svg+xml'
51
52
  />
@@ -120,7 +121,8 @@ const Layout = () => {
120
121
  my='8'
121
122
  mx='auto'
122
123
  >
123
- <style>{`
124
+ <style>
125
+ {`
124
126
  /* Shiki code blocks */
125
127
  pre.shiki {
126
128
  margin: 1rem 0;
@@ -151,7 +153,8 @@ const Layout = () => {
151
153
  background: transparent;
152
154
  display: block;
153
155
  }
154
- `}</style>
156
+ `}
157
+ </style>
155
158
  {header}
156
159
  {isShowSidebar && (
157
160
  <Sidebar
@@ -1,25 +1,28 @@
1
+ import { assetUrl } from '#api/utils/asset-url/index'
1
2
  import type { Vite } from '#dep/vite/index'
2
3
  import { Group, Str } from '@wollybeard/kit'
3
4
 
4
5
  export const injectManifestIntoHtml = (
5
6
  html: string,
6
7
  manifest: Vite.Manifest,
8
+ basePath: string,
7
9
  ): string => {
8
- const assets = getRelevantAsssetsFromManifest(manifest)
10
+ const assets = getRelevantAsssetsFromManifest(manifest, basePath)
9
11
  return injectAssetsIntoHtml(html, assets)
10
12
  }
11
13
 
12
14
  const getRelevantAsssetsFromManifest = (
13
15
  manifest: Vite.Manifest,
16
+ basePath: string,
14
17
  ): HttpAssetGroupSet => {
15
18
  const htmlAssets: HttpAsset[] = []
16
19
 
17
20
  for (const manifestChunk of Object.values(manifest)) {
18
21
  if (manifestChunk.isEntry) {
19
- htmlAssets.push({ type: `js`, path: `/${manifestChunk.file}` })
22
+ htmlAssets.push({ type: `js`, path: assetUrl(manifestChunk.file, basePath) })
20
23
  }
21
24
  for (const cssItem of manifestChunk.css ?? []) {
22
- htmlAssets.push({ type: `css`, path: `/${cssItem}` })
25
+ htmlAssets.push({ type: `css`, path: assetUrl(cssItem, basePath) })
23
26
  }
24
27
  }
25
28
 
@@ -6,6 +6,7 @@ import { Arr } from '@wollybeard/kit'
6
6
  import { StrictMode } from 'react'
7
7
  import * as ReactDomServer from 'react-dom/server'
8
8
  import { createStaticRouter, StaticRouterProvider } from 'react-router'
9
+ import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper'
9
10
  import viteClientAssetManifest from 'virtual:polen/vite/client/manifest'
10
11
  import { injectManifestIntoHtml } from './manifest.ts'
11
12
  import { view } from './view.ts'
@@ -29,7 +30,7 @@ export const renderPage = (
29
30
  }
30
31
 
31
32
  if (__BUILDING__) {
32
- html = injectManifestIntoHtml(html, viteClientAssetManifest)
33
+ html = injectManifestIntoHtml(html, viteClientAssetManifest, PROJECT_DATA.basePath)
33
34
  }
34
35
 
35
36
  // todo: what is this?
@@ -8,7 +8,22 @@ import { getRoutesPaths } from './get-route-paths.ts'
8
8
 
9
9
  export const generate = async (view: ReactRouter.StaticHandler) => {
10
10
  const handler: Hono.Handler = async (ctx) => {
11
- const staticHandlerContext = await view.query(ctx.req.raw)
11
+ // For SSG, we need to create a request with the base path prepended
12
+ // so React Router can match it correctly
13
+ const url = new URL(ctx.req.raw.url)
14
+ const basePath = PROJECT_DATA.basePath === '/' ? '' : PROJECT_DATA.basePath.slice(0, -1)
15
+
16
+ // Create a new request with the base path prepended to the pathname
17
+ const modifiedRequest = new Request(
18
+ `${url.protocol}//${url.host}${basePath}${url.pathname}${url.search}`,
19
+ {
20
+ method: ctx.req.raw.method,
21
+ headers: ctx.req.raw.headers,
22
+ body: ctx.req.raw.body,
23
+ },
24
+ )
25
+
26
+ const staticHandlerContext = await view.query(modifiedRequest)
12
27
  if (staticHandlerContext instanceof Response) {
13
28
  return staticHandlerContext
14
29
  }
@@ -21,12 +36,60 @@ export const generate = async (view: ReactRouter.StaticHandler) => {
21
36
  app.get(routePath, handler)
22
37
  }
23
38
 
24
- const result = await Hono.SSG.toSSG(app, NodeFs, {
25
- concurrency: 10,
26
- dir: PROJECT_DATA.paths.relative.build.root,
27
- })
39
+ // For large schemas, we need to process in smaller batches to avoid memory issues
40
+ const BATCH_SIZE = 50
41
+ const totalPaths = routePaths.length
42
+ console.log(`[info] Generating ${totalPaths} static pages...`)
43
+
44
+ for (let i = 0; i < totalPaths; i += BATCH_SIZE) {
45
+ const batchPaths = routePaths.slice(i, i + BATCH_SIZE)
46
+ const batchApp = new Hono.Hono()
47
+
48
+ // Create a custom handler for batch processing that includes base path handling
49
+ const batchHandler: Hono.Handler = async (ctx) => {
50
+ // For SSG, we need to create a request with the base path prepended
51
+ const url = new URL(ctx.req.raw.url)
52
+ const basePath = PROJECT_DATA.basePath === '/' ? '' : PROJECT_DATA.basePath.slice(0, -1)
53
+
54
+ // Create a new request with the base path prepended to the pathname
55
+ const modifiedRequest = new Request(
56
+ `${url.protocol}//${url.host}${basePath}${url.pathname}${url.search}`,
57
+ {
58
+ method: ctx.req.raw.method,
59
+ headers: ctx.req.raw.headers,
60
+ body: ctx.req.raw.body,
61
+ },
62
+ )
63
+
64
+ const staticHandlerContext = await view.query(modifiedRequest)
65
+ if (staticHandlerContext instanceof Response) {
66
+ return staticHandlerContext
67
+ }
68
+ return renderPage(staticHandlerContext)
69
+ }
28
70
 
29
- if (!result.success) {
30
- throw new Error(`Failed to generate static site`, { cause: result.error })
71
+ // Register only the routes for this batch
72
+ for (const routePath of batchPaths) {
73
+ batchApp.get(routePath, batchHandler)
74
+ }
75
+
76
+ console.log(
77
+ `[info] Processing batch ${Math.floor(i / BATCH_SIZE) + 1}/${
78
+ Math.ceil(totalPaths / BATCH_SIZE)
79
+ } (${batchPaths.length} pages)...`,
80
+ )
81
+
82
+ const result = await Hono.SSG.toSSG(batchApp, NodeFs, {
83
+ concurrency: 5, // Reduced concurrency for memory efficiency
84
+ dir: PROJECT_DATA.paths.relative.build.root,
85
+ })
86
+
87
+ if (!result.success) {
88
+ throw new Error(`Failed to generate static site at batch ${Math.floor(i / BATCH_SIZE) + 1}`, {
89
+ cause: result.error,
90
+ })
91
+ }
31
92
  }
93
+
94
+ console.log(`[info] Successfully generated ${totalPaths} static pages.`)
32
95
  }
@@ -1,4 +1,7 @@
1
1
  import { createStaticHandler } from 'react-router'
2
+ import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper'
2
3
  import { routes } from '../routes.jsx'
3
4
 
4
- export const view = createStaticHandler(routes)
5
+ export const view = createStaticHandler(routes, {
6
+ basename: PROJECT_DATA.basePath === `/` ? undefined : PROJECT_DATA.basePath.slice(0, -1), // Remove trailing slash for React Router
7
+ })