polen 0.9.0-next.3 → 0.9.0-next.5

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 (110) hide show
  1. package/build/api/server/report-error.d.ts +2 -0
  2. package/build/api/server/report-error.d.ts.map +1 -0
  3. package/build/api/server/report-error.js +47 -0
  4. package/build/api/server/report-error.js.map +1 -0
  5. package/build/api/vite/data/navbar.d.ts +9 -0
  6. package/build/api/vite/data/navbar.d.ts.map +1 -0
  7. package/build/api/vite/data/navbar.js +6 -0
  8. package/build/api/vite/data/navbar.js.map +1 -0
  9. package/build/api/vite/plugins/core.d.ts.map +1 -1
  10. package/build/api/vite/plugins/core.js +36 -104
  11. package/build/api/vite/plugins/core.js.map +1 -1
  12. package/build/api/vite/plugins/pages.d.ts +12 -3
  13. package/build/api/vite/plugins/pages.d.ts.map +1 -1
  14. package/build/api/vite/plugins/pages.js +155 -35
  15. package/build/api/vite/plugins/pages.js.map +1 -1
  16. package/build/api/vite/plugins/serve.d.ts.map +1 -1
  17. package/build/api/vite/plugins/serve.js +5 -26
  18. package/build/api/vite/plugins/serve.js.map +1 -1
  19. package/build/cli/commands/dev.js +9 -1
  20. package/build/cli/commands/dev.js.map +1 -1
  21. package/build/lib/debug/environment-variable.d.ts +1 -0
  22. package/build/lib/debug/environment-variable.d.ts.map +1 -1
  23. package/build/lib/debug/environment-variable.js +30 -15
  24. package/build/lib/debug/environment-variable.js.map +1 -1
  25. package/build/lib/extensible-data/extensible-data.d.ts +17 -0
  26. package/build/lib/extensible-data/extensible-data.d.ts.map +1 -0
  27. package/build/lib/extensible-data/extensible-data.js +24 -0
  28. package/build/lib/extensible-data/extensible-data.js.map +1 -0
  29. package/build/lib/extensible-data/index.d.ts +2 -0
  30. package/build/lib/extensible-data/index.d.ts.map +1 -0
  31. package/build/lib/extensible-data/index.js +2 -0
  32. package/build/lib/extensible-data/index.js.map +1 -0
  33. package/build/lib/kit-temp.d.ts +2 -0
  34. package/build/lib/kit-temp.d.ts.map +1 -1
  35. package/build/lib/kit-temp.js +10 -1
  36. package/build/lib/kit-temp.js.map +1 -1
  37. package/build/lib/react-router-loader/react-router-loader.d.ts.map +1 -1
  38. package/build/lib/react-router-loader/react-router-loader.js +0 -1
  39. package/build/lib/react-router-loader/react-router-loader.js.map +1 -1
  40. package/build/lib/vite-plugin-json/index.d.ts +2 -0
  41. package/build/lib/vite-plugin-json/index.d.ts.map +1 -0
  42. package/build/lib/vite-plugin-json/index.js +2 -0
  43. package/build/lib/vite-plugin-json/index.js.map +1 -0
  44. package/build/lib/vite-plugin-json/vite-plugin-json.d.ts +64 -0
  45. package/build/lib/vite-plugin-json/vite-plugin-json.d.ts.map +1 -0
  46. package/build/lib/vite-plugin-json/vite-plugin-json.js +59 -0
  47. package/build/lib/vite-plugin-json/vite-plugin-json.js.map +1 -0
  48. package/build/lib/vite-plugin-reactive-data/index.d.ts +2 -0
  49. package/build/lib/vite-plugin-reactive-data/index.d.ts.map +1 -0
  50. package/build/lib/vite-plugin-reactive-data/index.js +2 -0
  51. package/build/lib/vite-plugin-reactive-data/index.js.map +1 -0
  52. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts +39 -0
  53. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts.map +1 -0
  54. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js +92 -0
  55. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js.map +1 -0
  56. package/build/lib/vite-plugins/build-logger.d.ts.map +1 -1
  57. package/build/lib/vite-plugins/build-logger.js +9 -8
  58. package/build/lib/vite-plugins/build-logger.js.map +1 -1
  59. package/build/project-data.d.ts +0 -11
  60. package/build/project-data.d.ts.map +1 -1
  61. package/build/template/components/Link.d.ts.map +1 -1
  62. package/build/template/components/Link.jsx +4 -3
  63. package/build/template/components/Link.jsx.map +1 -1
  64. package/build/template/components/sidebar/Sidebar.d.ts +5 -3
  65. package/build/template/components/sidebar/Sidebar.d.ts.map +1 -1
  66. package/build/template/components/sidebar/Sidebar.jsx +3 -3
  67. package/build/template/components/sidebar/Sidebar.jsx.map +1 -1
  68. package/build/template/routes/changelog.jsx +1 -1
  69. package/build/template/routes/changelog.jsx.map +1 -1
  70. package/build/template/routes/reference.jsx +1 -1
  71. package/build/template/routes/reference.jsx.map +1 -1
  72. package/build/template/routes/root.d.ts.map +1 -1
  73. package/build/template/routes/root.jsx +28 -31
  74. package/build/template/routes/root.jsx.map +1 -1
  75. package/build/template/server/app.js +1 -1
  76. package/build/template/server/app.js.map +1 -1
  77. package/build/template/server/render-page.d.ts.map +1 -1
  78. package/build/template/server/render-page.jsx +4 -1
  79. package/build/template/server/render-page.jsx.map +1 -1
  80. package/build/template/server/ssg/generate.js +1 -1
  81. package/build/template/server/ssg/generate.js.map +1 -1
  82. package/build/template/server/ssg/get-route-paths.js +1 -1
  83. package/build/template/server/ssg/get-route-paths.js.map +1 -1
  84. package/package.json +2 -1
  85. package/src/api/server/report-error.ts +61 -0
  86. package/src/api/vite/data/navbar.ts +15 -0
  87. package/src/api/vite/plugins/core.ts +38 -116
  88. package/src/api/vite/plugins/pages.ts +185 -40
  89. package/src/api/vite/plugins/serve.ts +5 -26
  90. package/src/cli/commands/dev.ts +9 -1
  91. package/src/lib/debug/environment-variable.ts +31 -14
  92. package/src/lib/extensible-data/extensible-data.ts +38 -0
  93. package/src/lib/extensible-data/index.ts +1 -0
  94. package/src/lib/kit-temp.ts +12 -1
  95. package/src/lib/react-router-loader/react-router-loader.ts +0 -1
  96. package/src/lib/vite-plugin-json/index.ts +1 -0
  97. package/src/lib/vite-plugin-json/vite-plugin-json.ts +128 -0
  98. package/src/lib/vite-plugin-reactive-data/index.ts +1 -0
  99. package/src/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.ts +131 -0
  100. package/src/lib/vite-plugins/build-logger.ts +10 -8
  101. package/src/project-data.ts +0 -13
  102. package/src/template/components/Link.tsx +6 -3
  103. package/src/template/components/sidebar/Sidebar.tsx +7 -5
  104. package/src/template/routes/changelog.tsx +1 -1
  105. package/src/template/routes/reference.tsx +1 -1
  106. package/src/template/routes/root.tsx +62 -48
  107. package/src/template/server/app.ts +1 -1
  108. package/src/template/server/render-page.tsx +4 -1
  109. package/src/template/server/ssg/generate.ts +1 -1
  110. package/src/template/server/ssg/get-route-paths.ts +1 -1
