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
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export async function scaffoldMultisite(name: string | undefined, cwd: string) {
|
|
5
|
+
if (!name) {
|
|
6
|
+
console.error('Usage: davaux create-multisite <project-name>')
|
|
7
|
+
process.exit(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!/^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/.test(name)) {
|
|
11
|
+
console.error(
|
|
12
|
+
`[davaux] Invalid project name "${name}". Use lowercase letters, numbers, hyphens, underscores, or dots.`,
|
|
13
|
+
)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const dir = resolve(cwd, name)
|
|
18
|
+
|
|
19
|
+
if (existsSync(dir)) {
|
|
20
|
+
console.error(`[davaux] Directory "${name}" already exists.`)
|
|
21
|
+
process.exit(1)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const title = name.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
|
|
25
|
+
|
|
26
|
+
mkdirSync(join(dir, 'src', 'routes'), { recursive: true })
|
|
27
|
+
mkdirSync(join(dir, 'sites', 'main', 'routes'), { recursive: true })
|
|
28
|
+
mkdirSync(join(dir, 'public'), { recursive: true })
|
|
29
|
+
|
|
30
|
+
write(dir, 'package.json', packageJson(name))
|
|
31
|
+
write(dir, 'tsconfig.json', tsConfig())
|
|
32
|
+
write(dir, 'biome.json', biomeConfig())
|
|
33
|
+
write(dir, 'multisite.config.ts', multisiteConfig())
|
|
34
|
+
write(dir, 'server.ts', serverEntry())
|
|
35
|
+
write(dir, 'build.ts', buildEntry())
|
|
36
|
+
write(dir, 'src/routes/_layout.tsx', baseLayout(title))
|
|
37
|
+
write(dir, 'src/routes/index.page.tsx', baseIndexPage(title))
|
|
38
|
+
write(dir, 'sites/main/routes/_layout.tsx', siteLayout())
|
|
39
|
+
write(dir, 'sites/main/routes/index.page.tsx', siteIndexPage(title))
|
|
40
|
+
|
|
41
|
+
console.log(`\n Created multisite project: ${name}\n`)
|
|
42
|
+
console.log(' Next steps:\n')
|
|
43
|
+
console.log(` cd ${name}`)
|
|
44
|
+
console.log(' npm install')
|
|
45
|
+
console.log(' npm run dev\n')
|
|
46
|
+
console.log(' Docs: https://davaux.codeberg.page/docs/packages/multisite\n')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function write(dir: string, file: string, content: string) {
|
|
50
|
+
writeFileSync(join(dir, file), content, 'utf8')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function packageJson(name: string) {
|
|
54
|
+
return (
|
|
55
|
+
JSON.stringify(
|
|
56
|
+
{
|
|
57
|
+
name,
|
|
58
|
+
version: '0.1.0',
|
|
59
|
+
type: 'module',
|
|
60
|
+
scripts: {
|
|
61
|
+
dev: 'node --watch --import tsx/esm server.ts',
|
|
62
|
+
build: 'tsx build.ts',
|
|
63
|
+
start: 'NODE_ENV=production node dist/server.js',
|
|
64
|
+
},
|
|
65
|
+
dependencies: {
|
|
66
|
+
'@davaux/multisite': 'latest',
|
|
67
|
+
davaux: 'latest',
|
|
68
|
+
},
|
|
69
|
+
devDependencies: {
|
|
70
|
+
'@biomejs/biome': 'latest',
|
|
71
|
+
'@types/node': 'latest',
|
|
72
|
+
tsx: 'latest',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
null,
|
|
76
|
+
2,
|
|
77
|
+
) + '\n'
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function tsConfig() {
|
|
82
|
+
return (
|
|
83
|
+
JSON.stringify(
|
|
84
|
+
{
|
|
85
|
+
compilerOptions: {
|
|
86
|
+
target: 'ESNext',
|
|
87
|
+
module: 'NodeNext',
|
|
88
|
+
moduleResolution: 'NodeNext',
|
|
89
|
+
jsx: 'react-jsx',
|
|
90
|
+
jsxImportSource: 'davaux',
|
|
91
|
+
strict: true,
|
|
92
|
+
lib: ['ESNext', 'DOM'],
|
|
93
|
+
allowImportingTsExtensions: true,
|
|
94
|
+
noEmit: true,
|
|
95
|
+
skipLibCheck: true,
|
|
96
|
+
types: ['node', 'davaux/env'],
|
|
97
|
+
},
|
|
98
|
+
include: ['src/**/*', 'sites/**/*', '*.ts'],
|
|
99
|
+
},
|
|
100
|
+
null,
|
|
101
|
+
2,
|
|
102
|
+
) + '\n'
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function biomeConfig() {
|
|
107
|
+
return (
|
|
108
|
+
JSON.stringify(
|
|
109
|
+
{
|
|
110
|
+
$schema: 'https://biomejs.dev/schemas/2.4.15/schema.json',
|
|
111
|
+
assist: { actions: { source: { organizeImports: 'on' } } },
|
|
112
|
+
linter: {
|
|
113
|
+
enabled: true,
|
|
114
|
+
rules: {
|
|
115
|
+
recommended: true,
|
|
116
|
+
correctness: {
|
|
117
|
+
noUnusedVariables: 'error',
|
|
118
|
+
noUnusedImports: 'error',
|
|
119
|
+
},
|
|
120
|
+
style: {
|
|
121
|
+
noNonNullAssertion: 'warn',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
formatter: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
indentStyle: 'space',
|
|
128
|
+
indentWidth: 2,
|
|
129
|
+
lineWidth: 100,
|
|
130
|
+
},
|
|
131
|
+
javascript: {
|
|
132
|
+
formatter: {
|
|
133
|
+
quoteStyle: 'single',
|
|
134
|
+
trailingCommas: 'all',
|
|
135
|
+
semicolons: 'asNeeded',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
css: {
|
|
139
|
+
formatter: {
|
|
140
|
+
enabled: true,
|
|
141
|
+
quoteStyle: 'single',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
files: {
|
|
145
|
+
includes: ['src/**', 'sites/**', '*.ts'],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
null,
|
|
149
|
+
2,
|
|
150
|
+
) + '\n'
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function multisiteConfig() {
|
|
155
|
+
return `import { join } from 'node:path'
|
|
156
|
+
import { fileURLToPath } from 'node:url'
|
|
157
|
+
import { defineSites } from '@davaux/multisite'
|
|
158
|
+
|
|
159
|
+
const root = fileURLToPath(new URL('.', import.meta.url))
|
|
160
|
+
|
|
161
|
+
export interface SiteConfig {
|
|
162
|
+
name: string
|
|
163
|
+
primaryColor: string
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const sites = defineSites<SiteConfig>({
|
|
167
|
+
baseDir: join(root, 'src/routes'),
|
|
168
|
+
sites: [
|
|
169
|
+
{
|
|
170
|
+
name: 'main',
|
|
171
|
+
hostname: 'localhost',
|
|
172
|
+
routesDir: join(root, 'sites/main/routes'),
|
|
173
|
+
config: { name: 'Main Site', primaryColor: '#1d4ed8' },
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'fallback',
|
|
177
|
+
hostname: '*',
|
|
178
|
+
config: { name: 'Fallback', primaryColor: '#374151' },
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
})
|
|
182
|
+
`
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function serverEntry() {
|
|
186
|
+
return `import { startMultisite } from '@davaux/multisite'
|
|
187
|
+
import { sites } from './multisite.config.js'
|
|
188
|
+
|
|
189
|
+
startMultisite(sites, { port: 3000, hostname: 'localhost', cwd: import.meta.dirname })
|
|
190
|
+
`
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function buildEntry() {
|
|
194
|
+
return `import { buildMultisite } from '@davaux/multisite/build'
|
|
195
|
+
import { sites } from './multisite.config.js'
|
|
196
|
+
|
|
197
|
+
await buildMultisite(sites, { cwd: import.meta.dirname })
|
|
198
|
+
`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function baseLayout(title: string) {
|
|
202
|
+
return `import { defineLayout } from 'davaux'
|
|
203
|
+
|
|
204
|
+
export default defineLayout(({ children, ctx }) => (
|
|
205
|
+
<html lang="en">
|
|
206
|
+
<head>
|
|
207
|
+
<meta charset="utf-8" />
|
|
208
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
209
|
+
<title>{ctx.head.title ?? '${title}'}</title>
|
|
210
|
+
{ctx.head.description && <meta name="description" content={ctx.head.description} />}
|
|
211
|
+
{Object.entries(ctx.head.meta).map(([name, content]) => (
|
|
212
|
+
<meta name={name} content={content} />
|
|
213
|
+
))}
|
|
214
|
+
{ctx.head.stylesheets.map((href) => (
|
|
215
|
+
<link rel="stylesheet" href={href} />
|
|
216
|
+
))}
|
|
217
|
+
</head>
|
|
218
|
+
<body>
|
|
219
|
+
{children as unknown as string}
|
|
220
|
+
{ctx.head.scripts.map((src) => (
|
|
221
|
+
<script type="module" src={src}></script>
|
|
222
|
+
))}
|
|
223
|
+
</body>
|
|
224
|
+
</html>
|
|
225
|
+
))
|
|
226
|
+
`
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function baseIndexPage(title: string) {
|
|
230
|
+
return `import { definePage } from 'davaux'
|
|
231
|
+
|
|
232
|
+
export default definePage((ctx) => {
|
|
233
|
+
ctx.head.title = '${title}'
|
|
234
|
+
return (
|
|
235
|
+
<main>
|
|
236
|
+
<h1>Welcome to ${title}</h1>
|
|
237
|
+
<p>
|
|
238
|
+
This page is served from the shared base routes (<code>src/routes/</code>). It is visible
|
|
239
|
+
at any hostname not explicitly registered in <code>multisite.config.ts</code> — in this
|
|
240
|
+
project, that means any hostname other than <code>localhost</code>.
|
|
241
|
+
</p>
|
|
242
|
+
<h2>Get started</h2>
|
|
243
|
+
<ul>
|
|
244
|
+
<li>
|
|
245
|
+
<a href="https://davaux.codeberg.page/docs/packages/multisite">@davaux/multisite</a>
|
|
246
|
+
{' — '}concepts, API reference, and operating modes
|
|
247
|
+
</li>
|
|
248
|
+
<li>
|
|
249
|
+
<a href="https://davaux.codeberg.page/docs/routing">Routing</a>
|
|
250
|
+
{' — '}file-based routing, dynamic segments, layouts, and middlewares
|
|
251
|
+
</li>
|
|
252
|
+
<li>
|
|
253
|
+
<a href="https://davaux.codeberg.page/docs/getting-started">Getting started</a>
|
|
254
|
+
{' — '}core Davaux concepts
|
|
255
|
+
</li>
|
|
256
|
+
</ul>
|
|
257
|
+
<h2>Project structure</h2>
|
|
258
|
+
<ul>
|
|
259
|
+
<li>
|
|
260
|
+
<code>multisite.config.ts</code> — site definitions and per-site config
|
|
261
|
+
</li>
|
|
262
|
+
<li>
|
|
263
|
+
<code>src/routes/</code> — shared base routes (you are here)
|
|
264
|
+
</li>
|
|
265
|
+
<li>
|
|
266
|
+
<code>sites/main/routes/</code> — main site overrides (served at <code>localhost</code>)
|
|
267
|
+
</li>
|
|
268
|
+
<li>
|
|
269
|
+
<code>server.ts</code> — starts the dev and production server
|
|
270
|
+
</li>
|
|
271
|
+
<li>
|
|
272
|
+
<code>build.ts</code> — production build script
|
|
273
|
+
</li>
|
|
274
|
+
</ul>
|
|
275
|
+
</main>
|
|
276
|
+
)
|
|
277
|
+
})
|
|
278
|
+
`
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function siteLayout() {
|
|
282
|
+
return `export { default } from '../../../src/routes/_layout.js'
|
|
283
|
+
`
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function siteIndexPage(title: string) {
|
|
287
|
+
return `import { definePage } from 'davaux'
|
|
288
|
+
import { getSite } from '@davaux/multisite'
|
|
289
|
+
import type { SiteConfig } from '../../../multisite.config.js'
|
|
290
|
+
|
|
291
|
+
export default definePage((ctx) => {
|
|
292
|
+
const site = getSite<SiteConfig>(ctx)
|
|
293
|
+
ctx.head.title = site?.name ?? '${title}'
|
|
294
|
+
return (
|
|
295
|
+
<main>
|
|
296
|
+
<h1>Welcome to {site?.name ?? '${title}'}</h1>
|
|
297
|
+
<p>
|
|
298
|
+
You're looking at the <strong>main</strong> site. Edit{' '}
|
|
299
|
+
<code>sites/main/routes/index.page.tsx</code> to get started.
|
|
300
|
+
</p>
|
|
301
|
+
<p>
|
|
302
|
+
Site config is defined in <code>multisite.config.ts</code> and accessed here via{' '}
|
|
303
|
+
<code>getSite(ctx)</code>. Use it for per-site themes, database URLs, feature flags —
|
|
304
|
+
anything that varies between sites.
|
|
305
|
+
</p>
|
|
306
|
+
</main>
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
`
|
|
310
|
+
}
|
package/src/create.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export async function scaffold(name: string | undefined, cwd: string) {
|
|
5
|
+
if (!name) {
|
|
6
|
+
console.error('Usage: davaux create <project-name>')
|
|
7
|
+
process.exit(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!/^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/.test(name)) {
|
|
11
|
+
console.error(
|
|
12
|
+
`[davaux] Invalid project name "${name}". Use lowercase letters, numbers, hyphens, underscores, or dots.`,
|
|
13
|
+
)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const dir = resolve(cwd, name)
|
|
18
|
+
|
|
19
|
+
if (existsSync(dir)) {
|
|
20
|
+
console.error(`[davaux] Directory "${name}" already exists.`)
|
|
21
|
+
process.exit(1)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const title = name.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
|
|
25
|
+
|
|
26
|
+
mkdirSync(join(dir, 'src', 'routes'), { recursive: true })
|
|
27
|
+
mkdirSync(join(dir, 'public'), { recursive: true })
|
|
28
|
+
|
|
29
|
+
write(dir, 'package.json', packageJson(name))
|
|
30
|
+
write(dir, 'tsconfig.json', tsConfig())
|
|
31
|
+
write(dir, 'davaux.config.ts', davauxConfig())
|
|
32
|
+
write(dir, 'biome.json', biomeConfig())
|
|
33
|
+
write(dir, 'src/routes/_layout.tsx', layout(title))
|
|
34
|
+
write(dir, 'src/routes/index.page.tsx', indexPage(title))
|
|
35
|
+
|
|
36
|
+
console.log(`\n Created project: ${name}\n`)
|
|
37
|
+
console.log(' Next steps:\n')
|
|
38
|
+
console.log(` cd ${name}`)
|
|
39
|
+
console.log(' npm install')
|
|
40
|
+
console.log(' npm run dev\n')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function write(dir: string, file: string, content: string) {
|
|
44
|
+
writeFileSync(join(dir, file), content, 'utf8')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function packageJson(name: string) {
|
|
48
|
+
return (
|
|
49
|
+
JSON.stringify(
|
|
50
|
+
{
|
|
51
|
+
name,
|
|
52
|
+
version: '0.1.0',
|
|
53
|
+
type: 'module',
|
|
54
|
+
scripts: {
|
|
55
|
+
dev: 'davaux dev',
|
|
56
|
+
build: 'davaux build',
|
|
57
|
+
start: 'davaux start',
|
|
58
|
+
},
|
|
59
|
+
dependencies: {
|
|
60
|
+
davaux: 'latest',
|
|
61
|
+
},
|
|
62
|
+
devDependencies: {
|
|
63
|
+
'@biomejs/biome': 'latest',
|
|
64
|
+
'@types/node': 'latest',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
null,
|
|
68
|
+
2,
|
|
69
|
+
) + '\n'
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function tsConfig() {
|
|
74
|
+
return (
|
|
75
|
+
JSON.stringify(
|
|
76
|
+
{
|
|
77
|
+
compilerOptions: {
|
|
78
|
+
target: 'ESNext',
|
|
79
|
+
module: 'NodeNext',
|
|
80
|
+
moduleResolution: 'NodeNext',
|
|
81
|
+
jsx: 'react-jsx',
|
|
82
|
+
jsxImportSource: 'davaux',
|
|
83
|
+
strict: true,
|
|
84
|
+
lib: ['ESNext', 'DOM'],
|
|
85
|
+
allowImportingTsExtensions: true,
|
|
86
|
+
noEmit: true,
|
|
87
|
+
skipLibCheck: true,
|
|
88
|
+
types: ['node', 'davaux/env'],
|
|
89
|
+
},
|
|
90
|
+
include: ['src/**/*'],
|
|
91
|
+
},
|
|
92
|
+
null,
|
|
93
|
+
2,
|
|
94
|
+
) + '\n'
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function davauxConfig() {
|
|
99
|
+
return `import { defineConfig } from 'davaux/config'
|
|
100
|
+
|
|
101
|
+
export default defineConfig({
|
|
102
|
+
server: {
|
|
103
|
+
port: 3000,
|
|
104
|
+
hostname: 'localhost',
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function biomeConfig() {
|
|
111
|
+
return (
|
|
112
|
+
JSON.stringify(
|
|
113
|
+
{
|
|
114
|
+
$schema: 'https://biomejs.dev/schemas/2.4.15/schema.json',
|
|
115
|
+
assist: { actions: { source: { organizeImports: 'on' } } },
|
|
116
|
+
linter: {
|
|
117
|
+
enabled: true,
|
|
118
|
+
rules: {
|
|
119
|
+
recommended: true,
|
|
120
|
+
correctness: {
|
|
121
|
+
noUnusedVariables: 'error',
|
|
122
|
+
noUnusedImports: 'error',
|
|
123
|
+
},
|
|
124
|
+
style: {
|
|
125
|
+
noNonNullAssertion: 'warn',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
formatter: {
|
|
130
|
+
enabled: true,
|
|
131
|
+
indentStyle: 'space',
|
|
132
|
+
indentWidth: 2,
|
|
133
|
+
lineWidth: 100,
|
|
134
|
+
},
|
|
135
|
+
javascript: {
|
|
136
|
+
formatter: {
|
|
137
|
+
quoteStyle: 'single',
|
|
138
|
+
trailingCommas: 'all',
|
|
139
|
+
semicolons: 'asNeeded',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
css: {
|
|
143
|
+
formatter: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
quoteStyle: 'single',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
files: {
|
|
149
|
+
includes: ['src/**'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
null,
|
|
153
|
+
2,
|
|
154
|
+
) + '\n'
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function layout(title: string) {
|
|
159
|
+
return `import { defineLayout } from 'davaux'
|
|
160
|
+
|
|
161
|
+
export default defineLayout(({ children, ctx }) => (
|
|
162
|
+
<html lang="en">
|
|
163
|
+
<head>
|
|
164
|
+
<meta charset="utf-8" />
|
|
165
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
166
|
+
<title>{ctx.head.title ?? '${title}'}</title>
|
|
167
|
+
{ctx.head.description && <meta name="description" content={ctx.head.description} />}
|
|
168
|
+
{Object.entries(ctx.head.meta).map(([name, content]) => (
|
|
169
|
+
<meta name={name} content={content} />
|
|
170
|
+
))}
|
|
171
|
+
{ctx.head.stylesheets.map((href) => (
|
|
172
|
+
<link rel="stylesheet" href={href} />
|
|
173
|
+
))}
|
|
174
|
+
</head>
|
|
175
|
+
<body>
|
|
176
|
+
{children as unknown as string}
|
|
177
|
+
{ctx.head.scripts.map((src) => (
|
|
178
|
+
<script type="module" src={src}></script>
|
|
179
|
+
))}
|
|
180
|
+
</body>
|
|
181
|
+
</html>
|
|
182
|
+
))
|
|
183
|
+
`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function indexPage(title: string) {
|
|
187
|
+
return `import { definePage } from 'davaux'
|
|
188
|
+
|
|
189
|
+
export default definePage((ctx) => {
|
|
190
|
+
ctx.head.title = '${title}'
|
|
191
|
+
return <h1>Welcome to ${title}</h1>
|
|
192
|
+
})
|
|
193
|
+
`
|
|
194
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { extname, join } from 'node:path'
|
|
3
|
+
import { renderToHtml } from '../oml/render.js'
|
|
4
|
+
import type { OmlBlueprint, OmlPropSchema, OmlPropType } from '../oml/types.js'
|
|
5
|
+
import { parseOmlBlueprint } from '../oml/types.js'
|
|
6
|
+
|
|
7
|
+
export type BlueprintEntry = {
|
|
8
|
+
id: string
|
|
9
|
+
name: string
|
|
10
|
+
props: Record<string, OmlPropSchema>
|
|
11
|
+
previewHtml: string
|
|
12
|
+
jsxSnippet: string
|
|
13
|
+
imports: Record<string, string>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function collectJsonFiles(dir: string, out: string[]): void {
|
|
17
|
+
if (!existsSync(dir)) return
|
|
18
|
+
for (const entry of readdirSync(dir)) {
|
|
19
|
+
const full = join(dir, entry)
|
|
20
|
+
if (statSync(full).isDirectory()) {
|
|
21
|
+
collectJsonFiles(full, out)
|
|
22
|
+
} else if (extname(entry) === '.json') {
|
|
23
|
+
out.push(full)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function propPlaceholder(schema: OmlPropSchema): string {
|
|
29
|
+
if (schema.default !== undefined) {
|
|
30
|
+
const d = schema.default
|
|
31
|
+
if (typeof d === 'string') return `"${d}"`
|
|
32
|
+
return `{${JSON.stringify(d)}}`
|
|
33
|
+
}
|
|
34
|
+
const placeholders: Record<OmlPropType, string> = {
|
|
35
|
+
string: '"example"',
|
|
36
|
+
number: '{0}',
|
|
37
|
+
boolean: '{false}',
|
|
38
|
+
function: '{() => {}}',
|
|
39
|
+
node: '"content"',
|
|
40
|
+
array: '{[]}',
|
|
41
|
+
}
|
|
42
|
+
return placeholders[schema.type] ?? '"example"'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toUsageJsx(bp: OmlBlueprint): string {
|
|
46
|
+
const entries = Object.entries(bp.props)
|
|
47
|
+
if (entries.length === 0) return `<${bp.name} />`
|
|
48
|
+
const lines = entries.map(([name, schema]) => ` ${name}=${propPlaceholder(schema)}`)
|
|
49
|
+
return `<${bp.name}\n${lines.join('\n')}\n/>`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function scanBlueprints(cwd: string): BlueprintEntry[] {
|
|
53
|
+
const files: string[] = []
|
|
54
|
+
collectJsonFiles(join(cwd, 'src', 'blueprints'), files)
|
|
55
|
+
collectJsonFiles(join(cwd, 'src', 'components'), files)
|
|
56
|
+
|
|
57
|
+
const results: BlueprintEntry[] = []
|
|
58
|
+
for (const filePath of files) {
|
|
59
|
+
try {
|
|
60
|
+
const raw = JSON.parse(readFileSync(filePath, 'utf-8'))
|
|
61
|
+
const bp = parseOmlBlueprint(raw)
|
|
62
|
+
results.push({
|
|
63
|
+
id: bp.id,
|
|
64
|
+
name: bp.name,
|
|
65
|
+
props: bp.props,
|
|
66
|
+
previewHtml: bp.output ? renderToHtml(bp.output) : '',
|
|
67
|
+
jsxSnippet: toUsageJsx(bp),
|
|
68
|
+
imports: bp.imports ?? {},
|
|
69
|
+
})
|
|
70
|
+
} catch {
|
|
71
|
+
// Skip files that aren't valid blueprints
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return results
|
|
75
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { basename, extname, join, relative } from 'node:path'
|
|
3
|
+
import type { OmlPropSchema, OmlPropType } from '../oml/types.js'
|
|
4
|
+
|
|
5
|
+
export type ComponentEntry = {
|
|
6
|
+
name: string
|
|
7
|
+
file: string
|
|
8
|
+
importPath: string
|
|
9
|
+
props: Record<string, OmlPropSchema>
|
|
10
|
+
hasBlueprintAlready: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function mapTsType(rawType: string): OmlPropType {
|
|
14
|
+
const t = rawType
|
|
15
|
+
.trim()
|
|
16
|
+
.replace(/\s*[|&]\s*(undefined|null)$/g, '')
|
|
17
|
+
.trim()
|
|
18
|
+
if (t === 'string') return 'string'
|
|
19
|
+
if (t === 'number') return 'number'
|
|
20
|
+
if (t === 'boolean') return 'boolean'
|
|
21
|
+
if (/ReactNode|JSX\.Element|React\.ReactNode|OmlNode|OmlChild|VNode/.test(t)) return 'node'
|
|
22
|
+
if (/Array<|[[\]]/.test(t)) return 'array'
|
|
23
|
+
if (/=>/.test(t) || t === 'Function' || t.startsWith('(')) return 'function'
|
|
24
|
+
return 'string'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractProps(source: string): Record<string, OmlPropSchema> {
|
|
28
|
+
const props: Record<string, OmlPropSchema> = {}
|
|
29
|
+
const m = source.match(/(?:interface\s+Props|type\s+Props\s*=)\s*\{([\s\S]*?)\}/)
|
|
30
|
+
if (!m) return props
|
|
31
|
+
for (const line of m[1].split('\n')) {
|
|
32
|
+
const pm = line.match(/^\s*([a-zA-Z_$][a-zA-Z0-9_$]*)(\?)?\s*:\s*([^;,\n]+)/)
|
|
33
|
+
if (!pm) continue
|
|
34
|
+
const name = pm[1]
|
|
35
|
+
if (name === 'children') continue
|
|
36
|
+
props[name] = { type: mapTsType(pm[3].trim()), required: pm[2] !== '?' }
|
|
37
|
+
}
|
|
38
|
+
return props
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function collectComponentFiles(dir: string, out: string[]): void {
|
|
42
|
+
if (!existsSync(dir)) return
|
|
43
|
+
for (const entry of readdirSync(dir)) {
|
|
44
|
+
const full = join(dir, entry)
|
|
45
|
+
if (statSync(full).isDirectory()) {
|
|
46
|
+
collectComponentFiles(full, out)
|
|
47
|
+
} else if (['.tsx', '.ts', '.jsx', '.js'].includes(extname(entry)) && /^[A-Z]/.test(entry)) {
|
|
48
|
+
out.push(full)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function scanComponents(dir: string, cwd: string, blueprintsDir: string): ComponentEntry[] {
|
|
54
|
+
const files: string[] = []
|
|
55
|
+
collectComponentFiles(dir, files)
|
|
56
|
+
const routesDir = join(cwd, 'src', 'routes')
|
|
57
|
+
return files.map((filePath) => {
|
|
58
|
+
const name = basename(filePath, extname(filePath))
|
|
59
|
+
let props: Record<string, OmlPropSchema> = {}
|
|
60
|
+
try {
|
|
61
|
+
props = extractProps(readFileSync(filePath, 'utf-8'))
|
|
62
|
+
} catch {}
|
|
63
|
+
const noExt = filePath.replace(/\.(tsx?|jsx?)$/, '')
|
|
64
|
+
const rel = relative(routesDir, noExt)
|
|
65
|
+
const importPath = rel.startsWith('.') ? rel : `../${rel}`
|
|
66
|
+
return {
|
|
67
|
+
name,
|
|
68
|
+
file: relative(cwd, filePath),
|
|
69
|
+
importPath,
|
|
70
|
+
props,
|
|
71
|
+
hasBlueprintAlready: existsSync(join(blueprintsDir, `${name}.oml.json`)),
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function updateBlueprint(
|
|
77
|
+
id: string,
|
|
78
|
+
name: string,
|
|
79
|
+
props: Record<string, OmlPropSchema>,
|
|
80
|
+
blueprintsDir: string,
|
|
81
|
+
): { saved: boolean; error?: string } {
|
|
82
|
+
const filePath = join(blueprintsDir, `${id}.oml.json`)
|
|
83
|
+
try {
|
|
84
|
+
let existing: Record<string, unknown> = {}
|
|
85
|
+
if (existsSync(filePath)) existing = JSON.parse(readFileSync(filePath, 'utf-8'))
|
|
86
|
+
writeFileSync(filePath, JSON.stringify({ ...existing, id, name, props }, null, 2), 'utf-8')
|
|
87
|
+
return { saved: true }
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return { saved: false, error: String(e) }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function saveBlueprint(
|
|
94
|
+
name: string,
|
|
95
|
+
props: Record<string, OmlPropSchema>,
|
|
96
|
+
imports: Record<string, string>,
|
|
97
|
+
blueprintsDir: string,
|
|
98
|
+
): { saved: boolean; file?: string; error?: string } {
|
|
99
|
+
try {
|
|
100
|
+
if (!existsSync(blueprintsDir)) mkdirSync(blueprintsDir, { recursive: true })
|
|
101
|
+
const bp = { id: name, name, props, return: null, imports }
|
|
102
|
+
const filePath = join(blueprintsDir, `${name}.oml.json`)
|
|
103
|
+
writeFileSync(filePath, JSON.stringify(bp, null, 2), 'utf-8')
|
|
104
|
+
return { saved: true, file: filePath }
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return { saved: false, error: String(e) }
|
|
107
|
+
}
|
|
108
|
+
}
|