davaux 0.8.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.
- package/BASELINE.md +169 -0
- package/CLAUDE.md +518 -0
- package/LICENSE +21 -0
- package/README.md +36 -0
- package/ROADMAP.md +198 -0
- package/build.mjs +101 -0
- package/client/control.ts +247 -0
- package/client/hydrate.ts +37 -0
- package/client/index.ts +19 -0
- package/client/jsx-runtime.ts +209 -0
- package/client/resource.ts +122 -0
- package/client/signal.ts +211 -0
- package/client/store.ts +110 -0
- package/client/useHead.ts +63 -0
- package/dist/build/config.d.ts +3 -0
- package/dist/build/config.d.ts.map +1 -0
- package/dist/build/config.js +38 -0
- package/dist/build/config.js.map +7 -0
- package/dist/build/index.d.ts +2 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +13 -0
- package/dist/build/index.js.map +7 -0
- package/dist/build/plugins.d.ts +7 -0
- package/dist/build/plugins.d.ts.map +1 -0
- package/dist/build/plugins.js +85 -0
- package/dist/build/plugins.js.map +7 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +427 -0
- package/dist/cli.js.map +7 -0
- package/dist/client/control.d.ts +49 -0
- package/dist/client/control.d.ts.map +1 -0
- package/dist/client/control.js +154 -0
- package/dist/client/control.js.map +7 -0
- package/dist/client/hydrate.d.ts +7 -0
- package/dist/client/hydrate.d.ts.map +1 -0
- package/dist/client/hydrate.js +23 -0
- package/dist/client/hydrate.js.map +7 -0
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +32 -0
- package/dist/client/index.js.map +7 -0
- package/dist/client/jsx-runtime.d.ts +40 -0
- package/dist/client/jsx-runtime.d.ts.map +1 -0
- package/dist/client/jsx-runtime.js +139 -0
- package/dist/client/jsx-runtime.js.map +7 -0
- package/dist/client/resource.d.ts +31 -0
- package/dist/client/resource.d.ts.map +1 -0
- package/dist/client/resource.js +64 -0
- package/dist/client/resource.js.map +7 -0
- package/dist/client/signal.d.ts +90 -0
- package/dist/client/signal.d.ts.map +1 -0
- package/dist/client/signal.js +115 -0
- package/dist/client/signal.js.map +7 -0
- package/dist/client/store.d.ts +26 -0
- package/dist/client/store.d.ts.map +1 -0
- package/dist/client/store.js +63 -0
- package/dist/client/store.js.map +7 -0
- package/dist/client/useHead.d.ts +28 -0
- package/dist/client/useHead.d.ts.map +1 -0
- package/dist/client/useHead.js +33 -0
- package/dist/client/useHead.js.map +7 -0
- package/dist/config.d.ts +182 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +7 -0
- package/dist/create-multisite.d.ts +2 -0
- package/dist/create-multisite.d.ts.map +1 -0
- package/dist/create-multisite.js +291 -0
- package/dist/create-multisite.js.map +7 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +179 -0
- package/dist/create.js.map +7 -0
- package/dist/dev/blueprints.d.ts +11 -0
- package/dist/dev/blueprints.d.ts.map +1 -0
- package/dist/dev/blueprints.js +65 -0
- package/dist/dev/blueprints.js.map +7 -0
- package/dist/dev/components.d.ts +19 -0
- package/dist/dev/components.d.ts.map +1 -0
- package/dist/dev/components.js +87 -0
- package/dist/dev/components.js.map +7 -0
- package/dist/dev/insert.d.ts +11 -0
- package/dist/dev/insert.d.ts.map +1 -0
- package/dist/dev/insert.js +160 -0
- package/dist/dev/insert.js.map +7 -0
- package/dist/dev/remove.d.ts +53 -0
- package/dist/dev/remove.d.ts.map +1 -0
- package/dist/dev/remove.js +518 -0
- package/dist/dev/remove.js.map +7 -0
- package/dist/dev/watch.d.ts +26 -0
- package/dist/dev/watch.d.ts.map +1 -0
- package/dist/dev/watch.js +2905 -0
- package/dist/dev/watch.js.map +7 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +63 -0
- package/dist/errors.js.map +7 -0
- package/dist/generate.d.ts +2 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +191 -0
- package/dist/generate.js.map +7 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +7 -0
- package/dist/island.d.ts +24 -0
- package/dist/island.d.ts.map +1 -0
- package/dist/island.js +15 -0
- package/dist/island.js.map +7 -0
- package/dist/jsx-runtime.d.ts +406 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +90 -0
- package/dist/jsx-runtime.js.map +7 -0
- package/dist/link.d.ts +27 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +29 -0
- package/dist/link.js.map +7 -0
- package/dist/oml/fragment.d.ts +16 -0
- package/dist/oml/fragment.d.ts.map +1 -0
- package/dist/oml/fragment.js +26 -0
- package/dist/oml/fragment.js.map +7 -0
- package/dist/oml/index.d.ts +11 -0
- package/dist/oml/index.d.ts.map +1 -0
- package/dist/oml/index.js +21 -0
- package/dist/oml/index.js.map +7 -0
- package/dist/oml/jsx-runtime.d.ts +34 -0
- package/dist/oml/jsx-runtime.d.ts.map +1 -0
- package/dist/oml/jsx-runtime.js +59 -0
- package/dist/oml/jsx-runtime.js.map +7 -0
- package/dist/oml/jsx.d.ts +14 -0
- package/dist/oml/jsx.d.ts.map +1 -0
- package/dist/oml/jsx.js +96 -0
- package/dist/oml/jsx.js.map +7 -0
- package/dist/oml/page.d.ts +7 -0
- package/dist/oml/page.d.ts.map +1 -0
- package/dist/oml/page.js +6 -0
- package/dist/oml/page.js.map +7 -0
- package/dist/oml/render.d.ts +13 -0
- package/dist/oml/render.d.ts.map +1 -0
- package/dist/oml/render.js +117 -0
- package/dist/oml/render.js.map +7 -0
- package/dist/oml/types.d.ts +79 -0
- package/dist/oml/types.d.ts.map +1 -0
- package/dist/oml/types.js +64 -0
- package/dist/oml/types.js.map +7 -0
- package/dist/router/handler.d.ts +53 -0
- package/dist/router/handler.d.ts.map +1 -0
- package/dist/router/handler.js +342 -0
- package/dist/router/handler.js.map +7 -0
- package/dist/router/matcher.d.ts +21 -0
- package/dist/router/matcher.d.ts.map +1 -0
- package/dist/router/matcher.js +28 -0
- package/dist/router/matcher.js.map +7 -0
- package/dist/router/scanner.d.ts +17 -0
- package/dist/router/scanner.d.ts.map +1 -0
- package/dist/router/scanner.js +197 -0
- package/dist/router/scanner.js.map +7 -0
- package/dist/server/index.d.ts +23 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +29 -0
- package/dist/server/index.js.map +7 -0
- package/dist/signal.d.ts +15 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +29 -0
- package/dist/signal.js.map +7 -0
- package/dist/ssg.d.ts +45 -0
- package/dist/ssg.d.ts.map +1 -0
- package/dist/ssg.js +175 -0
- package/dist/ssg.js.map +7 -0
- package/dist/test/actions.test.d.ts +2 -0
- package/dist/test/actions.test.d.ts.map +1 -0
- package/dist/test/body-limits.test.d.ts +2 -0
- package/dist/test/body-limits.test.d.ts.map +1 -0
- package/dist/test/errors.test.d.ts +2 -0
- package/dist/test/errors.test.d.ts.map +1 -0
- package/dist/test/fixtures/routes/[id].page.d.ts +4 -0
- package/dist/test/fixtures/routes/[id].page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_error.d.ts +3 -0
- package/dist/test/fixtures/routes/_error.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_global.d.ts +3 -0
- package/dist/test/fixtures/routes/_global.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_layout-template.d.ts +3 -0
- package/dist/test/fixtures/routes/_layout-template.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_layout.d.ts +3 -0
- package/dist/test/fixtures/routes/_layout.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_layout_scripts.d.ts +3 -0
- package/dist/test/fixtures/routes/_layout_scripts.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_middleware.d.ts +3 -0
- package/dist/test/fixtures/routes/_middleware.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_redirect301_mw.d.ts +3 -0
- package/dist/test/fixtures/routes/_redirect301_mw.d.ts.map +1 -0
- package/dist/test/fixtures/routes/_redirect_mw.d.ts +3 -0
- package/dist/test/fixtures/routes/_redirect_mw.d.ts.map +1 -0
- package/dist/test/fixtures/routes/about.page.d.ts +3 -0
- package/dist/test/fixtures/routes/about.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/action.page.d.ts +6 -0
- package/dist/test/fixtures/routes/action.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/form-all.post.d.ts +3 -0
- package/dist/test/fixtures/routes/api/form-all.post.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/form-limited.post.d.ts +6 -0
- package/dist/test/fixtures/routes/api/form-limited.post.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/response-obj.get.d.ts +3 -0
- package/dist/test/fixtures/routes/api/response-obj.get.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/upload.post.d.ts +12 -0
- package/dist/test/fixtures/routes/api/upload.post.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/users.get.d.ts +6 -0
- package/dist/test/fixtures/routes/api/users.get.d.ts.map +1 -0
- package/dist/test/fixtures/routes/api/xml.get.d.ts +3 -0
- package/dist/test/fixtures/routes/api/xml.get.d.ts.map +1 -0
- package/dist/test/fixtures/routes/auth/_middleware.d.ts +3 -0
- package/dist/test/fixtures/routes/auth/_middleware.d.ts.map +1 -0
- package/dist/test/fixtures/routes/auth/protected.page.d.ts +3 -0
- package/dist/test/fixtures/routes/auth/protected.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/index.page.d.ts +3 -0
- package/dist/test/fixtures/routes/index.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/oml.page.d.ts +3 -0
- package/dist/test/fixtures/routes/oml.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/redirect.page.d.ts +3 -0
- package/dist/test/fixtures/routes/redirect.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/ssg/[slug].page.d.ts +5 -0
- package/dist/test/fixtures/routes/ssg/[slug].page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/ssg/server.page.d.ts +4 -0
- package/dist/test/fixtures/routes/ssg/server.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/state.page.d.ts +3 -0
- package/dist/test/fixtures/routes/state.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/throw.page.d.ts +3 -0
- package/dist/test/fixtures/routes/throw.page.d.ts.map +1 -0
- package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts +3 -0
- package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts.map +1 -0
- package/dist/test/helpers.d.ts +37 -0
- package/dist/test/helpers.d.ts.map +1 -0
- package/dist/test/layouts.test.d.ts +2 -0
- package/dist/test/layouts.test.d.ts.map +1 -0
- package/dist/test/middleware.test.d.ts +2 -0
- package/dist/test/middleware.test.d.ts.map +1 -0
- package/dist/test/multipart.test.d.ts +2 -0
- package/dist/test/multipart.test.d.ts.map +1 -0
- package/dist/test/oml-routing.test.d.ts +2 -0
- package/dist/test/oml-routing.test.d.ts.map +1 -0
- package/dist/test/oml.test.d.ts +2 -0
- package/dist/test/oml.test.d.ts.map +1 -0
- package/dist/test/redirects.test.d.ts +2 -0
- package/dist/test/redirects.test.d.ts.map +1 -0
- package/dist/test/routing.test.d.ts +2 -0
- package/dist/test/routing.test.d.ts.map +1 -0
- package/dist/test/ssg.test.d.ts +2 -0
- package/dist/test/ssg.test.d.ts.map +1 -0
- package/dist/test/web-response.test.d.ts +2 -0
- package/dist/test/web-response.test.d.ts.map +1 -0
- package/dist/types.d.ts +314 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +292 -0
- package/dist/types.js.map +7 -0
- package/package.json +103 -0
- package/pka.config.json +32 -0
- package/src/build/config.ts +42 -0
- package/src/build/index.ts +6 -0
- package/src/build/plugins.ts +118 -0
- package/src/cli.ts +502 -0
- package/src/config.ts +197 -0
- package/src/create-multisite.ts +310 -0
- package/src/create.ts +194 -0
- package/src/dev/blueprints.ts +75 -0
- package/src/dev/components.ts +108 -0
- package/src/dev/insert.ts +221 -0
- package/src/dev/remove.ts +677 -0
- package/src/dev/watch.ts +3098 -0
- package/src/env.d.ts +5 -0
- package/src/errors.ts +64 -0
- package/src/generate.ts +228 -0
- package/src/index.ts +67 -0
- package/src/island.ts +47 -0
- package/src/jsx-runtime.d.ts +408 -0
- package/src/jsx-runtime.d.ts.map +1 -0
- package/src/jsx-runtime.ts +536 -0
- package/src/link.ts +49 -0
- package/src/oml/fragment.ts +54 -0
- package/src/oml/index.ts +21 -0
- package/src/oml/jsx-runtime.ts +121 -0
- package/src/oml/jsx.ts +151 -0
- package/src/oml/page.ts +13 -0
- package/src/oml/render.ts +181 -0
- package/src/oml/types.ts +159 -0
- package/src/router/handler.ts +515 -0
- package/src/router/matcher.ts +52 -0
- package/src/router/scanner.ts +272 -0
- package/src/server/index.ts +49 -0
- package/src/signal.ts +39 -0
- package/src/ssg.ts +253 -0
- package/src/test/actions.test.ts +40 -0
- package/src/test/body-limits.test.ts +83 -0
- package/src/test/errors.test.ts +53 -0
- package/src/test/fixtures/routes/[id].page.ts +3 -0
- package/src/test/fixtures/routes/_error.ts +6 -0
- package/src/test/fixtures/routes/_global.ts +8 -0
- package/src/test/fixtures/routes/_layout-template.ts +7 -0
- package/src/test/fixtures/routes/_layout.ts +7 -0
- package/src/test/fixtures/routes/_layout_scripts.ts +8 -0
- package/src/test/fixtures/routes/_middleware.ts +8 -0
- package/src/test/fixtures/routes/_redirect301_mw.ts +5 -0
- package/src/test/fixtures/routes/_redirect_mw.ts +5 -0
- package/src/test/fixtures/routes/about.page.ts +6 -0
- package/src/test/fixtures/routes/action.page.ts +11 -0
- package/src/test/fixtures/routes/api/form-all.post.ts +5 -0
- package/src/test/fixtures/routes/api/form-limited.post.ts +6 -0
- package/src/test/fixtures/routes/api/response-obj.get.ts +17 -0
- package/src/test/fixtures/routes/api/upload.post.ts +14 -0
- package/src/test/fixtures/routes/api/users.get.ts +3 -0
- package/src/test/fixtures/routes/api/xml.get.ts +5 -0
- package/src/test/fixtures/routes/auth/_middleware.ts +11 -0
- package/src/test/fixtures/routes/auth/protected.page.ts +3 -0
- package/src/test/fixtures/routes/index.page.ts +3 -0
- package/src/test/fixtures/routes/oml.page.ts +7 -0
- package/src/test/fixtures/routes/redirect.page.ts +3 -0
- package/src/test/fixtures/routes/ssg/[slug].page.ts +8 -0
- package/src/test/fixtures/routes/ssg/server.page.ts +5 -0
- package/src/test/fixtures/routes/state.page.ts +4 -0
- package/src/test/fixtures/routes/throw.page.ts +5 -0
- package/src/test/fixtures/routes/wiki/[...slug].page.ts +3 -0
- package/src/test/helpers.ts +132 -0
- package/src/test/layouts.test.ts +76 -0
- package/src/test/middleware.test.ts +69 -0
- package/src/test/multipart.test.ts +91 -0
- package/src/test/oml-routing.test.ts +59 -0
- package/src/test/oml.test.ts +429 -0
- package/src/test/redirects.test.ts +32 -0
- package/src/test/routing.test.ts +118 -0
- package/src/test/ssg.test.ts +273 -0
- package/src/test/web-response.test.ts +33 -0
- package/src/types.ts +670 -0
- package/tsconfig.client.json +17 -0
- package/tsconfig.json +20 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import { createReadStream, existsSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { extname, join, resolve } from 'node:path'
|
|
5
|
+
import { collectCss, generateIslandsEntry, islandServerPlugin } from './build/plugins.js'
|
|
6
|
+
import { collectEsbuildPlugins, collectScannerSuffixes, pathsToAlias } from './config.js'
|
|
7
|
+
import { handleBuildError } from './errors.js'
|
|
8
|
+
|
|
9
|
+
const [, , command, ...args] = process.argv
|
|
10
|
+
const cwd = process.cwd()
|
|
11
|
+
|
|
12
|
+
switch (command) {
|
|
13
|
+
case 'create':
|
|
14
|
+
await runCreate()
|
|
15
|
+
break
|
|
16
|
+
case 'create-multisite':
|
|
17
|
+
await runCreateMultisite()
|
|
18
|
+
break
|
|
19
|
+
case 'generate':
|
|
20
|
+
await runGenerate()
|
|
21
|
+
break
|
|
22
|
+
case 'dev':
|
|
23
|
+
await runDev()
|
|
24
|
+
break
|
|
25
|
+
case 'build':
|
|
26
|
+
await runBuild()
|
|
27
|
+
break
|
|
28
|
+
case 'static':
|
|
29
|
+
await runStatic()
|
|
30
|
+
break
|
|
31
|
+
case 'start':
|
|
32
|
+
await runStart()
|
|
33
|
+
break
|
|
34
|
+
case 'preview':
|
|
35
|
+
await runPreview()
|
|
36
|
+
break
|
|
37
|
+
default:
|
|
38
|
+
console.error(`davaux: unknown command "${command ?? ''}"`)
|
|
39
|
+
console.error(
|
|
40
|
+
' Usage: davaux <create|create-multisite|generate|dev|build|start|static|preview> [options]',
|
|
41
|
+
)
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function runCreate() {
|
|
46
|
+
const { scaffold } = await import('./create.js')
|
|
47
|
+
await scaffold(args[0], cwd)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runCreateMultisite() {
|
|
51
|
+
const { scaffoldMultisite } = await import('./create-multisite.js')
|
|
52
|
+
await scaffoldMultisite(args[0], cwd)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function runGenerate() {
|
|
56
|
+
const { generate } = await import('./generate.js')
|
|
57
|
+
generate(args, cwd)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function runDev() {
|
|
61
|
+
loadEnv(cwd)
|
|
62
|
+
const serverSrc = resolve(cwd, 'server.ts')
|
|
63
|
+
if (existsSync(serverSrc)) {
|
|
64
|
+
console.warn('[davaux] server.ts detected — custom server entry is not used in dev mode')
|
|
65
|
+
console.warn(
|
|
66
|
+
' Use "node --watch --import tsx/esm server.ts" to develop with your custom server',
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
const { startDev } = await import('./dev/watch.js')
|
|
70
|
+
const { loadConfig } = await import('./build/config.js')
|
|
71
|
+
const config = await loadConfig(cwd)
|
|
72
|
+
const middlewareSrc = resolve(cwd, 'src', 'middleware.ts')
|
|
73
|
+
await startDev({
|
|
74
|
+
cwd,
|
|
75
|
+
port: Number(getFlag('--port', args) ?? config.server?.port ?? 3000),
|
|
76
|
+
hostname: getFlag('--hostname', args) ?? config.server?.hostname ?? 'localhost',
|
|
77
|
+
paths: config.paths,
|
|
78
|
+
plugins: config.plugins ?? [],
|
|
79
|
+
external: config.external,
|
|
80
|
+
middlewareSrc: existsSync(middlewareSrc) ? middlewareSrc : undefined,
|
|
81
|
+
editor: config.editor,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── Build ────────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
async function runBuild() {
|
|
88
|
+
rmSync(resolve(cwd, 'dist'), { recursive: true, force: true })
|
|
89
|
+
|
|
90
|
+
const { build } = await import('esbuild')
|
|
91
|
+
const { loadConfig } = await import('./build/config.js')
|
|
92
|
+
const config = await loadConfig(cwd)
|
|
93
|
+
const davauxPlugins = config.plugins ?? []
|
|
94
|
+
const extraPlugins = collectEsbuildPlugins(davauxPlugins)
|
|
95
|
+
const extraSuffixes = collectScannerSuffixes(davauxPlugins)
|
|
96
|
+
const userAlias = pathsToAlias(config.paths ?? {})
|
|
97
|
+
const routesDir = resolve(cwd, 'src', 'routes')
|
|
98
|
+
const islandsDir = resolve(cwd, 'src', 'islands')
|
|
99
|
+
|
|
100
|
+
if (!existsSync(routesDir)) {
|
|
101
|
+
console.error(`[davaux] No routes directory found at ${routesDir}`)
|
|
102
|
+
process.exit(1)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { scanRoutes } = await import('./router/scanner.js')
|
|
106
|
+
const { scanIslands } = await import('./router/scanner.js')
|
|
107
|
+
const { routes, layouts, middlewares, errorPage } = await scanRoutes(routesDir, extraSuffixes)
|
|
108
|
+
const islands = await scanIslands(islandsDir)
|
|
109
|
+
|
|
110
|
+
const outDir = resolve(cwd, 'dist')
|
|
111
|
+
const serverEntries = [
|
|
112
|
+
...routes.map((r) => r.filePath),
|
|
113
|
+
...layouts.map((l) => l.filePath),
|
|
114
|
+
...middlewares.map((m) => m.filePath),
|
|
115
|
+
...(errorPage ? [errorPage] : []),
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
const serverExternal = ['node:*', 'davaux', ...(config.external ?? [])]
|
|
119
|
+
|
|
120
|
+
if (serverEntries.length === 0) {
|
|
121
|
+
console.log('[davaux] No routes found — nothing to build.')
|
|
122
|
+
} else {
|
|
123
|
+
await build({
|
|
124
|
+
entryPoints: serverEntries,
|
|
125
|
+
outdir: outDir,
|
|
126
|
+
outbase: routesDir,
|
|
127
|
+
format: 'esm',
|
|
128
|
+
platform: 'node',
|
|
129
|
+
target: 'node22',
|
|
130
|
+
bundle: true,
|
|
131
|
+
jsx: 'automatic',
|
|
132
|
+
jsxImportSource: 'davaux',
|
|
133
|
+
external: serverExternal,
|
|
134
|
+
alias: { ...userAlias, 'davaux/client': 'davaux/signal' },
|
|
135
|
+
sourcemap: true,
|
|
136
|
+
plugins: [islandServerPlugin(islandsDir), ...extraPlugins],
|
|
137
|
+
}).catch((err) => handleBuildError('Server build', err))
|
|
138
|
+
console.log(
|
|
139
|
+
`[davaux] Built ${routes.length} route(s) and ${layouts.length} layout(s) to ${outDir}`,
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Build src/middleware.ts separately if it exists (not under routesDir, so needs its own outfile)
|
|
144
|
+
const middlewareSrc = resolve(cwd, 'src', 'middleware.ts')
|
|
145
|
+
if (existsSync(middlewareSrc)) {
|
|
146
|
+
await build({
|
|
147
|
+
entryPoints: [middlewareSrc],
|
|
148
|
+
outfile: resolve(outDir, 'middleware.js'),
|
|
149
|
+
format: 'esm',
|
|
150
|
+
platform: 'node',
|
|
151
|
+
target: 'node22',
|
|
152
|
+
bundle: true,
|
|
153
|
+
jsx: 'automatic',
|
|
154
|
+
jsxImportSource: 'davaux',
|
|
155
|
+
external: serverExternal,
|
|
156
|
+
alias: { ...userAlias, 'davaux/client': 'davaux/signal' },
|
|
157
|
+
sourcemap: true,
|
|
158
|
+
}).catch((err) => handleBuildError('Middleware build', err))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const clientScripts: string[] = []
|
|
162
|
+
|
|
163
|
+
// Islands client bundle (auto-generated from scanned islands)
|
|
164
|
+
if (islands.length > 0) {
|
|
165
|
+
const islandsOutFile = resolve(cwd, 'dist', '_davaux', 'islands.js')
|
|
166
|
+
await build({
|
|
167
|
+
stdin: {
|
|
168
|
+
contents: generateIslandsEntry(islands),
|
|
169
|
+
loader: 'ts',
|
|
170
|
+
resolveDir: cwd,
|
|
171
|
+
},
|
|
172
|
+
outfile: islandsOutFile,
|
|
173
|
+
format: 'esm',
|
|
174
|
+
platform: 'browser',
|
|
175
|
+
target: 'es2022',
|
|
176
|
+
bundle: true,
|
|
177
|
+
jsx: 'automatic',
|
|
178
|
+
jsxImportSource: 'davaux/client',
|
|
179
|
+
alias: userAlias,
|
|
180
|
+
tsconfigRaw: JSON.stringify({
|
|
181
|
+
compilerOptions: { jsx: 'react-jsx', jsxImportSource: 'davaux/client' },
|
|
182
|
+
}),
|
|
183
|
+
sourcemap: true,
|
|
184
|
+
plugins: [...extraPlugins],
|
|
185
|
+
}).catch((err) => handleBuildError('Islands build', err))
|
|
186
|
+
clientScripts.push('/_davaux/islands.js')
|
|
187
|
+
console.log(`[davaux] Built islands bundle (${islands.length} island(s))`)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Optional user-authored client bundle
|
|
191
|
+
const clientEntry = resolve(cwd, 'src', 'client.ts')
|
|
192
|
+
if (existsSync(clientEntry)) {
|
|
193
|
+
const clientOutFile = resolve(cwd, 'dist', '_davaux', 'client.js')
|
|
194
|
+
await build({
|
|
195
|
+
entryPoints: [clientEntry],
|
|
196
|
+
outfile: clientOutFile,
|
|
197
|
+
format: 'esm',
|
|
198
|
+
platform: 'browser',
|
|
199
|
+
target: 'es2022',
|
|
200
|
+
bundle: true,
|
|
201
|
+
jsx: 'automatic',
|
|
202
|
+
jsxImportSource: 'davaux/client',
|
|
203
|
+
alias: userAlias,
|
|
204
|
+
tsconfigRaw: JSON.stringify({
|
|
205
|
+
compilerOptions: { jsx: 'react-jsx', jsxImportSource: 'davaux/client' },
|
|
206
|
+
}),
|
|
207
|
+
sourcemap: true,
|
|
208
|
+
plugins: [...extraPlugins],
|
|
209
|
+
}).catch((err) => handleBuildError('Client build', err))
|
|
210
|
+
clientScripts.push('/_davaux/client.js')
|
|
211
|
+
console.log(`[davaux] Built client bundle to ${clientOutFile}`)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Collect CSS side-effect outputs from all builds into a single stylesheet
|
|
215
|
+
const stylesOutFile = resolve(cwd, 'dist', '_davaux', 'styles.css')
|
|
216
|
+
const hasCss = await collectCss(outDir, stylesOutFile, true)
|
|
217
|
+
if (hasCss) console.log('[davaux] Collected CSS into dist/_davaux/styles.css')
|
|
218
|
+
|
|
219
|
+
// Copy partial-updates polyfills (self-detecting — no-op when native support lands)
|
|
220
|
+
try {
|
|
221
|
+
const r = createRequire(import.meta.url)
|
|
222
|
+
const templateForCjs = r.resolve('template-for-polyfill')
|
|
223
|
+
const templateForJs = readFileSync(
|
|
224
|
+
templateForCjs.replace(/template-for-polyfill\.cjs$/, 'template-for-polyfill.js'),
|
|
225
|
+
'utf-8',
|
|
226
|
+
)
|
|
227
|
+
const htmlSettersMain = r.resolve('html-setters-polyfill')
|
|
228
|
+
const htmlSettersJs = readFileSync(
|
|
229
|
+
htmlSettersMain.replace(/index\.js$/, 'index.min.js'),
|
|
230
|
+
'utf-8',
|
|
231
|
+
)
|
|
232
|
+
writeFileSync(
|
|
233
|
+
resolve(cwd, 'dist', '_davaux', 'partial-updates.js'),
|
|
234
|
+
`${templateForJs}\n${htmlSettersJs}`,
|
|
235
|
+
)
|
|
236
|
+
} catch {
|
|
237
|
+
console.warn('[davaux] Could not copy partial-updates polyfills — install template-for-polyfill and html-setters-polyfill if you use ctx.defer()')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Compile server.ts if present — enables custom server entry (HTTPS, clustering, etc.)
|
|
241
|
+
const serverSrc = resolve(cwd, 'server.ts')
|
|
242
|
+
if (existsSync(serverSrc)) {
|
|
243
|
+
await build({
|
|
244
|
+
entryPoints: [serverSrc],
|
|
245
|
+
outfile: resolve(outDir, 'server.js'),
|
|
246
|
+
format: 'esm',
|
|
247
|
+
platform: 'node',
|
|
248
|
+
target: 'node22',
|
|
249
|
+
bundle: true,
|
|
250
|
+
external: [...serverExternal, 'davaux/*'],
|
|
251
|
+
sourcemap: true,
|
|
252
|
+
}).catch((err) => handleBuildError('Server entry build', err))
|
|
253
|
+
console.log('[davaux] Built server.ts → dist/server.js')
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ─── Static ───────────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
async function runStatic() {
|
|
260
|
+
loadEnv(cwd)
|
|
261
|
+
await runBuild()
|
|
262
|
+
|
|
263
|
+
const { generateStatic } = await import('./ssg.js')
|
|
264
|
+
const { scanRoutes } = await import('./router/scanner.js')
|
|
265
|
+
const { loadConfig } = await import('./build/config.js')
|
|
266
|
+
const config = await loadConfig(cwd)
|
|
267
|
+
|
|
268
|
+
const distDir = resolve(cwd, 'dist')
|
|
269
|
+
const publicDir = resolve(cwd, 'public')
|
|
270
|
+
const outDir = resolve(cwd, getFlag('--out', args) ?? config.ssg?.outDir ?? 'out')
|
|
271
|
+
|
|
272
|
+
const islandsFile = resolve(distDir, '_davaux', 'islands.js')
|
|
273
|
+
const clientFile = resolve(distDir, '_davaux', 'client.js')
|
|
274
|
+
const stylesFile = resolve(distDir, '_davaux', 'styles.css')
|
|
275
|
+
|
|
276
|
+
const clientScripts: string[] = []
|
|
277
|
+
if (existsSync(islandsFile)) clientScripts.push('/_davaux/islands.js')
|
|
278
|
+
if (existsSync(clientFile)) clientScripts.push('/_davaux/client.js')
|
|
279
|
+
|
|
280
|
+
const clientStylesheets: string[] = []
|
|
281
|
+
if (existsSync(stylesFile)) clientStylesheets.push('/_davaux/styles.css')
|
|
282
|
+
|
|
283
|
+
const scan = await scanRoutes(distDir)
|
|
284
|
+
|
|
285
|
+
const middlewareDist = resolve(distDir, 'middleware.js')
|
|
286
|
+
await generateStatic({
|
|
287
|
+
outDir,
|
|
288
|
+
distDir,
|
|
289
|
+
publicDir,
|
|
290
|
+
scan,
|
|
291
|
+
clientScripts,
|
|
292
|
+
clientStylesheets,
|
|
293
|
+
appMiddlewarePath: existsSync(middlewareDist) ? middlewareDist : undefined,
|
|
294
|
+
trailingSlash: config.ssg?.trailingSlash,
|
|
295
|
+
basePath: config.ssg?.basePath,
|
|
296
|
+
notFound: config.ssg?.notFound,
|
|
297
|
+
sitemap: config.ssg?.sitemap,
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─── Preview ─────────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
async function runPreview() {
|
|
304
|
+
const { loadConfig } = await import('./build/config.js')
|
|
305
|
+
const config = await loadConfig(cwd)
|
|
306
|
+
const outDir = resolve(cwd, getFlag('--dir', args) ?? config.ssg?.outDir ?? 'out')
|
|
307
|
+
const port = Number(getFlag('--port', args) ?? config.server?.port ?? 4173)
|
|
308
|
+
const hostname = getFlag('--hostname', args) ?? 'localhost'
|
|
309
|
+
|
|
310
|
+
if (!existsSync(outDir)) {
|
|
311
|
+
console.error(`[davaux] Preview directory not found: ${outDir}`)
|
|
312
|
+
console.error(' Run "davaux static" first to generate the output.')
|
|
313
|
+
process.exit(1)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function serveFile(
|
|
317
|
+
res: import('node:http').ServerResponse,
|
|
318
|
+
filePath: string,
|
|
319
|
+
status = 200,
|
|
320
|
+
): boolean {
|
|
321
|
+
if (existsSync(filePath) && statSync(filePath).isFile()) {
|
|
322
|
+
res.writeHead(status, {
|
|
323
|
+
'Content-Type': MIME[extname(filePath)] ?? 'application/octet-stream',
|
|
324
|
+
})
|
|
325
|
+
createReadStream(filePath).pipe(res)
|
|
326
|
+
return true
|
|
327
|
+
}
|
|
328
|
+
return false
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const server = createServer((req, res) => {
|
|
332
|
+
const pathname = new URL(req.url ?? '/', 'http://x').pathname
|
|
333
|
+
const safePath = join(outDir, pathname)
|
|
334
|
+
|
|
335
|
+
if (!safePath.startsWith(`${outDir}/`) && safePath !== outDir) {
|
|
336
|
+
res.writeHead(403, { 'Content-Type': 'text/plain' })
|
|
337
|
+
res.end('Forbidden')
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (serveFile(res, safePath)) return
|
|
342
|
+
if (serveFile(res, join(safePath, 'index.html'))) return
|
|
343
|
+
|
|
344
|
+
const notFoundPage = join(outDir, '404.html')
|
|
345
|
+
if (serveFile(res, notFoundPage, 404)) return
|
|
346
|
+
|
|
347
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
348
|
+
res.end('Not found')
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
server.listen(port, hostname, () => {
|
|
352
|
+
console.log(`\n davaux preview http://${hostname}:${port}`)
|
|
353
|
+
console.log(` Serving: ${outDir}\n`)
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ─── Start ────────────────────────────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
const MIME: Record<string, string> = {
|
|
360
|
+
'.js': 'application/javascript',
|
|
361
|
+
'.mjs': 'application/javascript',
|
|
362
|
+
'.css': 'text/css',
|
|
363
|
+
'.html': 'text/html',
|
|
364
|
+
'.json': 'application/json',
|
|
365
|
+
'.png': 'image/png',
|
|
366
|
+
'.jpg': 'image/jpeg',
|
|
367
|
+
'.jpeg': 'image/jpeg',
|
|
368
|
+
'.gif': 'image/gif',
|
|
369
|
+
'.svg': 'image/svg+xml',
|
|
370
|
+
'.ico': 'image/x-icon',
|
|
371
|
+
'.woff': 'font/woff',
|
|
372
|
+
'.woff2': 'font/woff2',
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function runStart() {
|
|
376
|
+
loadEnv(cwd)
|
|
377
|
+
const distDir = resolve(cwd, 'dist')
|
|
378
|
+
const publicDir = resolve(cwd, 'public')
|
|
379
|
+
const islandsFile = resolve(cwd, 'dist', '_davaux', 'islands.js')
|
|
380
|
+
const clientFile = resolve(cwd, 'dist', '_davaux', 'client.js')
|
|
381
|
+
const polyfillFile = resolve(cwd, 'dist', '_davaux', 'partial-updates.js')
|
|
382
|
+
|
|
383
|
+
const { loadConfig } = await import('./build/config.js')
|
|
384
|
+
const { scanRoutes } = await import('./router/scanner.js')
|
|
385
|
+
const { buildApp } = await import('./router/handler.js')
|
|
386
|
+
const { startServer } = await import('./server/index.js')
|
|
387
|
+
|
|
388
|
+
const [config, scanResult] = await Promise.all([loadConfig(cwd), scanRoutes(distDir)])
|
|
389
|
+
|
|
390
|
+
const stylesFile = resolve(cwd, 'dist', '_davaux', 'styles.css')
|
|
391
|
+
const middlewareDist = resolve(distDir, 'middleware.js')
|
|
392
|
+
const clientScripts: string[] = []
|
|
393
|
+
if (existsSync(islandsFile)) clientScripts.push('/_davaux/islands.js')
|
|
394
|
+
if (existsSync(clientFile)) clientScripts.push('/_davaux/client.js')
|
|
395
|
+
const clientStylesheets: string[] = []
|
|
396
|
+
if (existsSync(stylesFile)) clientStylesheets.push('/_davaux/styles.css')
|
|
397
|
+
|
|
398
|
+
const app = buildApp(
|
|
399
|
+
scanResult,
|
|
400
|
+
false,
|
|
401
|
+
clientScripts,
|
|
402
|
+
clientStylesheets,
|
|
403
|
+
existsSync(middlewareDist) ? middlewareDist : undefined,
|
|
404
|
+
'',
|
|
405
|
+
false,
|
|
406
|
+
config.oml?.cache,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
const serveStatics = (req: IncomingMessage, res: ServerResponse): Promise<boolean> => {
|
|
410
|
+
const path = req.url?.split('?')[0] ?? '/'
|
|
411
|
+
|
|
412
|
+
if (path === '/_davaux/styles.css' && existsSync(stylesFile)) {
|
|
413
|
+
res.writeHead(200, { 'Content-Type': 'text/css' })
|
|
414
|
+
createReadStream(stylesFile).pipe(res)
|
|
415
|
+
return Promise.resolve(true)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (path === '/_davaux/islands.js' && existsSync(islandsFile)) {
|
|
419
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' })
|
|
420
|
+
createReadStream(islandsFile).pipe(res)
|
|
421
|
+
return Promise.resolve(true)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (path === '/_davaux/client.js' && existsSync(clientFile)) {
|
|
425
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' })
|
|
426
|
+
createReadStream(clientFile).pipe(res)
|
|
427
|
+
return Promise.resolve(true)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (path === '/_davaux/partial-updates.js' && existsSync(polyfillFile)) {
|
|
431
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' })
|
|
432
|
+
createReadStream(polyfillFile).pipe(res)
|
|
433
|
+
return Promise.resolve(true)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (existsSync(publicDir)) {
|
|
437
|
+
const filePath = join(publicDir, new URL(path, 'http://x').pathname)
|
|
438
|
+
if (
|
|
439
|
+
(filePath.startsWith(`${publicDir}/`) || filePath === publicDir) &&
|
|
440
|
+
existsSync(filePath) &&
|
|
441
|
+
statSync(filePath).isFile()
|
|
442
|
+
) {
|
|
443
|
+
res.writeHead(200, {
|
|
444
|
+
'Content-Type': MIME[extname(filePath)] ?? 'application/octet-stream',
|
|
445
|
+
})
|
|
446
|
+
createReadStream(filePath).pipe(res)
|
|
447
|
+
return Promise.resolve(true)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return Promise.resolve(false)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const serverDist = resolve(distDir, 'server.js')
|
|
455
|
+
if (existsSync(serverDist)) {
|
|
456
|
+
const serverMod = await import(serverDist)
|
|
457
|
+
if (typeof serverMod.default !== 'function') {
|
|
458
|
+
console.error(
|
|
459
|
+
'[davaux] dist/server.js must export a default function: (app: CompiledApp, serveStatics) => void',
|
|
460
|
+
)
|
|
461
|
+
process.exit(1)
|
|
462
|
+
}
|
|
463
|
+
await serverMod.default(app, serveStatics)
|
|
464
|
+
} else {
|
|
465
|
+
startServer(app, {
|
|
466
|
+
port: Number(getFlag('--port', args) ?? config.server?.port ?? 3000),
|
|
467
|
+
hostname: getFlag('--hostname', args) ?? config.server?.hostname ?? 'localhost',
|
|
468
|
+
onRequest: serveStatics,
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
474
|
+
|
|
475
|
+
function getFlag(name: string, argv: string[]): string | undefined {
|
|
476
|
+
const idx = argv.indexOf(name)
|
|
477
|
+
return idx !== -1 ? argv[idx + 1] : undefined
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function loadEnv(cwd: string): void {
|
|
481
|
+
for (const name of ['.env', '.env.local']) {
|
|
482
|
+
const filePath = resolve(cwd, name)
|
|
483
|
+
if (!existsSync(filePath)) continue
|
|
484
|
+
const content = readFileSync(filePath, 'utf-8')
|
|
485
|
+
for (const line of content.split('\n')) {
|
|
486
|
+
const trimmed = line.trim()
|
|
487
|
+
if (!trimmed || trimmed.startsWith('#')) continue
|
|
488
|
+
const eq = trimmed.indexOf('=')
|
|
489
|
+
if (eq === -1) continue
|
|
490
|
+
const key = trimmed.slice(0, eq).trim()
|
|
491
|
+
if (!key) continue
|
|
492
|
+
let value = trimmed.slice(eq + 1).trim()
|
|
493
|
+
if (
|
|
494
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
495
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
496
|
+
) {
|
|
497
|
+
value = value.slice(1, -1)
|
|
498
|
+
}
|
|
499
|
+
if (!(key in process.env)) process.env[key] = value
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { Plugin } from 'esbuild'
|
|
2
|
+
import type { RouteType } from './types.js'
|
|
3
|
+
|
|
4
|
+
// ─── Plugin API ───────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A Davaux plugin — extends the framework with new route file types and/or
|
|
8
|
+
* esbuild transforms. Implement this interface and return it from a factory
|
|
9
|
+
* function, then pass it to `plugins` in `davaux.config.ts`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // packages/my-plugin/src/index.ts
|
|
13
|
+
* import type { DavauxPlugin } from 'davaux/config'
|
|
14
|
+
* export function myPlugin(): DavauxPlugin {
|
|
15
|
+
* return {
|
|
16
|
+
* name: 'my-plugin',
|
|
17
|
+
* esbuild: [myEsbuildPlugin()],
|
|
18
|
+
* scanner: { suffixes: [['.page.myext', 'page']] },
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
export interface DavauxPlugin {
|
|
23
|
+
name: string
|
|
24
|
+
/** esbuild plugins contributed to every build context (server routes, islands, client) */
|
|
25
|
+
esbuild?: Plugin[]
|
|
26
|
+
/** Additional route file suffixes the scanner should recognize */
|
|
27
|
+
scanner?: {
|
|
28
|
+
suffixes: [suffix: string, type: RouteType][]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Collect esbuild plugins from an array of DavauxPlugins. */
|
|
33
|
+
export function collectEsbuildPlugins(plugins: DavauxPlugin[] = []): Plugin[] {
|
|
34
|
+
return plugins.flatMap((p) => p.esbuild ?? [])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Collect scanner suffix entries from an array of DavauxPlugins. */
|
|
38
|
+
export function collectScannerSuffixes(plugins: DavauxPlugin[] = []): [string, RouteType][] {
|
|
39
|
+
return plugins.flatMap((p) => p.scanner?.suffixes ?? [])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── User-facing config type ──────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export interface EditorConfig {
|
|
45
|
+
/**
|
|
46
|
+
* Opt-in to the visual editor and inspector overlay.
|
|
47
|
+
* Must be explicitly set to `true` — disabled by default so the editor
|
|
48
|
+
* endpoints and inspector script are never exposed unless intended.
|
|
49
|
+
*/
|
|
50
|
+
enabled?: boolean
|
|
51
|
+
/** Floating inspector/editor badge options. */
|
|
52
|
+
badge?: {
|
|
53
|
+
/** Corner of the viewport where the badge appears. Default: `'bottom-right'` */
|
|
54
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
|
|
55
|
+
/** Custom label text shown on the badge. Default: `'Inspector'` */
|
|
56
|
+
label?: string
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Force a specific colour scheme for the editor UI instead of auto-detecting
|
|
60
|
+
* from the inspected page's theme attribute.
|
|
61
|
+
*/
|
|
62
|
+
theme?: 'auto' | 'dark' | 'light'
|
|
63
|
+
/**
|
|
64
|
+
* Raw CSS string injected into the editor frame's `<style>` tag.
|
|
65
|
+
* Use this to override colours, hide elements, or adjust layout.
|
|
66
|
+
*/
|
|
67
|
+
css?: string
|
|
68
|
+
/**
|
|
69
|
+
* Path to a CSS file (relative to the project root) whose contents are
|
|
70
|
+
* injected into the editor frame. Merged with `css` if both are provided.
|
|
71
|
+
* The file is re-read on each request in dev mode, so changes apply without a restart.
|
|
72
|
+
*/
|
|
73
|
+
cssFile?: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DavauxConfig {
|
|
77
|
+
/** Server defaults for `davaux dev`, `davaux start`, and `davaux preview`. CLI flags (--port, --hostname) take priority over these. */
|
|
78
|
+
server?: {
|
|
79
|
+
port?: number
|
|
80
|
+
hostname?: string
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Import path aliases, using the same glob syntax as `tsconfig.json` `paths`.
|
|
84
|
+
* Each entry is forwarded to esbuild `alias` in every build context so TypeScript
|
|
85
|
+
* and the bundler stay in sync without any extra plugin.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* import { defineConfig } from 'davaux/config'
|
|
89
|
+
* export default defineConfig({
|
|
90
|
+
* paths: { '@/*': './src/*' },
|
|
91
|
+
* })
|
|
92
|
+
*/
|
|
93
|
+
paths?: Record<string, string>
|
|
94
|
+
/**
|
|
95
|
+
* Davaux plugins — each plugin can contribute esbuild transforms and/or
|
|
96
|
+
* register new route file extensions with the scanner.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* import { defineConfig } from 'davaux/config'
|
|
100
|
+
* import { markdown } from '@davaux/markdown'
|
|
101
|
+
* export default defineConfig({
|
|
102
|
+
* plugins: [markdown()],
|
|
103
|
+
* })
|
|
104
|
+
*/
|
|
105
|
+
plugins?: DavauxPlugin[]
|
|
106
|
+
/**
|
|
107
|
+
* Additional packages to exclude from esbuild bundling. Useful for packages
|
|
108
|
+
* that use native bindings or CJS `require()` that cannot be bundled into ESM.
|
|
109
|
+
* Each entry is added to the esbuild `external` list alongside `node:*` and `davaux`.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* import { defineConfig } from 'davaux/config'
|
|
113
|
+
* export default defineConfig({
|
|
114
|
+
* external: ['@libsql/client', 'libsql'],
|
|
115
|
+
* })
|
|
116
|
+
*/
|
|
117
|
+
external?: string[]
|
|
118
|
+
/** Visual editor and inspector overlay configuration. Disabled by default — set `enabled: true` to opt in. */
|
|
119
|
+
editor?: EditorConfig
|
|
120
|
+
/**
|
|
121
|
+
* OML (Object Markup Language) options. OML is the intermediate representation
|
|
122
|
+
* used by the visual editor and production page caching.
|
|
123
|
+
*/
|
|
124
|
+
oml?: {
|
|
125
|
+
/**
|
|
126
|
+
* Control which routes use OML tree construction and result caching in production.
|
|
127
|
+
* By default no routes are cached — this must be explicitly configured.
|
|
128
|
+
*
|
|
129
|
+
* Use `include` for apps that are mostly dynamic with a few cacheable routes,
|
|
130
|
+
* or `exclude` for apps that are mostly static with a few dynamic routes.
|
|
131
|
+
* Routes not covered by the cache config use the string renderer directly in
|
|
132
|
+
* production — no OML tree is built and no result is stored.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Whitelist — only these routes are cached
|
|
136
|
+
* oml: { cache: { include: ['/', '/blog/:slug', '/docs/*'] } }
|
|
137
|
+
*
|
|
138
|
+
* // Blacklist — all routes cached except these
|
|
139
|
+
* oml: { cache: { exclude: ['/dashboard', '/account/*'] } }
|
|
140
|
+
*/
|
|
141
|
+
cache?: OmlCacheConfig
|
|
142
|
+
}
|
|
143
|
+
/** Options that only apply to `davaux static` (static site generation). */
|
|
144
|
+
ssg?: {
|
|
145
|
+
/**
|
|
146
|
+
* Override the default `out/` output directory.
|
|
147
|
+
* The `--out` CLI flag takes priority over this value.
|
|
148
|
+
*/
|
|
149
|
+
outDir?: string
|
|
150
|
+
/**
|
|
151
|
+
* Trailing slash behaviour for generated output files.
|
|
152
|
+
* - `'always'` (default) — `/about` → `out/about/index.html` (served as `/about/`)
|
|
153
|
+
* - `'never'` — `/about` → `out/about.html` (served as `/about`)
|
|
154
|
+
*/
|
|
155
|
+
trailingSlash?: 'always' | 'never'
|
|
156
|
+
/**
|
|
157
|
+
* Path prefix for subdirectory deployments (e.g. `'/docs'` for GitHub Pages
|
|
158
|
+
* at `user.github.io/my-project/`). Prepended to all injected `/_davaux/*`
|
|
159
|
+
* script and stylesheet URLs in the generated HTML.
|
|
160
|
+
*/
|
|
161
|
+
basePath?: string
|
|
162
|
+
/**
|
|
163
|
+
* Generate a `404.html` at the root of `outDir` for static hosts
|
|
164
|
+
* (Netlify, GitHub Pages) that serve it on unmatched paths.
|
|
165
|
+
* Defaults to `true` when an `_error.tsx` exists in the routes directory.
|
|
166
|
+
*/
|
|
167
|
+
notFound?: boolean
|
|
168
|
+
/**
|
|
169
|
+
* Auto-generate a `sitemap.xml` from all rendered routes.
|
|
170
|
+
* Pass `{ baseUrl: 'https://example.com' }` to enable.
|
|
171
|
+
* Pass `false` to explicitly disable.
|
|
172
|
+
*/
|
|
173
|
+
sitemap?: false | { baseUrl: string }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Convert tsconfig-style `paths` glob entries to esbuild `alias` entries.
|
|
179
|
+
* `'@/*': './src/*'` → `'@': './src'`
|
|
180
|
+
*/
|
|
181
|
+
export function pathsToAlias(paths: Record<string, string>): Record<string, string> {
|
|
182
|
+
return Object.fromEntries(
|
|
183
|
+
Object.entries(paths).map(([k, v]) => [k.replace(/\/\*$/, ''), v.replace(/\/\*$/, '')]),
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Controls which routes use OML tree construction and caching in production.
|
|
189
|
+
* Use `include` to whitelist specific URL patterns, or `exclude` to blacklist them.
|
|
190
|
+
* Patterns support `:param` (single dynamic segment) and `*` (any characters).
|
|
191
|
+
*/
|
|
192
|
+
export type OmlCacheConfig = { include: string[] } | { exclude: string[] }
|
|
193
|
+
|
|
194
|
+
/** Wraps a config object for TypeScript inference — returns it unchanged. */
|
|
195
|
+
export function defineConfig(config: DavauxConfig): DavauxConfig {
|
|
196
|
+
return config
|
|
197
|
+
}
|