@@ -1,38 +1,32 @@
1
1
  import type { Config } from '#api/config/index'
2
+ import { NavbarData } from '#api/vite/data/navbar'
2
3
  import { VitePluginSelfContainedMode } from '#cli/_/self-contained-mode'
3
4
  import type { ReactRouter } from '#dep/react-router/index'
4
5
  import type { Vite } from '#dep/vite/index'
5
- import { reportDiagnostics } from '#lib/file-router/diagnostic-reporter'
6
- import { FileRouter } from '#lib/file-router/index'
7
- import { Tree } from '#lib/tree/index'
6
+ import { VitePluginJson } from '#lib/vite-plugin-json/index'
7
+ import { VitePluginReactiveData } from '#lib/vite-plugin-reactive-data/index'
8
8
  import { ViteVirtual } from '#lib/vite-virtual/index'
9
9
  import { debug } from '#singletons/debug'
10
+ import { superjson } from '#singletons/superjson'
10
11
  import { Json, Str } from '@wollybeard/kit'
11
- import jsesc from 'jsesc'
12
- import type { ProjectData, SidebarIndex, SiteNavigationItem } from '../../../project-data.ts'
13
- import { superjson } from '../../../singletons/superjson.ts'
12
+ import type { ProjectData } from '../../../project-data.ts'
14
13
  import { SchemaAugmentation } from '../../schema-augmentation/index.ts'
