duto 0.260915.0 → 0.261001.0

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 (49) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/commands/new.ts +55 -30
  4. package/src/commands/templates/pages/README.md +21 -0
  5. package/src/commands/templates/pages/_gitignore +24 -0
  6. package/src/commands/templates/pages/components/locale-select.tsx +59 -0
  7. package/src/commands/templates/pages/components/select.tsx +95 -0
  8. package/src/commands/templates/pages/i18n/en.ts +17 -0
  9. package/src/commands/templates/pages/i18n/index.ts +19 -0
  10. package/src/commands/templates/pages/i18n/pt-br.ts +17 -0
  11. package/src/commands/templates/pages/package.json +22 -0
  12. package/src/commands/templates/pages/pages/404.tsx +12 -0
  13. package/src/commands/templates/pages/pages/index.tsx +16 -0
  14. package/src/commands/templates/pages/pages/layout.tsx +24 -0
  15. package/src/commands/templates/pages/style.css +24 -0
  16. package/src/commands/templates/pages/tsconfig.json +54 -0
  17. package/src/commands/templates/pages/types.ts +8 -0
  18. package/src/commands/templates/server/base/.editorconfig +21 -0
  19. package/src/commands/templates/{base → server/base}/README.md +1 -1
  20. package/src/pages/cli/commands/dev.ts +10 -4
  21. package/src/pages/cli/utils.ts +3 -22
  22. package/src/pages/client/app.tsx +26 -73
  23. package/src/pages/client/router.ts +14 -280
  24. package/src/pages/compiler/aliases.ts +2 -8
  25. package/src/pages/compiler/ast.ts +1 -538
  26. package/src/pages/compiler/constants.ts +95 -13
  27. package/src/pages/compiler/index.ts +20 -126
  28. package/src/pages/compiler/meta.ts +38 -227
  29. package/src/pages/compiler/routes.ts +4 -5
  30. package/src/pages/compiler/types.ts +1 -101
  31. package/src/pages/compiler/vite-plugin.ts +73 -27
  32. package/src/pages/i18n/client.ts +18 -6
  33. package/src/pages/i18n/server.ts +21 -6
  34. package/src/pages/index.ts +1 -2
  35. package/src/server/routes.ts +4 -26
  36. package/src/utils.ts +34 -3
  37. package/src/pages/compiler/island.ts +0 -108
  38. /package/src/commands/templates/{base → pages}/.editorconfig +0 -0
  39. /package/src/commands/templates/{base → server/base}/.env.dev.example +0 -0
  40. /package/src/commands/templates/{base → server/base}/.env.prod.example +0 -0
  41. /package/src/commands/templates/{base → server/base}/_gitignore +0 -0
  42. /package/src/commands/templates/{base → server/base}/actions/blog/index.get.ts +0 -0
  43. /package/src/commands/templates/{base → server/base}/actions/blog/post/[id].get.ts +0 -0
  44. /package/src/commands/templates/{base → server/base}/actions/index.get.ts +0 -0
  45. /package/src/commands/templates/{base → server/base}/configs/duto.ts +0 -0
  46. /package/src/commands/templates/{base → server/base}/configs/roles.ts +0 -0
  47. /package/src/commands/templates/{base → server/base}/package.json +0 -0
  48. /package/src/commands/templates/{base → server/base}/tsconfig.json +0 -0
  49. /package/src/commands/templates/{workerd → server/workerd}/wrangler.toml +0 -0
@@ -1,30 +1,23 @@
1
+ import { promisify } from 'node:util'
2
+ import { brotliCompress } from 'node:zlib'
1
3
  import { mkdir } from 'node:fs/promises'
2
4
  import { join, dirname } from 'node:path'
3
- import { getLayouts, parsePage, resolveStaticOutputPath } from './meta'
4
5
 
5
6
  import { h } from 'preact'
6
7
  import { render } from 'preact-render-to-string'
