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.
Files changed (333) hide show
  1. package/BASELINE.md +169 -0
  2. package/CLAUDE.md +518 -0
  3. package/LICENSE +21 -0
  4. package/README.md +36 -0
  5. package/ROADMAP.md +198 -0
  6. package/build.mjs +101 -0
  7. package/client/control.ts +247 -0
  8. package/client/hydrate.ts +37 -0
  9. package/client/index.ts +19 -0
  10. package/client/jsx-runtime.ts +209 -0
  11. package/client/resource.ts +122 -0
  12. package/client/signal.ts +211 -0
  13. package/client/store.ts +110 -0
  14. package/client/useHead.ts +63 -0
  15. package/dist/build/config.d.ts +3 -0
  16. package/dist/build/config.d.ts.map +1 -0
  17. package/dist/build/config.js +38 -0
  18. package/dist/build/config.js.map +7 -0
  19. package/dist/build/index.d.ts +2 -0
  20. package/dist/build/index.d.ts.map +1 -0
  21. package/dist/build/index.js +13 -0
  22. package/dist/build/index.js.map +7 -0
  23. package/dist/build/plugins.d.ts +7 -0
  24. package/dist/build/plugins.d.ts.map +1 -0
  25. package/dist/build/plugins.js +85 -0
  26. package/dist/build/plugins.js.map +7 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +427 -0
  30. package/dist/cli.js.map +7 -0
  31. package/dist/client/control.d.ts +49 -0
  32. package/dist/client/control.d.ts.map +1 -0
  33. package/dist/client/control.js +154 -0
  34. package/dist/client/control.js.map +7 -0
  35. package/dist/client/hydrate.d.ts +7 -0
  36. package/dist/client/hydrate.d.ts.map +1 -0
  37. package/dist/client/hydrate.js +23 -0
  38. package/dist/client/hydrate.js.map +7 -0
  39. package/dist/client/index.d.ts +12 -0
  40. package/dist/client/index.d.ts.map +1 -0
  41. package/dist/client/index.js +32 -0
  42. package/dist/client/index.js.map +7 -0
  43. package/dist/client/jsx-runtime.d.ts +40 -0
  44. package/dist/client/jsx-runtime.d.ts.map +1 -0
  45. package/dist/client/jsx-runtime.js +139 -0
  46. package/dist/client/jsx-runtime.js.map +7 -0
  47. package/dist/client/resource.d.ts +31 -0
  48. package/dist/client/resource.d.ts.map +1 -0
  49. package/dist/client/resource.js +64 -0
  50. package/dist/client/resource.js.map +7 -0
  51. package/dist/client/signal.d.ts +90 -0
  52. package/dist/client/signal.d.ts.map +1 -0
  53. package/dist/client/signal.js +115 -0
  54. package/dist/client/signal.js.map +7 -0
  55. package/dist/client/store.d.ts +26 -0
  56. package/dist/client/store.d.ts.map +1 -0
  57. package/dist/client/store.js +63 -0
  58. package/dist/client/store.js.map +7 -0
  59. package/dist/client/useHead.d.ts +28 -0
  60. package/dist/client/useHead.d.ts.map +1 -0
  61. package/dist/client/useHead.js +33 -0
  62. package/dist/client/useHead.js.map +7 -0
  63. package/dist/config.d.ts +182 -0
  64. package/dist/config.d.ts.map +1 -0
  65. package/dist/config.js +21 -0
  66. package/dist/config.js.map +7 -0
  67. package/dist/create-multisite.d.ts +2 -0
  68. package/dist/create-multisite.d.ts.map +1 -0
  69. package/dist/create-multisite.js +291 -0
  70. package/dist/create-multisite.js.map +7 -0
  71. package/dist/create.d.ts +2 -0
  72. package/dist/create.d.ts.map +1 -0
  73. package/dist/create.js +179 -0
  74. package/dist/create.js.map +7 -0
  75. package/dist/dev/blueprints.d.ts +11 -0
  76. package/dist/dev/blueprints.d.ts.map +1 -0
  77. package/dist/dev/blueprints.js +65 -0
  78. package/dist/dev/blueprints.js.map +7 -0
  79. package/dist/dev/components.d.ts +19 -0
  80. package/dist/dev/components.d.ts.map +1 -0
  81. package/dist/dev/components.js +87 -0
  82. package/dist/dev/components.js.map +7 -0
  83. package/dist/dev/insert.d.ts +11 -0
  84. package/dist/dev/insert.d.ts.map +1 -0
  85. package/dist/dev/insert.js +160 -0
  86. package/dist/dev/insert.js.map +7 -0
  87. package/dist/dev/remove.d.ts +53 -0
  88. package/dist/dev/remove.d.ts.map +1 -0
  89. package/dist/dev/remove.js +518 -0
  90. package/dist/dev/remove.js.map +7 -0
  91. package/dist/dev/watch.d.ts +26 -0
  92. package/dist/dev/watch.d.ts.map +1 -0
  93. package/dist/dev/watch.js +2905 -0
  94. package/dist/dev/watch.js.map +7 -0
  95. package/dist/errors.d.ts +6 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +63 -0
  98. package/dist/errors.js.map +7 -0
  99. package/dist/generate.d.ts +2 -0
  100. package/dist/generate.d.ts.map +1 -0
  101. package/dist/generate.js +191 -0
  102. package/dist/generate.js.map +7 -0
  103. package/dist/index.d.ts +9 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +57 -0
  106. package/dist/index.js.map +7 -0
  107. package/dist/island.d.ts +24 -0
  108. package/dist/island.d.ts.map +1 -0
  109. package/dist/island.js +15 -0
  110. package/dist/island.js.map +7 -0
  111. package/dist/jsx-runtime.d.ts +406 -0
  112. package/dist/jsx-runtime.d.ts.map +1 -0
  113. package/dist/jsx-runtime.js +90 -0
  114. package/dist/jsx-runtime.js.map +7 -0
  115. package/dist/link.d.ts +27 -0
  116. package/dist/link.d.ts.map +1 -0
  117. package/dist/link.js +29 -0
  118. package/dist/link.js.map +7 -0
  119. package/dist/oml/fragment.d.ts +16 -0
  120. package/dist/oml/fragment.d.ts.map +1 -0
  121. package/dist/oml/fragment.js +26 -0
  122. package/dist/oml/fragment.js.map +7 -0
  123. package/dist/oml/index.d.ts +11 -0
  124. package/dist/oml/index.d.ts.map +1 -0
  125. package/dist/oml/index.js +21 -0
  126. package/dist/oml/index.js.map +7 -0
  127. package/dist/oml/jsx-runtime.d.ts +34 -0
  128. package/dist/oml/jsx-runtime.d.ts.map +1 -0
  129. package/dist/oml/jsx-runtime.js +59 -0
  130. package/dist/oml/jsx-runtime.js.map +7 -0
  131. package/dist/oml/jsx.d.ts +14 -0
  132. package/dist/oml/jsx.d.ts.map +1 -0
  133. package/dist/oml/jsx.js +96 -0
  134. package/dist/oml/jsx.js.map +7 -0
  135. package/dist/oml/page.d.ts +7 -0
  136. package/dist/oml/page.d.ts.map +1 -0
  137. package/dist/oml/page.js +6 -0
  138. package/dist/oml/page.js.map +7 -0
  139. package/dist/oml/render.d.ts +13 -0
  140. package/dist/oml/render.d.ts.map +1 -0
  141. package/dist/oml/render.js +117 -0
  142. package/dist/oml/render.js.map +7 -0
  143. package/dist/oml/types.d.ts +79 -0
  144. package/dist/oml/types.d.ts.map +1 -0
  145. package/dist/oml/types.js +64 -0
  146. package/dist/oml/types.js.map +7 -0
  147. package/dist/router/handler.d.ts +53 -0
  148. package/dist/router/handler.d.ts.map +1 -0
  149. package/dist/router/handler.js +342 -0
  150. package/dist/router/handler.js.map +7 -0
  151. package/dist/router/matcher.d.ts +21 -0
  152. package/dist/router/matcher.d.ts.map +1 -0
  153. package/dist/router/matcher.js +28 -0
  154. package/dist/router/matcher.js.map +7 -0
  155. package/dist/router/scanner.d.ts +17 -0
  156. package/dist/router/scanner.d.ts.map +1 -0
  157. package/dist/router/scanner.js +197 -0
  158. package/dist/router/scanner.js.map +7 -0
  159. package/dist/server/index.d.ts +23 -0
  160. package/dist/server/index.d.ts.map +1 -0
  161. package/dist/server/index.js +29 -0
  162. package/dist/server/index.js.map +7 -0
  163. package/dist/signal.d.ts +15 -0
  164. package/dist/signal.d.ts.map +1 -0
  165. package/dist/signal.js +29 -0
  166. package/dist/signal.js.map +7 -0
  167. package/dist/ssg.d.ts +45 -0
  168. package/dist/ssg.d.ts.map +1 -0
  169. package/dist/ssg.js +175 -0
  170. package/dist/ssg.js.map +7 -0
  171. package/dist/test/actions.test.d.ts +2 -0
  172. package/dist/test/actions.test.d.ts.map +1 -0
  173. package/dist/test/body-limits.test.d.ts +2 -0
  174. package/dist/test/body-limits.test.d.ts.map +1 -0
  175. package/dist/test/errors.test.d.ts +2 -0
  176. package/dist/test/errors.test.d.ts.map +1 -0
  177. package/dist/test/fixtures/routes/[id].page.d.ts +4 -0
  178. package/dist/test/fixtures/routes/[id].page.d.ts.map +1 -0
  179. package/dist/test/fixtures/routes/_error.d.ts +3 -0
  180. package/dist/test/fixtures/routes/_error.d.ts.map +1 -0
  181. package/dist/test/fixtures/routes/_global.d.ts +3 -0
  182. package/dist/test/fixtures/routes/_global.d.ts.map +1 -0
  183. package/dist/test/fixtures/routes/_layout-template.d.ts +3 -0
  184. package/dist/test/fixtures/routes/_layout-template.d.ts.map +1 -0
  185. package/dist/test/fixtures/routes/_layout.d.ts +3 -0
  186. package/dist/test/fixtures/routes/_layout.d.ts.map +1 -0
  187. package/dist/test/fixtures/routes/_layout_scripts.d.ts +3 -0
  188. package/dist/test/fixtures/routes/_layout_scripts.d.ts.map +1 -0
  189. package/dist/test/fixtures/routes/_middleware.d.ts +3 -0
  190. package/dist/test/fixtures/routes/_middleware.d.ts.map +1 -0
  191. package/dist/test/fixtures/routes/_redirect301_mw.d.ts +3 -0
  192. package/dist/test/fixtures/routes/_redirect301_mw.d.ts.map +1 -0
  193. package/dist/test/fixtures/routes/_redirect_mw.d.ts +3 -0
  194. package/dist/test/fixtures/routes/_redirect_mw.d.ts.map +1 -0
  195. package/dist/test/fixtures/routes/about.page.d.ts +3 -0
  196. package/dist/test/fixtures/routes/about.page.d.ts.map +1 -0
  197. package/dist/test/fixtures/routes/action.page.d.ts +6 -0
  198. package/dist/test/fixtures/routes/action.page.d.ts.map +1 -0
  199. package/dist/test/fixtures/routes/api/form-all.post.d.ts +3 -0
  200. package/dist/test/fixtures/routes/api/form-all.post.d.ts.map +1 -0
  201. package/dist/test/fixtures/routes/api/form-limited.post.d.ts +6 -0
  202. package/dist/test/fixtures/routes/api/form-limited.post.d.ts.map +1 -0
  203. package/dist/test/fixtures/routes/api/response-obj.get.d.ts +3 -0
  204. package/dist/test/fixtures/routes/api/response-obj.get.d.ts.map +1 -0
  205. package/dist/test/fixtures/routes/api/upload.post.d.ts +12 -0
  206. package/dist/test/fixtures/routes/api/upload.post.d.ts.map +1 -0
  207. package/dist/test/fixtures/routes/api/users.get.d.ts +6 -0
  208. package/dist/test/fixtures/routes/api/users.get.d.ts.map +1 -0
  209. package/dist/test/fixtures/routes/api/xml.get.d.ts +3 -0
  210. package/dist/test/fixtures/routes/api/xml.get.d.ts.map +1 -0
  211. package/dist/test/fixtures/routes/auth/_middleware.d.ts +3 -0
  212. package/dist/test/fixtures/routes/auth/_middleware.d.ts.map +1 -0
  213. package/dist/test/fixtures/routes/auth/protected.page.d.ts +3 -0
  214. package/dist/test/fixtures/routes/auth/protected.page.d.ts.map +1 -0
  215. package/dist/test/fixtures/routes/index.page.d.ts +3 -0
  216. package/dist/test/fixtures/routes/index.page.d.ts.map +1 -0
  217. package/dist/test/fixtures/routes/oml.page.d.ts +3 -0
  218. package/dist/test/fixtures/routes/oml.page.d.ts.map +1 -0
  219. package/dist/test/fixtures/routes/redirect.page.d.ts +3 -0
  220. package/dist/test/fixtures/routes/redirect.page.d.ts.map +1 -0
  221. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts +5 -0
  222. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts.map +1 -0
  223. package/dist/test/fixtures/routes/ssg/server.page.d.ts +4 -0
  224. package/dist/test/fixtures/routes/ssg/server.page.d.ts.map +1 -0
  225. package/dist/test/fixtures/routes/state.page.d.ts +3 -0
  226. package/dist/test/fixtures/routes/state.page.d.ts.map +1 -0
  227. package/dist/test/fixtures/routes/throw.page.d.ts +3 -0
  228. package/dist/test/fixtures/routes/throw.page.d.ts.map +1 -0
  229. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts +3 -0
  230. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts.map +1 -0
  231. package/dist/test/helpers.d.ts +37 -0
  232. package/dist/test/helpers.d.ts.map +1 -0
  233. package/dist/test/layouts.test.d.ts +2 -0
  234. package/dist/test/layouts.test.d.ts.map +1 -0
  235. package/dist/test/middleware.test.d.ts +2 -0
  236. package/dist/test/middleware.test.d.ts.map +1 -0
  237. package/dist/test/multipart.test.d.ts +2 -0
  238. package/dist/test/multipart.test.d.ts.map +1 -0
  239. package/dist/test/oml-routing.test.d.ts +2 -0
  240. package/dist/test/oml-routing.test.d.ts.map +1 -0
  241. package/dist/test/oml.test.d.ts +2 -0
  242. package/dist/test/oml.test.d.ts.map +1 -0
  243. package/dist/test/redirects.test.d.ts +2 -0
  244. package/dist/test/redirects.test.d.ts.map +1 -0
  245. package/dist/test/routing.test.d.ts +2 -0
  246. package/dist/test/routing.test.d.ts.map +1 -0
  247. package/dist/test/ssg.test.d.ts +2 -0
  248. package/dist/test/ssg.test.d.ts.map +1 -0
  249. package/dist/test/web-response.test.d.ts +2 -0
  250. package/dist/test/web-response.test.d.ts.map +1 -0
  251. package/dist/types.d.ts +314 -0
  252. package/dist/types.d.ts.map +1 -0
  253. package/dist/types.js +292 -0
  254. package/dist/types.js.map +7 -0
  255. package/package.json +103 -0
  256. package/pka.config.json +32 -0
  257. package/src/build/config.ts +42 -0
  258. package/src/build/index.ts +6 -0
  259. package/src/build/plugins.ts +118 -0
  260. package/src/cli.ts +502 -0
  261. package/src/config.ts +197 -0
  262. package/src/create-multisite.ts +310 -0
  263. package/src/create.ts +194 -0
  264. package/src/dev/blueprints.ts +75 -0
  265. package/src/dev/components.ts +108 -0
  266. package/src/dev/insert.ts +221 -0
  267. package/src/dev/remove.ts +677 -0
  268. package/src/dev/watch.ts +3098 -0
  269. package/src/env.d.ts +5 -0
  270. package/src/errors.ts +64 -0
  271. package/src/generate.ts +228 -0
  272. package/src/index.ts +67 -0
  273. package/src/island.ts +47 -0
  274. package/src/jsx-runtime.d.ts +408 -0
  275. package/src/jsx-runtime.d.ts.map +1 -0
  276. package/src/jsx-runtime.ts +536 -0
  277. package/src/link.ts +49 -0
  278. package/src/oml/fragment.ts +54 -0
  279. package/src/oml/index.ts +21 -0
  280. package/src/oml/jsx-runtime.ts +121 -0
  281. package/src/oml/jsx.ts +151 -0
  282. package/src/oml/page.ts +13 -0
  283. package/src/oml/render.ts +181 -0
  284. package/src/oml/types.ts +159 -0
  285. package/src/router/handler.ts +515 -0
  286. package/src/router/matcher.ts +52 -0
  287. package/src/router/scanner.ts +272 -0
  288. package/src/server/index.ts +49 -0
  289. package/src/signal.ts +39 -0
  290. package/src/ssg.ts +253 -0
  291. package/src/test/actions.test.ts +40 -0
  292. package/src/test/body-limits.test.ts +83 -0
  293. package/src/test/errors.test.ts +53 -0
  294. package/src/test/fixtures/routes/[id].page.ts +3 -0
  295. package/src/test/fixtures/routes/_error.ts +6 -0
  296. package/src/test/fixtures/routes/_global.ts +8 -0
  297. package/src/test/fixtures/routes/_layout-template.ts +7 -0
  298. package/src/test/fixtures/routes/_layout.ts +7 -0
  299. package/src/test/fixtures/routes/_layout_scripts.ts +8 -0
  300. package/src/test/fixtures/routes/_middleware.ts +8 -0
  301. package/src/test/fixtures/routes/_redirect301_mw.ts +5 -0
  302. package/src/test/fixtures/routes/_redirect_mw.ts +5 -0
  303. package/src/test/fixtures/routes/about.page.ts +6 -0
  304. package/src/test/fixtures/routes/action.page.ts +11 -0
  305. package/src/test/fixtures/routes/api/form-all.post.ts +5 -0
  306. package/src/test/fixtures/routes/api/form-limited.post.ts +6 -0
  307. package/src/test/fixtures/routes/api/response-obj.get.ts +17 -0
  308. package/src/test/fixtures/routes/api/upload.post.ts +14 -0
  309. package/src/test/fixtures/routes/api/users.get.ts +3 -0
  310. package/src/test/fixtures/routes/api/xml.get.ts +5 -0
  311. package/src/test/fixtures/routes/auth/_middleware.ts +11 -0
  312. package/src/test/fixtures/routes/auth/protected.page.ts +3 -0
  313. package/src/test/fixtures/routes/index.page.ts +3 -0
  314. package/src/test/fixtures/routes/oml.page.ts +7 -0
  315. package/src/test/fixtures/routes/redirect.page.ts +3 -0
  316. package/src/test/fixtures/routes/ssg/[slug].page.ts +8 -0
  317. package/src/test/fixtures/routes/ssg/server.page.ts +5 -0
  318. package/src/test/fixtures/routes/state.page.ts +4 -0
  319. package/src/test/fixtures/routes/throw.page.ts +5 -0
  320. package/src/test/fixtures/routes/wiki/[...slug].page.ts +3 -0
  321. package/src/test/helpers.ts +132 -0
  322. package/src/test/layouts.test.ts +76 -0
  323. package/src/test/middleware.test.ts +69 -0
  324. package/src/test/multipart.test.ts +91 -0
  325. package/src/test/oml-routing.test.ts +59 -0
  326. package/src/test/oml.test.ts +429 -0
  327. package/src/test/redirects.test.ts +32 -0
  328. package/src/test/routing.test.ts +118 -0
  329. package/src/test/ssg.test.ts +273 -0
  330. package/src/test/web-response.test.ts +33 -0
  331. package/src/types.ts +670 -0
  332. package/tsconfig.client.json +17 -0
  333. 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
+ }