davaux 0.8.0 → 0.8.1
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/package.json +6 -2
- package/BASELINE.md +0 -169
- package/CLAUDE.md +0 -518
- package/ROADMAP.md +0 -198
- package/build.mjs +0 -101
- package/client/control.ts +0 -247
- package/client/hydrate.ts +0 -37
- package/client/index.ts +0 -19
- package/client/jsx-runtime.ts +0 -209
- package/client/resource.ts +0 -122
- package/client/signal.ts +0 -211
- package/client/store.ts +0 -110
- package/client/useHead.ts +0 -63
- package/pka.config.json +0 -32
- package/src/build/config.ts +0 -42
- package/src/build/index.ts +0 -6
- package/src/build/plugins.ts +0 -118
- package/src/cli.ts +0 -502
- package/src/config.ts +0 -197
- package/src/create-multisite.ts +0 -310
- package/src/create.ts +0 -194
- package/src/dev/blueprints.ts +0 -75
- package/src/dev/components.ts +0 -108
- package/src/dev/insert.ts +0 -221
- package/src/dev/remove.ts +0 -677
- package/src/dev/watch.ts +0 -3098
- package/src/errors.ts +0 -64
- package/src/generate.ts +0 -228
- package/src/index.ts +0 -67
- package/src/island.ts +0 -47
- package/src/jsx-runtime.d.ts +0 -408
- package/src/jsx-runtime.d.ts.map +0 -1
- package/src/jsx-runtime.ts +0 -536
- package/src/link.ts +0 -49
- package/src/oml/fragment.ts +0 -54
- package/src/oml/index.ts +0 -21
- package/src/oml/jsx-runtime.ts +0 -121
- package/src/oml/jsx.ts +0 -151
- package/src/oml/page.ts +0 -13
- package/src/oml/render.ts +0 -181
- package/src/oml/types.ts +0 -159
- package/src/router/handler.ts +0 -515
- package/src/router/matcher.ts +0 -52
- package/src/router/scanner.ts +0 -272
- package/src/server/index.ts +0 -49
- package/src/signal.ts +0 -39
- package/src/ssg.ts +0 -253
- package/src/test/actions.test.ts +0 -40
- package/src/test/body-limits.test.ts +0 -83
- package/src/test/errors.test.ts +0 -53
- package/src/test/fixtures/routes/[id].page.ts +0 -3
- package/src/test/fixtures/routes/_error.ts +0 -6
- package/src/test/fixtures/routes/_global.ts +0 -8
- package/src/test/fixtures/routes/_layout-template.ts +0 -7
- package/src/test/fixtures/routes/_layout.ts +0 -7
- package/src/test/fixtures/routes/_layout_scripts.ts +0 -8
- package/src/test/fixtures/routes/_middleware.ts +0 -8
- package/src/test/fixtures/routes/_redirect301_mw.ts +0 -5
- package/src/test/fixtures/routes/_redirect_mw.ts +0 -5
- package/src/test/fixtures/routes/about.page.ts +0 -6
- package/src/test/fixtures/routes/action.page.ts +0 -11
- package/src/test/fixtures/routes/api/form-all.post.ts +0 -5
- package/src/test/fixtures/routes/api/form-limited.post.ts +0 -6
- package/src/test/fixtures/routes/api/response-obj.get.ts +0 -17
- package/src/test/fixtures/routes/api/upload.post.ts +0 -14
- package/src/test/fixtures/routes/api/users.get.ts +0 -3
- package/src/test/fixtures/routes/api/xml.get.ts +0 -5
- package/src/test/fixtures/routes/auth/_middleware.ts +0 -11
- package/src/test/fixtures/routes/auth/protected.page.ts +0 -3
- package/src/test/fixtures/routes/index.page.ts +0 -3
- package/src/test/fixtures/routes/oml.page.ts +0 -7
- package/src/test/fixtures/routes/redirect.page.ts +0 -3
- package/src/test/fixtures/routes/ssg/[slug].page.ts +0 -8
- package/src/test/fixtures/routes/ssg/server.page.ts +0 -5
- package/src/test/fixtures/routes/state.page.ts +0 -4
- package/src/test/fixtures/routes/throw.page.ts +0 -5
- package/src/test/fixtures/routes/wiki/[...slug].page.ts +0 -3
- package/src/test/helpers.ts +0 -132
- package/src/test/layouts.test.ts +0 -76
- package/src/test/middleware.test.ts +0 -69
- package/src/test/multipart.test.ts +0 -91
- package/src/test/oml-routing.test.ts +0 -59
- package/src/test/oml.test.ts +0 -429
- package/src/test/redirects.test.ts +0 -32
- package/src/test/routing.test.ts +0 -118
- package/src/test/ssg.test.ts +0 -273
- package/src/test/web-response.test.ts +0 -33
- package/src/types.ts +0 -670
- package/tsconfig.client.json +0 -17
- package/tsconfig.json +0 -20
package/src/create-multisite.ts
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
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
|
-
}
|
package/src/dev/blueprints.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
}
|
package/src/dev/components.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
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
|
-
}
|