7
8
  import minify from '@minify-html/node'
8
9
 
9
- import type { ResolvedConfig } from 'vite'
10
+ import { JSJSON } from 't0n'
10
11
  import { DOCTYPE_RE } from './constants'
11
- import { getAsset, getChunk, normalizeId } from './assets'
12
-
13
- import type { IRoute, Metadata, RouteParams, StaticProps, SummaryMetadata } from './types'
14
-
15
-
16
- // import i18n from '../client/i18n'
12
+ import { getAsset, getChunk } from './assets'
13
+ import { getLayouts, resolveStaticOutputPath } from './meta'
17
14
  import { setLocale } from '../i18n/client'
18
15
 
16
+ import type { ResolvedConfig } from 'vite'
17
+ import type { IRoute, Metadata, RouteParams, StaticProps, SummaryMetadata } from './types'
19
18
 
20
19
 
21
- import { promisify } from 'node:util'
22
- import { brotliCompress } from 'node:zlib'
23
-
24
- const brCompress = promisify(brotliCompress);
25
-
26
-
27
- export const selfCloseEmptyTags = (html: string, tagName: string) => html.replace(new RegExp(`<${tagName}\\b([^>]*?)>\\s*</${tagName}>`, 'g'), (_, attrs) => `<${tagName}${attrs} />`)
20
+ const brCompress = promisify(brotliCompress)
28
21
 