15
14
  import { Schema } from '../../schema/index.ts'
16
15
  import { createLogger } from '../logger.ts'
17
16
  import { polenVirtual } from '../vi.ts'
18
- import { createPagesPlugin, getRouteTree } from './pages.ts'
17
+ import { Pages } from './pages.ts'
19
18
 
20
19
  const _debug = debug.sub(`vite-plugin-core`)
21
20
 
22
21
  const viTemplateVariables = polenVirtual([`template`, `variables`])
23
22
  const viTemplateSchemaAugmentations = polenVirtual([`template`, `schema-augmentations`])
24
- const viProjectData = polenVirtual([`project`, `data`])
23
+ const viProjectData = polenVirtual([`project`, `data.jsonsuper`], { allowPluginProcessing: true })
25
24
 
26
25
  export interface ProjectPagesModule {
27
26
  pages: ReactRouter.RouteObject[]
28
27
  }
29
28
 
30
29
  export const Core = (config: Config.Config): Vite.PluginOption[] => {
31
- // State for current pages data (updated by pages plugin)
32
- let currentPagesData: FileRouter.ScanResult | null = null
33
- let currentTreeData: FileRouter.RouteTreeNode | null = null
34
- let viteDevServer: Vite.ViteDevServer | null = null
35
-
36
30
  // Schema cache management
37
31
  let schemaCache: Awaited<ReturnType<typeof Schema.readOrThrow>> | null = null
38
32
 
@@ -52,6 +46,7 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
52
46
  }
53
47
 
54
48
  const plugins: Vite.Plugin[] = []
49
+ const navbarData = NavbarData()
55
50
 
56
51
  // Note: The main use for this right now is to resolve the react imports
57
52
  // from the mdx vite plugin which have to go through the Polen exports since Polen keeps those deps bundled.
@@ -64,26 +59,19 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
64
59
  }))
65
60
  }
66
61
 