29
22
  export async function renderPage(
30
23
  meta: Metadata,
@@ -32,40 +25,15 @@ export async function renderPage(
32
25
  page: IRoute
33
26
  ) {
34
27
  const {
35
- mod, islands,
28
+ mod,
36
29
  hasGetProps, hasGetStaticProps,
37
30
  isClientOnly,
38
- } = await parsePage(page, meta)
39
- // const {
40
- // mod, islands,
41
- // hasGetProps, hasGetStaticProps,
42
- // isClientOnly,
43
- // } = page
44
-
45
- if (!mod?.default) {
46
- console.warn(`No default export in ${page.filePath} — skipping`)
47
- return
48
- }
31
+ } = page
32
+ if (!mod?.default)
33
+ return console.warn(`No default export in ${page.filePath} — skipping`)
49
34
 
50
- // console.log(page.filePath)
51
- // console.error('hasGetProps:', hasGetProps)
52
- // console.error('hasGetStaticProps:', hasGetStaticProps)
53
- // console.error('isClientOnly:', isClientOnly)
54
-
55
- if (page.isInternal || page.is404 || (page.isDynamic && !hasGetStaticProps)) {
56
- meta.summary.set('/'+ page.fileName, {
57
- type: 2,
58
- path: '/'+ page.fileName,
59
- size: 0,
60
- sizeBr: 0,
61
- sizeJS: 0,
62
- })
63
- // meta.routes.set(page.fullFilePath, page)
64
- return
65
- }
66
-
67
- // console.log(meta.routes.size)
68
- // meta.routes.set(page.filePath, page)
35
+ if (page.isInternal || page.is404 || (page.isDynamic && !hasGetStaticProps))
36
+ return meta.summary.set('/'+ page.fileName, { type: 2, path: '/'+ page.fileName, size: 0, sizeBr: 0, sizeJS: 0 })
69
37
 
70
38
  const { filePath, pattern, params } = page
71
39
 
@@ -108,7 +76,6 @@ export async function renderPage(
108
76
  const allProps = { ...prop.pageProps, ...prop.routeParams }
109
77
 
110
78
  // @ts-ignore
111
- // i18n(allProps?.locale || meta.i18n.fallbackLocale, meta.i18n.locales, meta.i18n.messages, meta.i18n.routes, meta.i18n.routeMap)
112
79
  allProps?.locale && setLocale(allProps?.locale || meta.i18n.fallbackLocale)
113
80
 
114
81
  const pathname = resolveStaticOutputPath(page, prop.routeParams)
@@ -138,34 +105,10 @@ export async function renderPage(
138
105
  }
139
106
  // }
140
107
 
141
- // TODO: refatorar isso... aqui dentro já é o SSG
142
- const needsClient = hasGetProps || !!isClientOnly || islands.length > 0
143
-
144
- // console.error(page.fileName)
145
- // console.error('needsClient: ', needsClient)
146
-
147
- const assets = []
148
- for (const [depPath, dep] of meta.map.get(page.fullFilePath)!.deps)
149
- assets.push(normalizeId(dep.path))
150
-
151
-
152
- html = buildHtml(assets, html) //, needsClient, allProps, pattern, islands)
153
- // html = selfCloseEmptyTags(String(minify.minify(
154
- // Buffer.from(buildHtml(assets, html, needsClient, allProps, pattern, islands)),
155
- // {}
156
- // // options != null ? options : {},
157
- // )), 'i-i')
158
-
159
- const outFile = join(
160
- config.root,
161
- config.build.outDir,
162
- pathname +'.html'
163
- )
164
- const outHTML = meta.minify ? minify.minify(
165
- Buffer.from(html),
166
- {}
167
- // options != null ? options : {},
168
- ) : html
108
+ html = buildHtml(html, Object.keys(allProps).length ? allProps : undefined)
109
+
110
+ const outFile = join(config.root, config.build.outDir, pathname +'.html')
111
+ const outHTML = meta.minify ? minify.minify(Buffer.from(html), {}) : html
169
112
  await mkdir(dirname(outFile), {recursive: true})
170
113
  await Bun.write(outFile, outHTML)
171
114
 
@@ -206,12 +149,8 @@ export async function renderPage(
206
149
  }
207
150
 
208
151
  function buildHtml(
209
- _assets: string[],
210
152
  html: string,
211
- // needsClient: boolean,
212
- // props?: StaticProps,
213
- // routePattern?: string,
214
- // islands?: Island[],
153
+ props?: StaticProps,
215
154
  ): string {
216
155
  if (!(html.includes('<body') || html.includes('<html') || html.includes('<head')))
217
156
  html = wrapHtml(html)
@@ -219,58 +158,14 @@ function buildHtml(
219
158
  if (!DOCTYPE_RE.test(html))
220
159
  html = '<!DOCTYPE html>'+ html
221
160
 
222
- // const pageProps = needsClient
223
- // ? `<script>
224
- // window.__PROPS__ = ${JSON.stringify(props ?? {})};
225
- // </script>`
226
- // : ''
227
-
228
161
  const assets = [
229
162
  ':duto/client/app.tsx',
230
- // ..._assets,
231
163
  ]
232
164
 
233
165
  const prepend = assets.map(asset => {
234
166
  const href = getAsset(asset)
235
167
  return href ? `<link href="${href}" rel=stylesheet>` : null
236
168
  }).filter(Boolean).join('')
237
- // + `<style>d-b,d-p,i-d,i-s{display:contents}</style>`
238
- // + `<style>d,d-slot,d-s-slot{display:contents}</style>`
239
-
240
- // d>*,d-slot>*,d-s-slot>*{display:revert}
241
-
242
- // d {
243
- // display: block;
244
- // box-sizing: border-box;
245
- // }
246
-
247
- // /* Herda todos os estilos padrão de div */
248
- // d:not([hidden]) {
249
- // display: block;
250
- // }
251
-
252
- // /* Remove qualquer comportamento de display:contents */
253
- // d, d-slot, d-s-slot {
254
- // display: block !important;
255
- // }
256
-
257
- // d *,
258
- // d-slot *,
259
- // d-s-slot * {
260
- // visibility: visible !important;
261
- // opacity: 1 !important;
262
- // }
263
- // </style>
264
-
265
- // <script>class DDiv extends HTMLElement {
266
- // constructor() {
267
- // super();
268
- // }
269
- // }
270
-
271
- // if (typeof customElements !== 'undefined' && !customElements.get('d')) {
272
- // customElements.define('d', DDiv);
273
- // }</script>
274
169
 
275
170
  if (html.includes('</head>')) {
276
171
  html = html.replace('</head>', prepend +'</head>')
@@ -280,8 +175,7 @@ function buildHtml(
280
175
  html = prepend + html
281
176
  }
282
177
 
283
- // const append = pageProps + assets.map(file => {
284
- const append = assets.map(file => {
178
+ const append = (props ? `<script>(self.$duto=self.$duto||{}).props=${JSJSON(props)}</script>` : '') + assets.map(file => {
285
179
  const src = getChunk(file)
286
180
  return src ? `<script src="${src}" type=module></script>` : null
287
181
  }).filter(Boolean).join('')
@@ -4,150 +4,65 @@
4
4
  // https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/entries/pages-client-entry.ts
5
5
  // https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/shims/ALS-ARCHITECTURE.md
6
6
 
7
- import { dirname, extname, join } from 'node:path'
8
- import { readdirSync } from 'node:fs'
7
+ import { dirname, extname } from 'node:path'
9
8
 
10
- import { getEnv, IMPORT, JSJSON, newID } from 't0n'
9
+ import { getEnv, JSJSON, newID } from 't0n'
11
10
 
12
11
  import { getAliases } from './aliases'
13
- import { EXT_RE, DYNAMIC_RE, INTERNAL_RE, LAYOUT_RE, NF_NAMES } from './constants'
12
+ import { DYNAMIC_RE, INTERNAL_RE, LAYOUT_RE, NF_NAMES, FILE_EXTENSIONS } from './constants'
13
+ import { scan } from '@/utils'
14
14
 
15
15
  import { USE_CLIENT_RE } from './constants'
16
- import { parseSync } from '@swc/core'
17
- import { findUsage, getFileMap } from './ast'
18
- import { replaceIslands } from './island'
19
- import { mkdir } from 'node:fs/promises'
20
-
21
16
 
22
17
  import getI18n from '../i18n/server'
23
18
  import i18n from '../i18n/client'
24
19
 
25
20
  import type {
26
- PageModule,
27
21
  IRoute, RouteParam, RouteParams,
28
22
  Metadata,
29
- ImportUsage,
30
23
  SummaryMetadata,
31
24
  } from './types'
32
25
 
33
- export function getAssets(meta: Metadata): Set<string> {
34
- const deps = new Set<string>()
35
- const queue: string[] = []
36
- const dir = join(meta.root, meta.dir)
37
-
38
- for (const [path] of meta.map) {
39
- if (path.startsWith(dir))
40
- queue.push(path)
41
- }
42
-
43
- while (queue.length > 0) {
44
- const path = queue.pop()!
45
- const imports = meta.map.get(path)!.deps
46
- if (!imports?.size) continue
47
-
48
- for (const [dep] of imports) {
49
- if (!dep.startsWith(meta.root) || dep.startsWith(dir) || deps.has(dep)) continue
50
- deps.add(dep)
51
- if (dep in meta.map) queue.push(dep)
52
- }
53
- }
54
-
55
- return deps
56
- }
57
-
58
-
59
- export async function parsePage(page: IRoute, meta: Metadata) {
60
- const fileMeta = meta.map.get(page.fullFilePath)!
61
- if (!meta.violations.has(page.fullFilePath)) {
62
- return page
63
- }
64
-
65
- const islands: ImportUsage[] = []
66
- const ast = parseSync(fileMeta.content, { syntax: 'typescript', tsx: true })
67
- for (const [depPath, dep] of fileMeta.deps) {
68
- // console.error(depPath, page.fullFilePath)
69
- if (!meta.violations.has(depPath)) continue
70
-
71
- const usages = findUsage(fileMeta.content, dep.ref, ast)
72
- // console.log(usages, dep.ref)
73
- islands.push(...usages)
74
- // for (const usage of usages)
75
- // islands.push(...usage.usages)
76
-
77
-
78
- // console.log(id)
79
- // console.log(usage)
80
- // console.log(usage[0].usages)
81
- }
82
-
83
- // // Limpar linhas vazias extras
84
- // result = result.replace(/^\s*\n/gm, '')
85
-
86
- const content = await replaceIslands(fileMeta.content, islands)
87
- // @ts-ignore
88
- let mod: PageModule = {}
89
- const tmpPath = join(meta.root, 'tmp', page.filePath)
90
- try {
91
- await mkdir(dirname(tmpPath), {recursive: true})
92
- await Bun.write(tmpPath, content)
93
- mod = await IMPORT(tmpPath)
94
- } catch {}
95
-
96
- return {
97
- safeFilePath: tmpPath,
98
- mod, content, islands,
99
- hasGetStaticProps: typeof mod.getStaticProps === 'function',
100
- hasGetProps: typeof mod.getProps === 'function',
101
- isClientOnly: USE_CLIENT_RE.test(content),
102
- }
103
- }
104
-
105
26
 
106
27
  // TODO: https://github.com/cloudflare/vinext/blob/adfc2236cba5f7cde63f32f6fb35c0d892428376/packages/vinext/src/routing/file-matcher.ts#L90
107
28
  export async function getPages(meta: Metadata) {
108
- const files = readdirSync(join(meta.root, meta.dir), {recursive: true})
109
29
  const nf = new Map<string, IRoute>()
110
30
  const layouts = new Map<string, IRoute>()
111
31
  const pages = new Map<string, IRoute>()
112
32
  const spages = new Set<string>()
113
33
  const routes: IRoute[] = []
114
34
 
115
- for (const file of files) {
116
- if (typeof file !== 'string' || !EXT_RE.test(file)) continue
117
-
35
+ for await (const [file, path, mod] of scan(meta.dir, FILE_EXTENSIONS)) {
118
36
  const ext = extname(file)
119
- const fullFilePath = join(meta.root, meta.dir, file)
120
- const { pattern, params } = filePathToWouterPattern(file)
121
-
122
- const name = file.replace(EXT_RE, '').split('/').pop() as string
37
+ const filePath = path.split(meta.dir+'/').pop() as string
38
+ const fileName = filePath.replace(ext, '')
39
+ const { pattern, params } = filePathToWouterPattern(fileName)
123
40
 
124
- const fileMeta = meta.map.get(fullFilePath)!
125
- const mod = (await IMPORT(fullFilePath)) as PageModule
41
+ const name = file.replace(ext, '').split('/').pop() as string
42
+ const content = await Bun.file(file).text()
126
43
 
127
- // console.error(file, INTERNAL_RE.test(file))
128
44
  const page = {
129
45
  fileExt: ext.substring(1),
130
- fileName: file.replace(ext, ''),
131
- filePath: file,
132
- fullFilePath,
46
+ fileName,
47
+ filePath,
48
+ fullFilePath: file,
133
49
  pattern,
134
50
  params,
135
51
  is404: name ? NF_NAMES.includes(name) : false,
136
- isDynamic: DYNAMIC_RE.test(file),
137
- isInternal: INTERNAL_RE.test(file),
52
+ isDynamic: DYNAMIC_RE.test(path),
53
+ isInternal: INTERNAL_RE.test(path),
138
54
 
139
55
  importName: newID(),
140
56
 
141
57
  mod,
142
- content: fileMeta.content,
143
- islands: [],
58
+ content: content,
144
59
  layouts: [],
145
60
  hasGetStaticProps: typeof mod.getStaticProps === 'function',
146
61
  hasGetProps: typeof mod.getProps === 'function',
147
- isClientOnly: USE_CLIENT_RE.test(fileMeta.content),
62
+ isClientOnly: USE_CLIENT_RE.test(content),
148
63
  }
149
64
 
150
- if (LAYOUT_RE.test(file)) {
65
+ if (LAYOUT_RE.test(path)) {
151
66
  layouts.set(page.fileName, page)
152
67
  continue
153
68
  }
@@ -155,14 +70,14 @@ export async function getPages(meta: Metadata) {
155
70
  if (page.is404) {
156
71
  nf.set(page.fileName, page)
157
72
  routes.push(page)
158
- pages.set(fullFilePath, page)
73
+ pages.set(file, page)
159
74
  continue
160
75
  }
161
76
 
162
77
  if (!page.isInternal)
163
- pages.set(fullFilePath, page)
78
+ pages.set(file, page)
164
79
 
165
- if (!page.isInternal && page.isDynamic && page.hasGetProps)
80
+ if (!page.isInternal)
166
81
  routes.push(page)
167
82
 
168
83
  if (!page.isInternal) {
@@ -170,11 +85,9 @@ export async function getPages(meta: Metadata) {
170
85
  try {
171
86
  const _props = await page.mod.getStaticProps?.()
172
87
  const props = Array.isArray(_props) ? _props : [_props]
173
- for (const prop of props) {
174
- // console.log(prop, file)
88
+ for (const prop of props)
175
89
  spages.add('/'+resolveStaticOutputPath(page, prop as any))
176
- }
177
- // spages.add('/'+resolveStaticOutputPath(page, prop as any))
90
+
178
91
  } catch(e) {
179
92
  console.log('errro', e)
180
93
  }
@@ -192,49 +105,6 @@ export async function getPages(meta: Metadata) {
192
105
  meta.routes = await sortRoutes(meta, routes)
193
106
  }
194
107
 
195
- /**
196
- * A partir do path de um arquivo relativo à raiz de rotas do Next.js
197
- * (app/ ou pages/), retorna:
198
- *
199
- * - layouts: arquivos "layout.{tsx,ts,jsx,js}" aplicáveis à rota, ordenados
200
- * do mais próximo do arquivo (segmento irmão) ao mais distante (raiz).
201
- * Ex.: ["users/layout.tsx", "layout.tsx"]
202
- * - document: o "_document.{tsx,ts,jsx,js}" do Pages Router, se existir na
203
- * raiz das rotas. Só é checado uma vez no topo, nunca por segmento — é
204
- * um arquivo único, diferente do layout, que pode ser aninhado.
205
- *
206
- * Compatibilidade com a especificação oficial do Next.js:
207
- * - App Router: nenhum arquivo especial usa prefixo "_" — é "layout", não
208
- * "_layout". layout.js é o componente mais externo de um route segment e
209
- * envolve template.js, error.js, loading.js, not-found.js e page.js.
210
- * - Pages Router: pages/_document.js (com "_") customiza as tags <html> e
211
- * <body> e existe só uma vez, na raiz de pages/. No App Router esse papel
212
- * foi assumido pelo root layout.js — não há "_document" equivalente lá.
213
- */
214
- // export async function getLayouts(meta: Metadata, path: string) {
215
- // const isDir = existsSync(path) && statSync(path).isDirectory()
216
- // const fileDir = isDir ? path : dirname(path)
217
-
218
- // const layouts: Page[] = []
219
- // let currentDir = fileDir
220
-
221
- // while (true) {
222
- // const layout = findFileByName(meta, currentDir, 'layout')
223
- // if (layout && meta.pages.has(layout)) {
224
- // // const mod = meta.pages.get(layout)?.mod
225
- // const { mod, content } = await parsePage(meta.pages.get(layout)!, meta)
226
- // if (mod?.default) layouts.push(mod.default)
227
- // }
228
-
229
- // const parentDir = dirname(currentDir)
230
- // if (parentDir === currentDir) break
231
-
232
- // currentDir = parentDir
233
- // }
234
-
235
- // return layouts
236
- // }
237
-
238
108
  export async function getLayouts(meta: Metadata, path: string) {
239
109
  const layouts: IRoute[] = []
240
110
  const segments = dirname(path).split(/[\\/]/).filter(Boolean)
@@ -242,15 +112,12 @@ export async function getLayouts(meta: Metadata, path: string) {
242
112
  for (let i = segments.length; i >= 0; i--) {
243
113
  const dir = segments.slice(0, i).join('/')
244
114
 
245
- const page = meta.layouts.get(dir ? dir +'/_layout' : '_layout') || meta.layouts.get(dir ? dir +'/layout' : 'layout')
246
- if (!page) continue
247
-
248
- const { mod } = await parsePage(page, meta) // @ts-ignore
249
- if (mod?.default) {
250
- page.mod = mod
251
- layouts.push(page)
252
- }
115
+ const layout = meta.layouts.get(dir ? dir +'/_layout' : '_layout') || meta.layouts.get(dir ? dir +'/layout' : 'layout')
116
+ if (!layout) continue
253
117
 
118
+ // @ts-ignore
119
+ if (layout.mod?.default)
120
+ layouts.push(layout)
254
121
  }
255
122
 
256
123
  return layouts
@@ -263,27 +130,18 @@ export async function getMetadata(root: string, dir: string) {
263
130
  meta.i18n = await getI18n(meta)
264
131
  i18n(meta.i18n.fallbackLocale, meta.i18n.locales, meta.i18n.messages, meta.i18n.routes, meta.i18n.routeMap)
265
132
 
266
- meta.aliases = getAliases(root)
267
- const fileMap = await getFileMap(meta)
268
- meta.map = fileMap.map
269
- meta.violations = fileMap.violations
270
- meta.assets = Array.from(getAssets(meta))
133
+ meta.aliases = await getAliases(root)
271
134
 
272
135
  await getPages(meta)
273
136
 
274
137
  meta.string = `// AUTO-GENERATED FILE - DO NOT EDIT
275
- ${meta.routes.map((page, index) => `\nimport Page${index + (page.hasGetProps ? `, { getProps as getProps${index} }` : '')} from '${page.fullFilePath}'`).join('\n')}
276
- ${Array.from(meta.layouts.values()).map(layout => `\nimport Layout${layout.importName} from '${layout.fullFilePath}'`).join('\n')}
138
+ ${meta.routes.filter(r => r.is404).map(r => `\nimport Page${r.importName + (r.hasGetProps ? `, { getProps as getProps${r.importName} }` : '')} from '${r.fullFilePath}'`).join('\n')}
139
+ ${Array.from(meta.layouts.values()).map(l => `\nimport Layout${l.importName} from '${l.fullFilePath}'`).join('\n')}
277
140
 
278
- export const views = [${meta.routes.map((page, index) => `['${page.pattern}',Page${index},${JSJSON(Object.entries(page.params).map(([_,v]) => v))}${page.hasGetProps ? `,getProps${index}` : (page.layouts.length ? ',0': '')}${page.layouts.length ? ',[' + Object.entries(page.layouts).map(([_,v]) => 'Layout'+v.importName) +']' : ''}]`).join(',')}]
141
+ export const views = [${meta.routes.map(r => `['${r.pattern}',${r.is404 ? 'Page'+ r.importName : `'${r.fullFilePath}'`},${Object.keys(r.params).length ? JSJSON(Object.entries(r.params).map(([_,v]) => v)) : '0'}${r.hasGetProps ? (r.is404 ? `,getProps${r.importName}` : `,1`) : (r.layouts.length ? ',0': '')}${r.layouts.length ? ',[' + Object.entries(r.layouts).map(([_,l]) => 'Layout'+l.importName) +']' : ''}]`).join(',')}]
279
142
 
280
143
  export const SRoutes = new Set(${JSJSON([...meta.spages])})
281
-
282
-
283
- ` /// + meta.i18n.string
284
-
285
- // console.error(meta.string)
286
- // console.log(meta.map.get(`/Users/nicolau/Projects/preact-skeleton/pages/index.tsx`))
144
+ `
287
145
 
288
146
  return meta
289
147
  }
@@ -381,64 +239,19 @@ function getRouteMeta(pattern: string): RouteMeta {
381
239
  segmentsCount: segments.length,
382
240
  }
383
241
  }
384
- // //////////////////////////////////////////////////////////////////////////////////////////
385
242
 
386
- // export function sortRoutes(pages: Map<string, IRoute>) {
387
- // const metas = new Map<string, { score: number, segmentsCount: number }>()
388
-
389
- // for (const [route, page] of pages)
390
- // metas.set(route, computeRouteMeta(page.pattern))
391
-
392
- // const list = Array.from(pages.values()).sort((a, b) => {
393
- // const metaA = metas.get(a.fullFilePath)!
394
- // const metaB = metas.get(b.fullFilePath)!
395
-
396
- // if (metaA.score === metaB.score)
397
- // return metaB.segmentsCount - metaA.segmentsCount
398
-
399
- // return metaB.score - metaA.score
400
- // })
401
-
402
- // while (list.length && list.at(-1)?.pattern === '/') {
403
- // const last = list.pop()
404
- // last && list.unshift(last)
405
- // }
406
-
407
- // return list
408
- // }
409
-
410
- // function computeRouteMeta(path: string) {
411
- // const segments = path.split('/').filter(Boolean)
412
-
413
- // let score = 0
414
- // for (const segment of segments) {
415
- // if (segment === '*') {
416
- // continue
417
- // } else if (segment.startsWith(':')) {
418
- // score += 1
419
- // } else {
420
- // score += 10
421
- // }
422
- // }
423
-
424
- // return { score, segmentsCount: segments.length }
425
- // }
426
-
427
- // //////////////////////////////////////////////////////////////////////////////////////////
428
243
  const CATCHALL_NAMES = new Set(['404', 'not-found'])
429
244
 
430
- export function filePathToWouterPattern(filePath: string): {
245
+ export function filePathToWouterPattern(path: string): {
431
246
  pattern: string
432
247
  params: Record<string, RouteParam>
433
248
  } {
434
- const withoutExt = filePath.replace(EXT_RE, '')
435
-
436
249
  const normalized =
437
- withoutExt === 'index'
250
+ path === 'index'
438
251
  ? ''
439
- : withoutExt.endsWith('/index')
440
- ? withoutExt.slice(0, -6)
441
- : withoutExt
252
+ : path.endsWith('/index')
253
+ ? path.slice(0, -6)
254
+ : path
442
255
 
443
256
  const segments = normalized ? normalized.split('/') : []
444
257
 
@@ -535,8 +348,6 @@ export function filePathToWouterPattern(filePath: string): {
535
348
 
536
349
  return { pattern, params }
537
350
  }
538
- // //////////////////////////////////////////////////////////////////////////////////////////
539
-
540
351
 
541
352
  /**
542
353
  * Given a static props entry (returned by getStaticProps),
@@ -1,13 +1,12 @@
1
- import { join } from 'pathe'
2
- import { _root } from '@/utils'
3
- import type { IRoute, Metadata } from './types'
4
- import { dirname } from 'node:path'
1
+ import { dirname, join } from 'pathe'
5
2
  import { NF_NAMES } from './constants'
3
+ import { _root } from '@/utils'
4
+ import type { Metadata } from './types'
6
5
 
7
6
  export const router = new Bun.FileSystemRouter({
8
7
  style: 'nextjs',
9
8
  dir: join(_root, 'pages'),
10
- // origin: 'https://mydomain.com',
9
+ // origin: 'https://example.com',
11
10
  // assetPrefix: 'assets/',
12
11
  })
13
12
  export const reload = () => router.reload()