62
+ const json = VitePluginJson.create({
63
+ codec: {
64
+ validate: superjson,
65
+ importPath: import.meta.resolve('#singletons/superjson'),
66
+ importExport: 'superjson',
67
+ },
68
+ filter: {
69
+ moduleTypes: ['jsonsuper'],
70
+ },
71
+ })
72
+
67
73
  return [
68
74
  ...plugins,
69
-
70
- // Self-contained pages plugin
71
- ...createPagesPlugin({
72
- config,
73
- onPagesChange: (pages) => {
74
- currentPagesData = pages
75
- // Invalidate project data virtual module to regenerate navigation/sidebar
76
- if (viteDevServer) {
77
- const projectDataModule = viteDevServer.moduleGraph.getModuleById(viProjectData.resolved)
78
- if (projectDataModule) {
79
- viteDevServer.moduleGraph.invalidateModule(projectDataModule)
80
- }
81
- }
82
- },
83
- onTreeChange: (tree) => {
84
- currentTreeData = tree
85
- },
86
- }),
87
75
  /**
88
76
  * If a `polen*` import is encountered from the user's project, resolve it to the currently
89
77
  * running source code of Polen rather than the user's node_modules.
@@ -95,9 +83,9 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
95
83
  * 2. Secondary: Using Polen CLI on a project that does not have Polen installed.
96
84
  * (User would likely not want to do this because they would not be able to achieve type safety)
97
85
  */
98
-
99
86
  {
100
87
  name: `polen:internal-import-alias`,
88
+ enforce: 'pre' as const,
101
89
  resolveId(id, importer) {
102
90
  const d = debug.sub(`vite-plugin:internal-import-alias`)
103
91
 
@@ -133,11 +121,20 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
133
121
  return to
134
122
  },
135
123
  },
124
+ json,
125
+ VitePluginReactiveData.create({
126
+ moduleId: `virtual:polen/project/data/navbar`,
127
+ data: navbarData.value,
128
+ codec: superjson,
129
+ name: `polen-navbar`,
130
+ moduleType: 'jsonsuper',
131
+ }),
132
+ ...Pages({
133
+ config,
134
+ navbarData,
135
+ }),
136
136
  {
137
137
  name: `polen:core`,
138
- configureServer(server) {
139
- viteDevServer = server
140
- },
141
138
  config(_, { command }) {
142
139
  // isServing = command === `serve`
143
140
  return {
@@ -187,81 +184,15 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
187
184
  identifier: viProjectData,
188
185
  async loader() {
189
186
  _debug(`loadingViProjectDataVirtualModule`)
190
- // todo: parallel
191
187
  const schema = await readSchema()
192
188
 
193
- // Get pages data from the pages plugin or load initially
194
- if (!currentPagesData) {
195
- _debug(`loadingPagesDataInitially`)
196
- currentPagesData = await FileRouter.scan({
197
- dir: config.paths.project.absolute.pages,
198
- glob: `**/*.{md,mdx}`,
199
- })
200
- // Report any diagnostics from initial scan
201
- reportDiagnostics(currentPagesData.diagnostics)
202
- }
203
- if (!currentTreeData) {
204
- _debug(`loadingTreeDataInitially`)
205
- currentTreeData = await getRouteTree(config)
206
- }
207
- const pagesScanResult = currentPagesData
208
- const routeTree = currentTreeData
209
- _debug(`usingPageRoutesFromPagesPlugin`, pagesScanResult.routes.length)
210
-
211
- const siteNavigationItems: SiteNavigationItem[] = []
212
-
213
- //
214
- // ━━ Build Navbar
215
- //
216
-
217
- // Process first-level children as navigation items
218
- for (const child of routeTree.children) {
219
- if (child.value.type === 'directory') {
220
- // Check if this directory has an index file
221
- const hasIndex = child.children.some(c => c.value.type === 'file' && c.value.name === 'index')
222
-
223
- if (hasIndex) {
224
- const pathExp = FileRouter.pathToExpression([child.value.name])
225
- const title = Str.titlizeSlug(child.value.name)
226
- siteNavigationItems.push({
227
- pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
228
- title,
229
- })
230
- }
231
- } else if (child.value.type === 'file' && child.value.name !== 'index') {
232
- const pathExp = FileRouter.pathToExpression([child.value.name])
233
- const title = Str.titlizeSlug(child.value.name)
234
- siteNavigationItems.push({
235
- pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
236
- title,
237
- })
238
- }
239
- }
240
-
241
189
  // ━ Schema presence causes adding some navbar items
190
+ const schemaNavbar = navbarData.get('schema')
191
+ schemaNavbar.length = 0 // Clear existing
242
192
  if (schema) {
243
- siteNavigationItems.push({ pathExp: `reference`, title: `Reference` })
193
+ schemaNavbar.push({ pathExp: `reference`, title: `Reference` })
244
194
  if (schema.versions.length > 1) {
245
- siteNavigationItems.push({ pathExp: `changelog`, title: `Changelog` })
246
- }
247
- }
248
-
249
- //
250
- // ━━ Build Sidebar
251
- //
252
-
253
- const sidebarIndex: SidebarIndex = {}
254
-
255
- // Build sidebar for each top-level directory
256
- for (const child of routeTree.children) {
257
- if (child.value.type === 'directory') {
258
- const pathExp = `/${child.value.name}`
259
- // Create a subtree starting from this directory
260
- const subtree = Tree.node(child.value, child.children)
261
- // Pass the directory name as base path so paths are built correctly
262
- const sidebar = FileRouter.Sidebar.buildFromTree(subtree, [child.value.name])
263
- _debug(`Built sidebar for ${pathExp}:`, sidebar)
264
- sidebarIndex[pathExp] = sidebar
195
+ schemaNavbar.push({ pathExp: `changelog`, title: `Changelog` })
265
196
  }
266
197
  }
267
198
 
@@ -271,10 +202,7 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
271
202
 
272
203
  const projectData: ProjectData = {
273
204
  schema,
274
- siteNavigationItems,
275
- sidebarIndex,
276
205
  faviconPath: `/logo.svg`,
277
- pagesScanResult: pagesScanResult,
278
206
  paths: config.paths.project,
279
207
  server: {
280
208
  static: {
@@ -288,14 +216,8 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
288
216
  },
289
217
  }
290
218
 
291
- const projectDataCode = jsesc(superjson.stringify(projectData))
292
- const content = `
293
- import { superjson } from '#singletons/superjson'
294
-
295
- export const PROJECT_DATA = superjson.parse('${projectDataCode}')
296
- `
297
-
298
- return content
219
+ // Return just the JSON string - let the JSON plugin handle the transformation
220
+ return superjson.stringify(projectData)
299
221
  },
300
222
  },
301
223
  ),
@@ -1,10 +1,12 @@
1
1
  import type { Config } from '#api/config/index'
2
+ import type { NavbarDataRegistry } from '#api/vite/data/navbar'
2
3
  import { polenVirtual } from '#api/vite/vi'
3
4
  import type { Vite } from '#dep/vite/index'
4
5
  import { reportDiagnostics } from '#lib/file-router/diagnostic-reporter'
5
6
  import { FileRouter } from '#lib/file-router/index'
6
- import { ViteVirtual } from '#lib/vite-virtual/index'
7
+ import { Tree } from '#lib/tree/index'
7
8
  import { debug } from '#singletons/debug'
9
+ import { superjson } from '#singletons/superjson'
8
10
  import mdx from '@mdx-js/rollup'
9
11
  import { Path, Str } from '@wollybeard/kit'
10
12
  import remarkGfm from 'remark-gfm'
@@ -12,19 +14,36 @@ import remarkGfm from 'remark-gfm'
12
14
  const _debug = debug.sub(`vite-plugin-pages`)
13
15
 
14
16
  export const viProjectPages = polenVirtual([`project`, `pages.jsx`], { allowPluginProcessing: true })
17
+ export const viProjectPagesData = polenVirtual([`project`, `data`, 'pages.jsonsuper'], { allowPluginProcessing: true })
15
18
 
16
19
  export interface PagesTreePluginOptions {
17
20
  config: Config.Config
21
+ navbarData?: NavbarDataRegistry
18
22
  onPagesChange?: (pages: FileRouter.ScanResult) => void
19
23
  onTreeChange?: (tree: FileRouter.RouteTreeNode) => void
20
24
  }
21
25
 
26
+ export interface ProjectDataPages {
27
+ sidebarIndex: SidebarIndex
28
+ pagesScanResult: FileRouter.ScanResult
29
+ }
30
+
31
+ export interface SidebarIndex {
32
+ [pathExpression: string]: FileRouter.Sidebar.Sidebar
33
+ }
34
+
22
35
  /**
23
36
  * Pages plugin with tree support
24
37
  */
25
- export const createPagesPlugin = (
26
- { config, onPagesChange, onTreeChange }: PagesTreePluginOptions,
27
- ): Vite.Plugin[] => {
38
+ export const Pages = ({
39
+ config,
40
+ navbarData,
41
+ onPagesChange,
42
+ onTreeChange,
43
+ }: PagesTreePluginOptions): Vite.Plugin[] => {
44
+ let currentPagesData: FileRouter.ScanResult | null = null
45
+ let currentTreeData: FileRouter.RouteTreeNode | null = null
46
+
28
47
  // State management
29
48
  let pagesCache: FileRouter.ScanResult | null = null
30
49
  let treeCache: FileRouter.RouteTreeNode | null = null
@@ -119,60 +138,186 @@ export const createPagesPlugin = (
119
138
  },
120
139
 
121
140
  // Hot update handling
122
- async handleHotUpdate({ file, server }) {
141
+ async handleHotUpdate({ file, server, modules }) {
123
142
  _debug(`handleHotUpdate`, file)
124
143
  if (!isPageFile(file)) return
125
144
 
126
145
  _debug(`Page file changed:`, file)
127
146
 
128
- // Clear cache
147
+ // Check if this is a content-only change to an existing page
148
+ const oldPages = pagesCache
149
+
150
+ // Clear cache and rescan
129
151
  clearCache()
152
+ const newPages = await scanPages()
153
+ currentPagesData = newPages
130
154
 
131
- // Invalidate virtual module
132
- const mod = server.moduleGraph.getModuleById(viProjectPages.resolved)
133
- if (mod) {
134
- server.moduleGraph.invalidateModule(mod)
135
- _debug(`Invalidated pages virtual module`)
136
- }
155
+ // Check if page structure changed (added/removed pages)
156
+ const structureChanged = !oldPages
157
+ || oldPages.routes.length !== newPages.routes.length
158
+ || !oldPages.routes.every((oldRoute, i) =>
159
+ oldRoute.file.path.absolute === newPages.routes[i]?.file.path.absolute
160
+ )
137
161
 
138
- // Notify about pages change (for other plugins that depend on pages)
139
- if (onPagesChange) {
140
- const pages = await scanPages()
141
- // Report any diagnostics
142
- reportDiagnostics(pages.diagnostics)
143
- onPagesChange(pages)
144
- }
162
+ if (structureChanged) {
163
+ _debug(`Page structure changed, triggering full reload`)
145
164
 
146
- // Notify about tree change
147
- if (onTreeChange) {
148
- const tree = await scanTree()
149
- onTreeChange(tree)
165
+ // Invalidate virtual module
166
+ const mod = server.moduleGraph.getModuleById(viProjectPages.id)
167
+ if (mod) {
168
+ server.moduleGraph.invalidateModule(mod)
169
+ _debug(`Invalidated pages virtual module`)
170
+ }
171
+
172
+ // Notify about changes
173
+ if (onPagesChange) {
174
+ reportDiagnostics(newPages.diagnostics)
175
+ onPagesChange(newPages)
176
+ }
177
+
178
+ if (onTreeChange) {
179
+ const tree = await scanTree()
180
+ onTreeChange(tree)
181
+ currentTreeData = tree
182
+ }
183
+
184
+ // Trigger full reload for structure changes
185
+ server.ws.send({ type: `full-reload` })
186
+ return []
187
+ } else {
188
+ _debug(`Page content changed, allowing HMR`)
189
+ // Let default HMR handle the MDX file change
190
+ return modules
191
+ }
192
+ },
193
+ resolveId(id) {
194
+ if (id === viProjectPagesData.id) {
195
+ return viProjectPagesData.resolved
150
196
  }
197
+ },
198
+ load: {
199
+ // filter: {
200
+ // id: viProjectPagesData.resolved,
201
+ // },
202
+ async handler(id) {
203
+ if (id !== viProjectPagesData.resolved) return
204
+ _debug(`viProjectDataPages`)
205
+
206
+ // Get pages data from the pages plugin or load initially
207
+ if (!currentPagesData) {
208
+ _debug(`loadingPagesDataInitially`)
209
+ currentPagesData = await FileRouter.scan({
210
+ dir: config.paths.project.absolute.pages,
211
+ glob: `**/*.{md,mdx}`,
212
+ })
213
+ // Report any diagnostics from initial scan
214
+ reportDiagnostics(currentPagesData.diagnostics)
215
+ }
216
+ if (!currentTreeData) {
217
+ _debug(`loadingTreeDataInitially`)
218
+ currentTreeData = await getRouteTree(config)
219
+ }
220
+ const pagesScanResult = currentPagesData
221
+ const routeTree = currentTreeData
222
+ _debug(`usingPageRoutesFromPagesPlugin`, pagesScanResult.routes.length)
223
+
224
+ //
225
+ // ━━ Build Navbar
226
+ //
151
227
 
152
- // Trigger full reload
153
- server.ws.send({ type: `full-reload` })
228
+ // Update navbar if provided
229
+ if (navbarData) {
230
+ const navbarPages = navbarData.get('pages')
231
+ navbarPages.length = 0 // Clear existing
154
232
 
155
- return []
233
+ // Process first-level children as navigation items
234
+ for (const child of routeTree.children) {
235
+ if (child.value.type === 'directory') {
236
+ // Check if this directory has an index file
237
+ const hasIndex = child.children.some(c => c.value.type === 'file' && c.value.name === 'index')
238
+
239
+ if (hasIndex) {
240
+ const pathExp = FileRouter.pathToExpression([child.value.name])
241
+ const title = Str.titlizeSlug(child.value.name)
242
+ navbarPages.push({
243
+ pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
244
+ title,
245
+ })
246
+ }
247
+ } else if (child.value.type === 'file' && child.value.name !== 'index') {
248
+ const pathExp = FileRouter.pathToExpression([child.value.name])
249
+ const title = Str.titlizeSlug(child.value.name)
250
+ navbarPages.push({
251
+ pathExp: pathExp.startsWith('/') ? pathExp.slice(1) : pathExp,
252
+ title,
253
+ })
254
+ }
255
+ }
256
+ }
257
+
258
+ //
259
+ // ━━ Build Sidebar
260
+ //
261
+
262
+ const sidebarIndex: SidebarIndex = {}
263
+
264
+ // Build sidebar for each top-level directory
265
+ for (const child of routeTree.children) {
266
+ if (child.value.type === 'directory') {
267
+ const pathExp = `/${child.value.name}`
268
+ // Create a subtree starting from this directory
269
+ const subtree = Tree.node(child.value, child.children)
270
+ // Pass the directory name as base path so paths are built correctly
271
+ const sidebar = FileRouter.Sidebar.buildFromTree(subtree, [child.value.name])
272
+ _debug(`Built sidebar for ${pathExp}:`, sidebar)
273
+ sidebarIndex[pathExp] = sidebar
274
+ }
275
+ }
276
+
277
+ //
278
+ // ━━ Put It All together
279
+ //
280
+
281
+ const projectDataPages: ProjectDataPages = {
282
+ sidebarIndex,
283
+ pagesScanResult: pagesScanResult,
284
+ }
285
+
286
+ // Return just the JSON string - let the JSON plugin handle the transformation
287
+ return superjson.stringify(projectDataPages)
288
+ },
156
289
  },
290
+ },
291
+ // Plugin 4: Virtual Module for Pages Routes
292
+ {
293
+ name: 'polen:pages:routes',
294
+ resolveId(id) {
295
+ if (id === viProjectPages.id) {
296
+ return viProjectPages.resolved
297
+ }
298
+ },
299
+ load: {
300
+ // filter: {
301
+ // id: viProjectPages.resolved,
302
+ // },
303
+ handler: async (id) => {
304
+ if (id !== viProjectPages.resolved) return
157
305
 
158
- // Virtual module handling
159
- ...ViteVirtual.IdentifiedLoader.toHooks({
160
- identifier: viProjectPages,
161
- async loader() {
162
306
  _debug(`Loading viProjectPages virtual module`)
163
- const pagesScanResult = await scanPages()
164
- const tree = await scanTree()
165
307
 
166
- // Report any diagnostics
167
- reportDiagnostics(pagesScanResult.diagnostics)
308
+ // Ensure we have pages data
309
+ if (!currentPagesData) {
310
+ currentPagesData = await scanPages()
311
+ reportDiagnostics(currentPagesData.diagnostics)
312
+ }
168
313
 
169
- // Notify about pages (useful for initial load)
170
- onPagesChange?.(pagesScanResult)
171
- onTreeChange?.(tree)
172
-
173
- return generatePagesModule(pagesScanResult)
314
+ // Generate the module code
315
+ return {
316
+ code: generatePagesModule(currentPagesData),
317
+ moduleType: 'js',
318
+ }
174
319
  },
175
- }),
320
+ },
176
321
  },
177
322
  ]
178
323
  }
@@ -1,11 +1,11 @@
1
1
  import type { Config } from '#api/config/index'
2
+ import { reportError } from '#api/server/report-error'
2
3
  import type { Hono } from '#dep/hono/index'
3
4
  import type { Vite } from '#dep/vite/index'
5
+ import { ResponseInternalServerError } from '#lib/kit-temp'
4
6
  import { debug } from '#singletons/debug'
5
7
  import * as HonoNodeServer from '@hono/node-server'
6
- import { Err, Http } from '@wollybeard/kit'
7
- import cleanStack from 'clean-stack'
8
- import { ErrorParser } from 'youch-core'
8
+ import { Err } from '@wollybeard/kit'
9
9
 
10
10
  type App = Hono.Hono
11
11
 
@@ -28,25 +28,7 @@ export const Serve = (
28
28
  if (Err.is(error)) {
29
29
  // ━ Clean Stack Trace
30
30
  server.ssrFixStacktrace(error)
31
- const stack = cleanStack(error.stack, {
32
- pathFilter: (path) => {
33
- return !path.match(/.*rolldown-vite.*/)
34
- },
35
- basePath: config.paths.project.rootDir,
36
- // pretty: true,
37
- })
38
- error.stack = stack
39
- // ━ Log Error
40
- Err.log(error)
41
- const parser = new ErrorParser()
42
- const parsedError = await parser.parse(error)
43
- const snippet = parsedError.frames[0]?.source?.map(line => {
44
- return line.lineNumber.toString().padStart(4, ' ') + `: ` + line.chunk
45
- }).join(`\n`)
46
- if (snippet) {
47
- console.log('-----------------------------')
48
- console.log(snippet)
49
- }
31
+ reportError(error)
50
32
  return error
51
33
  }
52
34
  throw error
@@ -97,10 +79,7 @@ export const Serve = (
97
79
  const app = await appPromise
98
80
  if (Err.is(app)) {
99
81
  // Err.log(app)
100
- return new Response(null, {
101
- status: Http.Status.InternalServerError.code,
102
- statusText: Http.Status.InternalServerError.description,
103
- })
82
+ return ResponseInternalServerError()
104
83
  }
105
84
  const response = await app.fetch(request, { viteDevServer: server })
106
85
  return response
@@ -8,6 +8,7 @@ import { Err, Path } from '@wollybeard/kit'
8
8
  import { z } from 'zod'
9
9
 
10
10
  const args = Command.create()
11
+ .parameter(`--debug -d`, z.boolean().optional())
11
12
  .parameter(
12
13
  `--project -p`,
13
14
  // @ts-expect-error
@@ -28,7 +29,14 @@ const args = Command.create()
28
29
 
29
30
  const dir = ensureOptionalAbsoluteWithCwd(args.project) as string
30
31
 
31
- const viteUserConfig = await Api.ConfigResolver.fromFile({ dir })
32
+ const viteUserConfig = await Api.ConfigResolver.fromFile({
33
+ dir,
34
+ overrides: {
35
+ advanced: {
36
+ debug: args.debug,
37
+ },
38
+ },
39
+ })
32
40
 
33
41
  const viteDevServer = await Err.tryCatch(() => Vite.createServer(viteUserConfig))
34
42
 
@@ -2,6 +2,7 @@ import { Arr, Language } from '@wollybeard/kit'
2
2
 
3
3
  export const enVarName = `DEBUG`
4
4
 
5
+ export const deliminator = `,`
5
6
  export const wildcard = `*`
6
7
  export const enVarEnabledValuesStatic = [`true`, wildcard, `1`]
7
8
 
@@ -10,29 +11,45 @@ export const calcIsEnabledFromEnv = (
10
11
  namespace?: string[],
11
12
  ): boolean => {
12
13
  const namespace_ = namespace?.map(_ => _.toLowerCase())
13
- const pattern = typeof enVars[enVarName] === `string`
14
+
15
+ const includeFilters = typeof enVars[enVarName] === `string`
14
16
  ? enVars[enVarName]
15
17
  .trim()
16
18
  .toLowerCase()
17
- .split(`:`)
18
- .map(_ => _.trim())
19
+ .split(deliminator)
20
+ .map(patternExpression => {
21
+ return patternExpression
22
+ .trim()
23
+ .split(`:`)
24
+ .map(_ => _.trim())
25
+ })
19
26
  : undefined
20
27
 
21
- if (!pattern) return false
28
+ if (!includeFilters) return false
22
29
 
23
- if (pattern.length === 1 && enVarEnabledValuesStatic.includes(pattern[0]!)) return true
30
+ if (includeFilters.length === 0) return false
24
31
 
25
- if (!namespace_) return false
32
+ // If any is like * then it means "enable everything"
33
+ if (
34
+ includeFilters.some(includeFilter => {
35
+ if (includeFilter.length === 1 && enVarEnabledValuesStatic.includes(includeFilter[0]!)) return true
36
+ })
37
+ ) return true
26
38
 
27
- if (Arr.getLast(pattern) !== wildcard) {
28
- return pattern.join() === namespace_.join()
29
- }
39
+ // At this point, if there is no namespace (e.g. root), then we cannot match anything
40
+ if (!namespace_) return false
30
41
 
31
- let i = 0
32
- for (const segment of pattern) {
33
- if (segment === wildcard) return true
34
- if (segment !== namespace_[i]) return false
35
- i++
42
+ for (const includeFilter of includeFilters) {
43
+ if (Arr.getLast(includeFilter) !== wildcard) {
44
+ return includeFilter.join() === namespace_.join()
45
+ }
46
+
47
+ let i = 0
48
+ for (const segment of includeFilter) {
49
+ if (segment === wildcard) return true
50
+ if (segment !== namespace_[i]) return false
51
+ i++
52
+ }
36
53
  }
37
54
 
38
55
  Language.never()