toiljs 0.0.4 → 0.0.6

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 (409) hide show
  1. package/.idea/prettier.xml +1 -0
  2. package/as-pect.config.js +1 -1
  3. package/build/backend/.tsbuildinfo +1 -1
  4. package/build/backend/index.js +1 -2
  5. package/build/cli/.tsbuildinfo +1 -1
  6. package/build/cli/configure.d.ts +15 -0
  7. package/build/cli/configure.js +201 -0
  8. package/build/cli/create.d.ts +4 -0
  9. package/build/cli/create.js +193 -68
  10. package/build/cli/features.d.ts +23 -0
  11. package/build/cli/features.js +85 -0
  12. package/build/cli/index.js +42 -2
  13. package/build/cli/proc.d.ts +1 -0
  14. package/build/cli/proc.js +11 -0
  15. package/build/cli/ui.js +1 -2
  16. package/build/cli/validate.d.ts +4 -0
  17. package/build/cli/validate.js +19 -0
  18. package/build/client/.tsbuildinfo +1 -1
  19. package/build/client/Link.d.ts +8 -0
  20. package/build/client/Link.js +44 -0
  21. package/build/client/NavLink.d.ts +14 -0
  22. package/build/client/NavLink.js +37 -0
  23. package/build/client/Router.d.ts +7 -0
  24. package/build/client/Router.js +55 -0
  25. package/build/client/channel/channel.d.ts +23 -0
  26. package/build/client/channel/channel.js +94 -0
  27. package/build/client/error-boundary.d.ts +16 -0
  28. package/build/client/error-boundary.js +19 -0
  29. package/build/client/head/head.d.ts +26 -0
  30. package/build/client/head/head.js +87 -0
  31. package/build/client/head.d.ts +26 -0
  32. package/build/client/head.js +87 -0
  33. package/build/client/hooks.d.ts +17 -0
  34. package/build/client/hooks.js +48 -0
  35. package/build/client/index.d.ts +18 -6
  36. package/build/client/index.js +10 -3
  37. package/build/client/lazy.d.ts +16 -0
  38. package/build/client/lazy.js +53 -0
  39. package/build/client/match.js +7 -0
  40. package/build/client/mount.d.ts +2 -0
  41. package/build/client/mount.js +13 -0
  42. package/build/client/navigation/Link.d.ts +8 -0
  43. package/build/client/navigation/Link.js +44 -0
  44. package/build/client/navigation/NavLink.d.ts +14 -0
  45. package/build/client/navigation/NavLink.js +37 -0
  46. package/build/client/navigation/navigation.d.ts +13 -0
  47. package/build/client/navigation/navigation.js +97 -0
  48. package/build/client/navigation/prefetch.d.ts +11 -0
  49. package/build/client/navigation/prefetch.js +100 -0
  50. package/build/client/navigation/scroll.d.ts +8 -0
  51. package/build/client/navigation/scroll.js +36 -0
  52. package/build/client/navigation.d.ts +13 -0
  53. package/build/client/navigation.js +97 -0
  54. package/build/client/params-context.d.ts +2 -0
  55. package/build/client/params-context.js +2 -0
  56. package/build/client/prefetch.d.ts +11 -0
  57. package/build/client/prefetch.js +100 -0
  58. package/build/client/routing/Router.d.ts +7 -0
  59. package/build/client/routing/Router.js +55 -0
  60. package/build/client/routing/error-boundary.d.ts +16 -0
  61. package/build/client/routing/error-boundary.js +19 -0
  62. package/build/client/routing/hooks.d.ts +17 -0
  63. package/build/client/routing/hooks.js +48 -0
  64. package/build/client/routing/lazy.d.ts +16 -0
  65. package/build/client/routing/lazy.js +53 -0
  66. package/build/client/routing/match.d.ts +2 -0
  67. package/build/client/routing/match.js +32 -0
  68. package/build/client/routing/mount.d.ts +2 -0
  69. package/build/client/routing/mount.js +13 -0
  70. package/build/client/routing/params-context.d.ts +2 -0
  71. package/build/client/routing/params-context.js +2 -0
  72. package/build/client/scroll.d.ts +8 -0
  73. package/build/client/scroll.js +36 -0
  74. package/build/client/types.d.ts +27 -0
  75. package/build/client/types.js +1 -0
  76. package/build/compiler/.tsbuildinfo +1 -1
  77. package/build/compiler/config.d.ts +2 -0
  78. package/build/compiler/config.js +14 -2
  79. package/build/compiler/docs.d.ts +10 -0
  80. package/build/compiler/docs.js +59 -0
  81. package/build/compiler/generate.js +144 -16
  82. package/build/compiler/index.d.ts +4 -2
  83. package/build/compiler/index.js +4 -3
  84. package/build/compiler/plugin.js +16 -1
  85. package/build/compiler/routes.js +5 -1
  86. package/build/compiler/vite.d.ts +1 -1
  87. package/build/compiler/vite.js +17 -1
  88. package/build/io/.tsbuildinfo +1 -1
  89. package/build/io/BinaryWriter.js +2 -2
  90. package/eslint.config.js +1 -1
  91. package/examples/basic/.toil/docs/cli.md +3 -0
  92. package/examples/basic/.toil/docs/client.md +3 -0
  93. package/examples/basic/.toil/docs/index.md +3 -0
  94. package/examples/basic/.toil/docs/routing.md +3 -0
  95. package/examples/basic/.toil/docs/server.md +3 -0
  96. package/examples/basic/.toil/docs/styling.md +3 -0
  97. package/examples/basic/.toil/entry.tsx +3 -8
  98. package/examples/basic/.toil/globals.ts +6 -0
  99. package/examples/basic/.toil/index.html +16 -12
  100. package/examples/basic/.toil/public/images/.gitkeep +1 -0
  101. package/examples/basic/.toil/public/images/logo.svg +37 -0
  102. package/examples/basic/.toil/public/robots.txt +2 -0
  103. package/examples/basic/.toil/routes.ts +9 -7
  104. package/examples/basic/build/client/assets/404-Bq0jNTUo.js +1 -0
  105. package/examples/basic/build/client/assets/_...slug_-CXKf6qnB.js +1 -0
  106. package/examples/basic/build/client/assets/_id_-BadAyQnb.js +1 -0
  107. package/examples/basic/build/client/assets/about-BOhoEcEO.js +1 -0
  108. package/examples/basic/build/client/assets/get-started-BIXpcjkT.js +9 -0
  109. package/examples/basic/build/client/assets/index-BmqcTaBB.js +1 -0
  110. package/examples/basic/build/client/assets/io-DEVjjaJj.js +1 -0
  111. package/examples/basic/build/client/assets/layout-DJegirdz.js +1 -0
  112. package/examples/basic/build/client/assets/react-DEQrz1q7.js +9 -0
  113. package/examples/basic/build/client/assets/rolldown-runtime-KL5VtC6j.js +1 -0
  114. package/examples/basic/build/client/assets/routes-BYWn6TxK.js +1 -0
  115. package/examples/basic/build/client/css/style.css +2 -0
  116. package/examples/basic/build/client/images/.gitkeep +1 -0
  117. package/examples/basic/build/client/images/logo.svg +37 -0
  118. package/examples/basic/build/client/index.html +17 -0
  119. package/examples/basic/build/client/robots.txt +2 -0
  120. package/examples/basic/client/404.tsx +2 -5
  121. package/examples/basic/client/components/.gitkeep +1 -0
  122. package/examples/basic/client/components/Footer.tsx +8 -0
  123. package/examples/basic/client/components/HoneycombBackground.tsx +162 -0
  124. package/examples/basic/client/layout.tsx +43 -26
  125. package/examples/basic/client/public/favicon.ico +0 -0
  126. package/examples/basic/client/public/images/.gitkeep +1 -0
  127. package/examples/basic/client/public/images/logo.svg +37 -0
  128. package/examples/basic/client/public/index.html +16 -0
  129. package/examples/basic/client/public/robots.txt +2 -0
  130. package/examples/basic/client/routes/about.tsx +1 -3
  131. package/examples/basic/client/routes/blog/[id].tsx +2 -4
  132. package/examples/basic/client/routes/docs/[...slug].tsx +3 -6
  133. package/examples/basic/client/routes/get-started.tsx +84 -0
  134. package/examples/basic/client/routes/index.tsx +74 -7
  135. package/examples/basic/client/routes/io.tsx +3 -7
  136. package/examples/basic/client/styles/main.css +461 -0
  137. package/examples/basic/client/toil.tsx +7 -0
  138. package/examples/basic/node_modules/.bin/toilinit +16 -0
  139. package/examples/basic/node_modules/.bin/toilinit.cmd +17 -0
  140. package/examples/basic/node_modules/.bin/toilinit.ps1 +28 -0
  141. package/examples/basic/node_modules/.bin/toilscript +16 -0
  142. package/examples/basic/node_modules/.bin/toilscript.cmd +17 -0
  143. package/examples/basic/node_modules/.bin/toilscript.ps1 +28 -0
  144. package/examples/basic/node_modules/.bin/wasm-as +16 -0
  145. package/examples/basic/node_modules/.bin/wasm-as.cmd +17 -0
  146. package/examples/basic/node_modules/.bin/wasm-as.ps1 +28 -0
  147. package/examples/basic/node_modules/.bin/wasm-ctor-eval +16 -0
  148. package/examples/basic/node_modules/.bin/wasm-ctor-eval.cmd +17 -0
  149. package/examples/basic/node_modules/.bin/wasm-ctor-eval.ps1 +28 -0
  150. package/examples/basic/node_modules/.bin/wasm-dis +16 -0
  151. package/examples/basic/node_modules/.bin/wasm-dis.cmd +17 -0
  152. package/examples/basic/node_modules/.bin/wasm-dis.ps1 +28 -0
  153. package/examples/basic/node_modules/.bin/wasm-merge +16 -0
  154. package/examples/basic/node_modules/.bin/wasm-merge.cmd +17 -0
  155. package/examples/basic/node_modules/.bin/wasm-merge.ps1 +28 -0
  156. package/examples/basic/node_modules/.bin/wasm-metadce +16 -0
  157. package/examples/basic/node_modules/.bin/wasm-metadce.cmd +17 -0
  158. package/examples/basic/node_modules/.bin/wasm-metadce.ps1 +28 -0
  159. package/examples/basic/node_modules/.bin/wasm-opt +16 -0
  160. package/examples/basic/node_modules/.bin/wasm-opt.cmd +17 -0
  161. package/examples/basic/node_modules/.bin/wasm-opt.ps1 +28 -0
  162. package/examples/basic/node_modules/.bin/wasm-reduce +16 -0
  163. package/examples/basic/node_modules/.bin/wasm-reduce.cmd +17 -0
  164. package/examples/basic/node_modules/.bin/wasm-reduce.ps1 +28 -0
  165. package/examples/basic/node_modules/.bin/wasm-shell +16 -0
  166. package/examples/basic/node_modules/.bin/wasm-shell.cmd +17 -0
  167. package/examples/basic/node_modules/.bin/wasm-shell.ps1 +28 -0
  168. package/examples/basic/node_modules/.bin/wasm2js +16 -0
  169. package/examples/basic/node_modules/.bin/wasm2js.cmd +17 -0
  170. package/examples/basic/node_modules/.bin/wasm2js.ps1 +28 -0
  171. package/examples/basic/node_modules/.package-lock.json +49 -1
  172. package/examples/basic/node_modules/.vite/deps/_metadata.json +9 -9
  173. package/examples/basic/node_modules/binaryen/LICENSE +201 -0
  174. package/examples/basic/node_modules/binaryen/README.md +1362 -0
  175. package/examples/basic/node_modules/binaryen/bin/package.json +3 -0
  176. package/examples/basic/node_modules/binaryen/bin/wasm-as +0 -0
  177. package/examples/basic/node_modules/binaryen/bin/wasm-ctor-eval +0 -0
  178. package/examples/basic/node_modules/binaryen/bin/wasm-dis +0 -0
  179. package/examples/basic/node_modules/binaryen/bin/wasm-merge +0 -0
  180. package/examples/basic/node_modules/binaryen/bin/wasm-metadce +0 -0
  181. package/examples/basic/node_modules/binaryen/bin/wasm-opt +0 -0
  182. package/examples/basic/node_modules/binaryen/bin/wasm-reduce +0 -0
  183. package/examples/basic/node_modules/binaryen/bin/wasm-shell +0 -0
  184. package/examples/basic/node_modules/binaryen/bin/wasm2js +0 -0
  185. package/examples/basic/node_modules/binaryen/index.d.ts +2371 -0
  186. package/examples/basic/node_modules/binaryen/index.js +30552 -0
  187. package/examples/basic/node_modules/binaryen/package.json +50 -0
  188. package/examples/basic/node_modules/long/LICENSE +202 -0
  189. package/examples/basic/node_modules/long/README.md +286 -0
  190. package/examples/basic/node_modules/long/index.d.ts +2 -0
  191. package/examples/basic/node_modules/long/index.js +1581 -0
  192. package/examples/basic/node_modules/long/package.json +58 -0
  193. package/examples/basic/node_modules/long/types.d.ts +474 -0
  194. package/examples/basic/node_modules/long/umd/index.d.ts +3 -0
  195. package/examples/basic/node_modules/long/umd/index.js +1622 -0
  196. package/examples/basic/node_modules/long/umd/package.json +3 -0
  197. package/examples/basic/node_modules/long/umd/types.d.ts +474 -0
  198. package/examples/basic/node_modules/toilscript/LICENSE +201 -0
  199. package/examples/basic/node_modules/toilscript/NOTICE +94 -0
  200. package/examples/basic/node_modules/toilscript/README.md +66 -0
  201. package/examples/basic/node_modules/toilscript/bin/toilinit.js +468 -0
  202. package/examples/basic/node_modules/toilscript/bin/toilscript.js +35 -0
  203. package/examples/basic/node_modules/toilscript/dist/cli.d.ts +4 -0
  204. package/examples/basic/node_modules/toilscript/dist/cli.generated.d.ts +10027 -0
  205. package/examples/basic/node_modules/toilscript/dist/cli.js +24474 -0
  206. package/examples/basic/node_modules/toilscript/dist/cli.js.map +7 -0
  207. package/examples/basic/node_modules/toilscript/dist/importmap.json +9 -0
  208. package/examples/basic/node_modules/toilscript/dist/toilscript.d.ts +4 -0
  209. package/examples/basic/node_modules/toilscript/dist/toilscript.generated.d.ts +11242 -0
  210. package/examples/basic/node_modules/toilscript/dist/toilscript.js +337 -0
  211. package/examples/basic/node_modules/toilscript/dist/toilscript.js.map +7 -0
  212. package/examples/basic/node_modules/toilscript/dist/transform.cjs +1 -0
  213. package/examples/basic/node_modules/toilscript/dist/transform.d.ts +1 -0
  214. package/examples/basic/node_modules/toilscript/dist/transform.js +1 -0
  215. package/examples/basic/node_modules/toilscript/dist/web.js +22 -0
  216. package/examples/basic/node_modules/toilscript/lib/binaryen.d.ts +2 -0
  217. package/examples/basic/node_modules/toilscript/lib/binaryen.js +2 -0
  218. package/examples/basic/node_modules/toilscript/package.json +115 -0
  219. package/examples/basic/node_modules/toilscript/std/README.md +6 -0
  220. package/examples/basic/node_modules/toilscript/std/assembly/array.ts +550 -0
  221. package/examples/basic/node_modules/toilscript/std/assembly/arraybuffer.ts +77 -0
  222. package/examples/basic/node_modules/toilscript/std/assembly/atomics.ts +127 -0
  223. package/examples/basic/node_modules/toilscript/std/assembly/bindings/asyncify.ts +16 -0
  224. package/examples/basic/node_modules/toilscript/std/assembly/bindings/dom.ts +291 -0
  225. package/examples/basic/node_modules/toilscript/std/assembly/bindings/node.ts +6 -0
  226. package/examples/basic/node_modules/toilscript/std/assembly/bitflags.ts +53 -0
  227. package/examples/basic/node_modules/toilscript/std/assembly/builtins.ts +2650 -0
  228. package/examples/basic/node_modules/toilscript/std/assembly/byteslice.ts +177 -0
  229. package/examples/basic/node_modules/toilscript/std/assembly/compat.ts +2 -0
  230. package/examples/basic/node_modules/toilscript/std/assembly/console.ts +42 -0
  231. package/examples/basic/node_modules/toilscript/std/assembly/crypto.ts +9 -0
  232. package/examples/basic/node_modules/toilscript/std/assembly/dataview.ts +181 -0
  233. package/examples/basic/node_modules/toilscript/std/assembly/date.ts +375 -0
  234. package/examples/basic/node_modules/toilscript/std/assembly/diagnostics.ts +11 -0
  235. package/examples/basic/node_modules/toilscript/std/assembly/encoding.ts +151 -0
  236. package/examples/basic/node_modules/toilscript/std/assembly/endian.ts +45 -0
  237. package/examples/basic/node_modules/toilscript/std/assembly/error.ts +44 -0
  238. package/examples/basic/node_modules/toilscript/std/assembly/fixedarray.ts +173 -0
  239. package/examples/basic/node_modules/toilscript/std/assembly/fixedmap.ts +326 -0
  240. package/examples/basic/node_modules/toilscript/std/assembly/fixedset.ts +275 -0
  241. package/examples/basic/node_modules/toilscript/std/assembly/function.ts +42 -0
  242. package/examples/basic/node_modules/toilscript/std/assembly/index.d.ts +2892 -0
  243. package/examples/basic/node_modules/toilscript/std/assembly/iterator.ts +35 -0
  244. package/examples/basic/node_modules/toilscript/std/assembly/map.ts +269 -0
  245. package/examples/basic/node_modules/toilscript/std/assembly/math.ts +3289 -0
  246. package/examples/basic/node_modules/toilscript/std/assembly/memory.ts +123 -0
  247. package/examples/basic/node_modules/toilscript/std/assembly/number.ts +388 -0
  248. package/examples/basic/node_modules/toilscript/std/assembly/object.ts +36 -0
  249. package/examples/basic/node_modules/toilscript/std/assembly/performance.ts +9 -0
  250. package/examples/basic/node_modules/toilscript/std/assembly/pointer.ts +80 -0
  251. package/examples/basic/node_modules/toilscript/std/assembly/polyfills.ts +27 -0
  252. package/examples/basic/node_modules/toilscript/std/assembly/process.ts +50 -0
  253. package/examples/basic/node_modules/toilscript/std/assembly/reference.ts +48 -0
  254. package/examples/basic/node_modules/toilscript/std/assembly/regexp.ts +12 -0
  255. package/examples/basic/node_modules/toilscript/std/assembly/rt/README.md +83 -0
  256. package/examples/basic/node_modules/toilscript/std/assembly/rt/common.ts +81 -0
  257. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-incremental.ts +2 -0
  258. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-memory.ts +1 -0
  259. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-minimal.ts +2 -0
  260. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-stub.ts +1 -0
  261. package/examples/basic/node_modules/toilscript/std/assembly/rt/index.d.ts +37 -0
  262. package/examples/basic/node_modules/toilscript/std/assembly/rt/itcms.ts +419 -0
  263. package/examples/basic/node_modules/toilscript/std/assembly/rt/memory-runtime.ts +94 -0
  264. package/examples/basic/node_modules/toilscript/std/assembly/rt/rtrace.ts +15 -0
  265. package/examples/basic/node_modules/toilscript/std/assembly/rt/stub.ts +133 -0
  266. package/examples/basic/node_modules/toilscript/std/assembly/rt/tcms.ts +254 -0
  267. package/examples/basic/node_modules/toilscript/std/assembly/rt/tlsf.ts +592 -0
  268. package/examples/basic/node_modules/toilscript/std/assembly/rt.ts +90 -0
  269. package/examples/basic/node_modules/toilscript/std/assembly/set.ts +225 -0
  270. package/examples/basic/node_modules/toilscript/std/assembly/shared/feature.ts +68 -0
  271. package/examples/basic/node_modules/toilscript/std/assembly/shared/runtime.ts +13 -0
  272. package/examples/basic/node_modules/toilscript/std/assembly/shared/target.ts +11 -0
  273. package/examples/basic/node_modules/toilscript/std/assembly/shared/tsconfig.json +11 -0
  274. package/examples/basic/node_modules/toilscript/std/assembly/shared/typeinfo.ts +72 -0
  275. package/examples/basic/node_modules/toilscript/std/assembly/staticarray.ts +423 -0
  276. package/examples/basic/node_modules/toilscript/std/assembly/string.ts +850 -0
  277. package/examples/basic/node_modules/toilscript/std/assembly/symbol.ts +114 -0
  278. package/examples/basic/node_modules/toilscript/std/assembly/table.ts +16 -0
  279. package/examples/basic/node_modules/toilscript/std/assembly/toilscript.ts +16 -0
  280. package/examples/basic/node_modules/toilscript/std/assembly/tsconfig.json +6 -0
  281. package/examples/basic/node_modules/toilscript/std/assembly/typedarray.ts +1954 -0
  282. package/examples/basic/node_modules/toilscript/std/assembly/uri.ts +17 -0
  283. package/examples/basic/node_modules/toilscript/std/assembly/util/bytes.ts +107 -0
  284. package/examples/basic/node_modules/toilscript/std/assembly/util/casemap.ts +497 -0
  285. package/examples/basic/node_modules/toilscript/std/assembly/util/error.ts +58 -0
  286. package/examples/basic/node_modules/toilscript/std/assembly/util/hash.ts +117 -0
  287. package/examples/basic/node_modules/toilscript/std/assembly/util/math.ts +1922 -0
  288. package/examples/basic/node_modules/toilscript/std/assembly/util/memory.ts +290 -0
  289. package/examples/basic/node_modules/toilscript/std/assembly/util/number.ts +873 -0
  290. package/examples/basic/node_modules/toilscript/std/assembly/util/sort.ts +313 -0
  291. package/examples/basic/node_modules/toilscript/std/assembly/util/string.ts +1202 -0
  292. package/examples/basic/node_modules/toilscript/std/assembly/util/uri.ts +275 -0
  293. package/examples/basic/node_modules/toilscript/std/assembly/vector.ts +4 -0
  294. package/examples/basic/node_modules/toilscript/std/assembly.json +16 -0
  295. package/examples/basic/node_modules/toilscript/std/portable/index.d.ts +461 -0
  296. package/examples/basic/node_modules/toilscript/std/portable/index.js +416 -0
  297. package/examples/basic/node_modules/toilscript/std/portable.json +11 -0
  298. package/examples/basic/node_modules/toilscript/std/types/assembly/index.d.ts +1 -0
  299. package/examples/basic/node_modules/toilscript/std/types/assembly/package.json +3 -0
  300. package/examples/basic/node_modules/toilscript/std/types/portable/index.d.ts +1 -0
  301. package/examples/basic/node_modules/toilscript/std/types/portable/package.json +3 -0
  302. package/examples/basic/node_modules/toilscript/tsconfig-base.json +13 -0
  303. package/examples/basic/node_modules/toilscript/util/README.md +23 -0
  304. package/examples/basic/node_modules/toilscript/util/browser/fs.js +1 -0
  305. package/examples/basic/node_modules/toilscript/util/browser/module.js +5 -0
  306. package/examples/basic/node_modules/toilscript/util/browser/path.js +520 -0
  307. package/examples/basic/node_modules/toilscript/util/browser/process.js +59 -0
  308. package/examples/basic/node_modules/toilscript/util/browser/url.js +23 -0
  309. package/examples/basic/node_modules/toilscript/util/cpu.d.ts +9 -0
  310. package/examples/basic/node_modules/toilscript/util/cpu.js +42 -0
  311. package/examples/basic/node_modules/toilscript/util/find.d.ts +6 -0
  312. package/examples/basic/node_modules/toilscript/util/find.js +20 -0
  313. package/examples/basic/node_modules/toilscript/util/node.d.ts +21 -0
  314. package/examples/basic/node_modules/toilscript/util/node.js +34 -0
  315. package/examples/basic/node_modules/toilscript/util/options.d.ts +70 -0
  316. package/examples/basic/node_modules/toilscript/util/options.js +262 -0
  317. package/examples/basic/node_modules/toilscript/util/terminal.d.ts +52 -0
  318. package/examples/basic/node_modules/toilscript/util/terminal.js +35 -0
  319. package/examples/basic/node_modules/toilscript/util/text.d.ts +26 -0
  320. package/examples/basic/node_modules/toilscript/util/text.js +114 -0
  321. package/examples/basic/node_modules/toilscript/util/tsconfig.json +9 -0
  322. package/examples/basic/node_modules/toilscript/util/web.d.ts +11 -0
  323. package/examples/basic/node_modules/toilscript/util/web.js +33 -0
  324. package/examples/basic/package-lock.json +50 -1
  325. package/examples/basic/package.json +5 -2
  326. package/examples/basic/server/index.ts +3 -0
  327. package/examples/basic/server/main.ts +6 -0
  328. package/examples/basic/server/tsconfig.json +7 -0
  329. package/examples/basic/toil-env.d.ts +20 -1
  330. package/examples/basic/toil.config.ts +2 -5
  331. package/examples/basic/toilconfig.json +30 -0
  332. package/package.json +2 -2
  333. package/presets/eslint.js +2 -7
  334. package/presets/no-uint8array-tostring.js +4 -5
  335. package/presets/prettier.json +8 -1
  336. package/src/backend/index.ts +11 -18
  337. package/src/cli/configure.ts +272 -0
  338. package/src/cli/create.ts +267 -82
  339. package/src/cli/features.ts +128 -0
  340. package/src/cli/index.ts +44 -3
  341. package/src/cli/proc.ts +20 -0
  342. package/src/cli/ui.ts +4 -6
  343. package/src/cli/validate.ts +31 -0
  344. package/src/client/head/head.ts +140 -0
  345. package/src/client/index.ts +39 -9
  346. package/src/client/navigation/Link.tsx +99 -0
  347. package/src/client/navigation/NavLink.tsx +86 -0
  348. package/src/client/navigation/navigation.ts +142 -0
  349. package/src/client/navigation/prefetch.ts +130 -0
  350. package/src/client/navigation/scroll.ts +53 -0
  351. package/src/client/routing/Router.tsx +95 -0
  352. package/src/client/routing/error-boundary.tsx +43 -0
  353. package/src/client/routing/hooks.ts +115 -0
  354. package/src/client/routing/lazy.ts +93 -0
  355. package/src/client/{match.ts → routing/match.ts} +11 -3
  356. package/src/client/routing/mount.tsx +28 -0
  357. package/src/client/routing/params-context.ts +10 -0
  358. package/src/client/types.ts +36 -0
  359. package/src/compiler/config.ts +26 -10
  360. package/src/compiler/docs.ts +87 -0
  361. package/src/compiler/generate.ts +180 -23
  362. package/src/compiler/index.ts +6 -4
  363. package/src/compiler/plugin.ts +22 -1
  364. package/src/compiler/routes.ts +13 -7
  365. package/src/compiler/vite.ts +28 -5
  366. package/src/io/BinaryReader.ts +1 -5
  367. package/src/io/BinaryWriter.ts +3 -3
  368. package/src/server/index.ts +3 -4
  369. package/src/server/tsconfig.json +4 -0
  370. package/templates/app/client/404.tsx +11 -0
  371. package/templates/app/client/components/.gitkeep +1 -0
  372. package/templates/app/client/components/Footer.tsx +8 -0
  373. package/templates/app/client/components/HoneycombBackground.tsx +162 -0
  374. package/templates/app/client/layout.tsx +53 -0
  375. package/templates/app/client/public/favicon.ico +0 -0
  376. package/templates/app/client/public/images/.gitkeep +1 -0
  377. package/templates/app/client/public/images/logo.svg +37 -0
  378. package/templates/app/client/public/index.html +16 -0
  379. package/templates/app/client/public/robots.txt +2 -0
  380. package/templates/app/client/routes/about.tsx +11 -0
  381. package/templates/app/client/routes/blog/[id].tsx +12 -0
  382. package/templates/app/client/routes/docs/[...slug].tsx +12 -0
  383. package/templates/app/client/routes/get-started.tsx +84 -0
  384. package/templates/app/client/routes/index.tsx +80 -0
  385. package/templates/app/client/routes/io.tsx +24 -0
  386. package/templates/app/client/styles/main.css +461 -0
  387. package/templates/app/client/toil.tsx +7 -0
  388. package/test/channel.test.ts +1 -1
  389. package/test/configure.test.ts +90 -0
  390. package/test/features.test.ts +111 -0
  391. package/test/head.test.ts +35 -0
  392. package/test/io.test.ts +8 -0
  393. package/test/navlink.test.ts +28 -0
  394. package/test/routes.test.ts +16 -1
  395. package/test/validate.test.ts +42 -0
  396. package/vitest.config.ts +1 -1
  397. package/examples/basic/dist/assets/404-D1bS2aH_.js +0 -1
  398. package/examples/basic/dist/assets/_...slug_-wR3shlWn.js +0 -1
  399. package/examples/basic/dist/assets/_id_-EWYvHfi2.js +0 -1
  400. package/examples/basic/dist/assets/about-Ddvj1tjF.js +0 -1
  401. package/examples/basic/dist/assets/index-CdG0me90.js +0 -1
  402. package/examples/basic/dist/assets/io-CODNJU57.js +0 -1
  403. package/examples/basic/dist/assets/layout-C15ZTPYI.js +0 -1
  404. package/examples/basic/dist/assets/react-JbAfoxYe.js +0 -9
  405. package/examples/basic/dist/assets/rolldown-runtime-1VNLd2iN.js +0 -1
  406. package/examples/basic/dist/assets/routes-GoydenoY.js +0 -1
  407. package/examples/basic/dist/index.html +0 -12
  408. package/src/client/runtime.tsx +0 -190
  409. /package/src/client/{channel.ts → channel/channel.ts} +0 -0
@@ -6,7 +6,8 @@ export type RouteParams = Record<string, string>;
6
6
  * Pure and runtime-agnostic (used by the router and unit-tested directly).
7
7
  * matchRoute('/', '/') -> {}
8
8
  * matchRoute('/blog/:id', '/blog/42') -> { id: '42' }
9
- * matchRoute('/docs/*slug', '/docs/a/b') -> { slug: 'a/b' } (catch-all)
9
+ * matchRoute('/docs/*slug', '/docs/a/b') -> { slug: 'a/b' } (catch-all, 1+ segments)
10
+ * matchRoute('/docs/**slug', '/docs') -> { slug: '' } (optional catch-all, 0+ segments)
10
11
  * matchRoute('/about', '/x') -> null
11
12
  */
12
13
  export function matchRoute(pattern: string, pathname: string): RouteParams | null {
@@ -17,7 +18,15 @@ export function matchRoute(pattern: string, pathname: string): RouteParams | nul
17
18
  for (let i = 0; i < patternSegs.length; i++) {
18
19
  const p = patternSegs[i];
19
20
 
20
- // Catch-all (`*slug`): captures the rest of the path (one or more segments).
21
+ // Optional catch-all (`**slug`): captures the rest of the path, matching zero or more segments.
22
+ if (p.startsWith('**')) {
23
+ params[p.slice(2)] = pathSegs
24
+ .slice(i)
25
+ .map((s) => decodeURIComponent(s))
26
+ .join('/');
27
+ return params;
28
+ }
29
+
21
30
  if (p.startsWith('*')) {
22
31
  const rest = pathSegs.slice(i);
23
32
  if (rest.length === 0) return null;
@@ -34,6 +43,5 @@ export function matchRoute(pattern: string, pathname: string): RouteParams | nul
34
43
  }
35
44
  }
36
45
 
37
- // No catch-all consumed the tail: lengths must match exactly.
38
46
  return patternSegs.length === pathSegs.length ? params : null;
39
47
  }
@@ -0,0 +1,28 @@
1
+ import { createRoot } from 'react-dom/client';
2
+
3
+ import { initNavigation } from '../navigation/navigation.js';
4
+ import { startPrefetcher } from '../navigation/prefetch.js';
5
+ import { Router } from './Router.js';
6
+ import type { LayoutLoader, NotFoundLoader, RouteDef } from '../types.js';
7
+
8
+ /**
9
+ * Mounts the toil client app into `#root` and starts idle link prefetching. Called by the
10
+ * compiler-generated `.toil/entry.tsx`.
11
+ */
12
+ export function mount(
13
+ routes: RouteDef[],
14
+ layout: LayoutLoader = null,
15
+ notFound: NotFoundLoader = null,
16
+ ): void {
17
+ const el = document.getElementById('root');
18
+ if (!el) throw new Error('toil: #root element not found');
19
+ initNavigation();
20
+ createRoot(el).render(
21
+ <Router
22
+ routes={routes}
23
+ layout={layout}
24
+ notFound={notFound}
25
+ />,
26
+ );
27
+ startPrefetcher(routes);
28
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * React context carrying the current route's dynamic params. The provider is set by {@link Router};
3
+ * read it with the {@link useParams} hook rather than consuming the context directly.
4
+ */
5
+ import { createContext } from 'react';
6
+
7
+ import type { RouteParams } from './match.js';
8
+
9
+ /** Holds the params extracted from the active route (e.g. `{ id }` for `/blog/:id`). */
10
+ export const ParamsContext = createContext<RouteParams>({});
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Public router types shared across the client runtime. Kept dependency-free (type-only React
3
+ * imports) so any module can import them without pulling in component or DOM code.
4
+ */
5
+ import type { ComponentType, ReactNode } from 'react';
6
+
7
+ /** Lazy loader for a layout component (wraps children). */
8
+ export type LayoutComponentLoader = () => Promise<{
9
+ default: ComponentType<{ children?: ReactNode }>;
10
+ }>;
11
+
12
+ /** Props passed to an `error.tsx` component: the thrown error and a function to retry rendering. */
13
+ export interface RouteErrorProps {
14
+ readonly error: Error;
15
+ readonly reset: () => void;
16
+ }
17
+
18
+ /**
19
+ * A route entry produced by the compiler: a URL pattern, a lazy loader for its page component, and
20
+ * the chain of nested layout loaders (shallowest → deepest, from nested `layout.tsx` files) that wrap it.
21
+ */
22
+ export interface RouteDef {
23
+ readonly pattern: string;
24
+ readonly load: () => Promise<{ default: ComponentType }>;
25
+ readonly layouts?: readonly LayoutComponentLoader[];
26
+ /** Nearest `loading.tsx` — shown as the Suspense fallback while this route loads. */
27
+ readonly loading?: () => Promise<{ default: ComponentType }>;
28
+ /** Nearest `error.tsx` — rendered by an error boundary around this route. */
29
+ readonly errorComponent?: () => Promise<{ default: ComponentType<RouteErrorProps> }>;
30
+ }
31
+
32
+ /** Optional root layout loader (wraps every page). `null` when the project defines no layout. */
33
+ export type LayoutLoader = LayoutComponentLoader | null;
34
+
35
+ /** Optional custom not-found (404) page loader, rendered when no route matches. */
36
+ export type NotFoundLoader = (() => Promise<{ default: ComponentType }>) | null;
@@ -12,7 +12,13 @@ export interface ClientConfig {
12
12
  readonly srcDir?: string;
13
13
  /** Routes directory, relative to `srcDir`. Default `routes`. */
14
14
  readonly routesDir?: string;
15
- /** Production output directory, relative to root. Default `dist`. */
15
+ /**
16
+ * Static assets directory, relative to root. Default `<srcDir>/public` (e.g. `client/public`).
17
+ * Holds the `index.html` template (owned and edited by you) plus any files served as-is at the
18
+ * base path (favicons, images, …).
19
+ */
20
+ readonly publicDir?: string;
21
+ /** Production output directory, relative to root. Default `build/client`. */
16
22
  readonly outDir?: string;
17
23
  /** Public base path. Default `/`. */
18
24
  readonly base?: string;
@@ -27,7 +33,7 @@ export interface ClientConfig {
27
33
  }
28
34
 
29
35
  /**
30
- * Server-side (AssemblyScript → WASM) configuration. Reserved: the compiler does not yet
36
+ * Server-side (toilscript → WASM) configuration. Reserved: the compiler does not yet
31
37
  * build the server target via `toil build`; today it is compiled by `toilscript` directly.
32
38
  */
33
39
  export interface ServerConfig {
@@ -46,7 +52,7 @@ export interface ToilConfig {
46
52
  readonly root?: string;
47
53
  /** Client (TSX/React/Vite) configuration. */
48
54
  readonly client?: ClientConfig;
49
- /** Server (AssemblyScript/WASM) configuration. */
55
+ /** Server (toilscript/WASM) configuration. */
50
56
  readonly server?: ServerConfig;
51
57
  }
52
58
 
@@ -56,6 +62,8 @@ export interface ResolvedToilConfig {
56
62
  readonly srcDir: string;
57
63
  readonly clientAbsDir: string;
58
64
  readonly routesAbsDir: string;
65
+ /** Absolute path to the static-assets dir (holds the `index.html` template). */
66
+ readonly publicDir: string;
59
67
  readonly toilDir: string;
60
68
  readonly outDir: string;
61
69
  readonly base: string;
@@ -70,14 +78,23 @@ export function defineConfig(config: ToilConfig): ToilConfig {
70
78
  return config;
71
79
  }
72
80
 
73
- const CONFIG_NAMES = ['toil.config.ts', 'toil.config.mts', 'toil.config.js', 'toil.config.mjs'];
81
+ const CONFIG_NAMES = [
82
+ 'toil.config.ts',
83
+ 'toil.config.mts',
84
+ 'toil.config.js',
85
+ 'toil.config.mjs',
86
+ 'toiljs.config.ts',
87
+ 'toiljs.config.mts',
88
+ 'toiljs.config.js',
89
+ 'toiljs.config.mjs',
90
+ ];
74
91
 
75
92
  /** Path to the built client runtime (`build/client/index.js`), sibling to `build/compiler`. */
76
93
  function resolveRuntimePath(): string {
77
94
  return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../client/index.js');
78
95
  }
79
96
 
80
- /** Finds and loads `toil.config.*` from `root` (via Vite's bundling loader), then resolves defaults. */
97
+ /** Finds and loads `toil.config.*` or `toiljs.config.*` from `root`, then resolves defaults. */
81
98
  export async function loadConfig(
82
99
  opts: { root?: string; port?: number } = {},
83
100
  ): Promise<ResolvedToilConfig> {
@@ -87,10 +104,6 @@ export async function loadConfig(
87
104
  for (const name of CONFIG_NAMES) {
88
105
  const candidate = path.join(root, name);
89
106
  if (fs.existsSync(candidate)) {
90
- // Native ESM import (Node strips types from .ts/.mts). This keeps config loading
91
- // independent of the project's tsconfig — which may `extends` a not-yet-installed
92
- // package and would otherwise throw TSCONFIG_ERROR — and a file URL imports absolute
93
- // paths correctly on Windows.
94
107
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- dynamic import() is typed `any`
95
108
  const loaded: { default?: ToilConfig } = await import(pathToFileURL(candidate).href);
96
109
  if (loaded.default) user = loaded.default;
@@ -108,8 +121,11 @@ export async function loadConfig(
108
121
  srcDir,
109
122
  clientAbsDir,
110
123
  routesAbsDir: path.join(clientAbsDir, routesDir),
124
+ publicDir: client.publicDir
125
+ ? path.resolve(root, client.publicDir)
126
+ : path.join(clientAbsDir, 'public'),
111
127
  toilDir: path.join(root, '.toil'),
112
- outDir: client.outDir ?? 'dist',
128
+ outDir: client.outDir ?? 'build/client',
113
129
  base: client.base ?? '/',
114
130
  port: opts.port ?? client.port ?? 3000,
115
131
  runtimePath: resolveRuntimePath(),
@@ -0,0 +1,87 @@
1
+ /**
2
+ * AI-assistant helper files. `toiljs create` writes a small pointer file for each tool at the
3
+ * project root (committed, yours to edit); the real documentation lives under `.toil/docs/`,
4
+ * regenerated on every `toiljs dev` / `toiljs build` so it stays in sync with the installed
5
+ * toiljs version. The pointer files just tell the agent to read `.toil/docs/`.
6
+ */
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+
10
+ /** Shared body for the per-tool pointer files. */
11
+ const POINTER_BODY = `# toiljs · AI assistant guide
12
+
13
+ This is a **toiljs** project — a full-stack React framework (React + Vite client, file-based
14
+ routing, and a toilscript→WebAssembly server).
15
+
16
+ **Before editing this project, read the generated documentation in \`.toil/docs/\`.** It describes
17
+ the conventions you must follow:
18
+
19
+ - \`.toil/docs/index.md\` — overview and project layout
20
+ - \`.toil/docs/routing.md\` — file-based routing, nested layouts, loading / error files
21
+ - \`.toil/docs/client.md\` — the \`Toil\` global, Link / NavLink, router hooks
22
+ - \`.toil/docs/styling.md\` — CSS / Sass / Less / Stylus / Tailwind (via \`toiljs configure\`)
23
+ - \`.toil/docs/server.md\` — the toilscript server target
24
+ - \`.toil/docs/cli.md\` — toiljs CLI commands
25
+
26
+ \`.toil/docs/\` is regenerated by toiljs; do not edit it by hand. This pointer file is yours to edit.
27
+ `;
28
+
29
+ /** A selectable AI coding-tool helper: its id, label, and the root pointer file(s) it scaffolds. */
30
+ export interface AiHelper {
31
+ readonly id: string;
32
+ readonly label: string;
33
+ readonly files: Record<string, string>;
34
+ }
35
+
36
+ /**
37
+ * Per-tool pointer files, individually selectable in `toiljs create`. Each tool reads its own
38
+ * conventional file: Claude Code → CLAUDE.md, Codex / others → AGENTS.md, Cursor → .cursor/rules,
39
+ * GitHub Copilot → .github/copilot-instructions.md. Written once by `create`; not regenerated.
40
+ */
41
+ export const AI_HELPERS: readonly AiHelper[] = [
42
+ { id: 'claude', label: 'Claude Code (CLAUDE.md)', files: { 'CLAUDE.md': POINTER_BODY } },
43
+ { id: 'codex', label: 'Codex / AGENTS.md', files: { 'AGENTS.md': POINTER_BODY } },
44
+ {
45
+ id: 'cursor',
46
+ label: 'Cursor (.cursor/rules)',
47
+ files: {
48
+ '.cursor/rules/toiljs.mdc': `---\ndescription: toiljs project conventions\nalwaysApply: true\n---\n\n${POINTER_BODY}`,
49
+ },
50
+ },
51
+ {
52
+ id: 'copilot',
53
+ label: 'GitHub Copilot (.github/copilot-instructions.md)',
54
+ files: { '.github/copilot-instructions.md': POINTER_BODY },
55
+ },
56
+ ];
57
+
58
+ /** All AI helper ids (the default selection). */
59
+ export const AI_HELPER_IDS: readonly string[] = AI_HELPERS.map((h) => h.id);
60
+
61
+ /** Merges the pointer files for the given helper ids into one path → content map. */
62
+ export function aiHelperFiles(ids: readonly string[]): Record<string, string> {
63
+ const out: Record<string, string> = {};
64
+ for (const helper of AI_HELPERS) {
65
+ if (ids.includes(helper.id)) Object.assign(out, helper.files);
66
+ }
67
+ return out;
68
+ }
69
+
70
+ /** The framework docs written into `.toil/docs/` (placeholders for now), keyed by filename. */
71
+ export const TOIL_DOCS: Record<string, string> = {
72
+ 'index.md': '# toiljs\n\n<!-- TODO: overview and project layout -->\n',
73
+ 'routing.md': '# Routing\n\n<!-- TODO: file-based routing, nested layouts, loading / error -->\n',
74
+ 'client.md': '# Client runtime\n\n<!-- TODO: Toil global, Link / NavLink, router hooks -->\n',
75
+ 'styling.md': '# Styling\n\n<!-- TODO: CSS / preprocessors / Tailwind via `toiljs configure` -->\n',
76
+ 'server.md': '# Server (toilscript → WebAssembly)\n\n<!-- TODO -->\n',
77
+ 'cli.md': '# CLI\n\n<!-- TODO: create / dev / build / start / configure -->\n',
78
+ };
79
+
80
+ /** Writes the framework docs into `<toilDir>/docs/`. Called by `generate` each dev/build. */
81
+ export function writeDocs(toilDir: string): void {
82
+ const dir = path.join(toilDir, 'docs');
83
+ fs.mkdirSync(dir, { recursive: true });
84
+ for (const [name, content] of Object.entries(TOIL_DOCS)) {
85
+ fs.writeFileSync(path.join(dir, name), content);
86
+ }
87
+ }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
4
  import { type ResolvedToilConfig } from './config.js';
5
+ import { writeDocs } from './docs.js';
5
6
  import { scanRoutes, type ScannedRoute } from './routes.js';
6
7
 
7
8
  /**
@@ -12,16 +13,52 @@ import { scanRoutes, type ScannedRoute } from './routes.js';
12
13
  * Lives at the project root because TypeScript's `include` globs skip dot-directories.
13
14
  * Exported so `toiljs create` can write it during scaffolding, before the first dev/build.
14
15
  */
16
+ /** Side-effect style imports (e.g. `import './styles/main.css'`). */
17
+ const STYLE_EXTENSIONS = ['css', 'scss', 'sass', 'less', 'styl', 'stylus', 'pcss', 'sss'];
18
+ /** Asset imports whose default export is the resolved URL string (e.g. `import logo from './logo.svg'`). */
19
+ const ASSET_EXTENSIONS = [
20
+ 'svg',
21
+ 'png',
22
+ 'jpg',
23
+ 'jpeg',
24
+ 'gif',
25
+ 'webp',
26
+ 'avif',
27
+ 'ico',
28
+ 'bmp',
29
+ 'apng',
30
+ ];
31
+
32
+ const STYLE_MODULES = STYLE_EXTENSIONS.map((ext) => `declare module '*.${ext}' {}`).join('\n');
33
+ const ASSET_MODULES = ASSET_EXTENSIONS.map(
34
+ (ext) => `declare module '*.${ext}' {\n const src: string;\n export default src;\n}`,
35
+ ).join('\n');
36
+
15
37
  export const TOIL_ENV_DTS =
16
38
  `// AUTO-GENERATED by toil — do not edit.\n` +
39
+ `declare const Toil: typeof import('toiljs/client');\n` +
17
40
  `declare const BinaryWriter: typeof import('toiljs/io').BinaryWriter;\n` +
18
41
  `declare const BinaryReader: typeof import('toiljs/io').BinaryReader;\n` +
19
42
  `declare const FastMap: typeof import('toiljs/io').FastMap;\n` +
20
- `declare const FastSet: typeof import('toiljs/io').FastSet;\n`;
43
+ `declare const FastSet: typeof import('toiljs/io').FastSet;\n` +
44
+ `\n` +
45
+ `${STYLE_MODULES}\n` +
46
+ `\n` +
47
+ `${ASSET_MODULES}\n` +
48
+ `\n` +
49
+ `declare module 'toiljs/routes' {\n` +
50
+ ` export const routes: import('toiljs/client').RouteDef[];\n` +
51
+ ` export const layout: import('toiljs/client').LayoutLoader;\n` +
52
+ ` export const notFound: import('toiljs/client').NotFoundLoader;\n` +
53
+ `}\n`;
21
54
 
22
- /** Returns a `./`-prefixed POSIX path from the `.toil` dir to `abs`, for use in generated imports. */
55
+ /**
56
+ * Returns a `./`-prefixed, **extensionless** POSIX module specifier from `.toil` to `abs`, for use
57
+ * in generated `import(...)` calls. Extensionless so TypeScript doesn't demand
58
+ * `allowImportingTsExtensions` (TS5097) when the generated files are checked; Vite still resolves it.
59
+ */
23
60
  function relFromToil(cfg: ResolvedToilConfig, abs: string): string {
24
- let rel = path.relative(cfg.toilDir, abs).replace(/\\/g, '/');
61
+ let rel = path.relative(cfg.toilDir, abs).replace(/\\/g, '/').replace(/\.(tsx|jsx)$/, '');
25
62
  if (!rel.startsWith('.')) rel = './' + rel;
26
63
  return rel;
27
64
  }
@@ -39,8 +76,57 @@ function findNotFound(cfg: ResolvedToilConfig): string | undefined {
39
76
  .find((p) => fs.existsSync(p));
40
77
  }
41
78
 
79
+ /** Finds the user-owned app entry at `client/toil.{tsx,jsx}` (where `mount` is called). */
80
+ function findEntry(cfg: ResolvedToilConfig): string | undefined {
81
+ return ['toil.tsx', 'toil.jsx']
82
+ .map((name) => path.join(cfg.clientAbsDir, name))
83
+ .find((p) => fs.existsSync(p));
84
+ }
85
+
86
+ /** A `layout.{tsx,jsx}` in `dir`, or undefined. */
87
+ function layoutIn(dir: string): string | undefined {
88
+ return ['layout.tsx', 'layout.jsx']
89
+ .map((name) => path.join(dir, name))
90
+ .find((p) => fs.existsSync(p));
91
+ }
92
+
93
+ /**
94
+ * Nested layout chain for a route file: `layout.{tsx,jsx}` at the routes root and each ancestor
95
+ * directory down to the file's own, shallowest → deepest. (The project root `client/layout.tsx`
96
+ * is handled separately as the top-level layout.)
97
+ */
98
+ function findLayoutChain(cfg: ResolvedToilConfig, routeFile: string): string[] {
99
+ const relDir = path.dirname(path.relative(cfg.routesAbsDir, routeFile));
100
+ const segments = relDir === '.' ? [] : relDir.split(path.sep);
101
+ const chain: string[] = [];
102
+ let dir = cfg.routesAbsDir;
103
+ for (let i = 0; i <= segments.length; i++) {
104
+ if (i > 0) dir = path.join(dir, segments[i - 1]);
105
+ const layout = layoutIn(dir);
106
+ if (layout) chain.push(layout);
107
+ }
108
+ return chain;
109
+ }
110
+
111
+ /** Nearest special file named `base` (e.g. `loading`/`error`) from the route's dir up to the routes root. */
112
+ function findNearest(cfg: ResolvedToilConfig, routeFile: string, base: string): string | undefined {
113
+ const root = path.resolve(cfg.routesAbsDir);
114
+ let dir = path.dirname(routeFile);
115
+ for (;;) {
116
+ const found = [`${base}.tsx`, `${base}.jsx`]
117
+ .map((name) => path.join(dir, name))
118
+ .find((p) => fs.existsSync(p));
119
+ if (found) return found;
120
+ if (path.resolve(dir) === root) return undefined;
121
+ const parent = path.dirname(dir);
122
+ if (parent === dir) return undefined;
123
+ dir = parent;
124
+ }
125
+ }
126
+
42
127
  /**
43
- * Generates the `.toil/` working dir (routes table, mount entry, HTML) and returns the scanned
128
+ * Generates the `.toil/` working dir (routes table, mount entry, the HTML entry built from the
129
+ * project's `public/index.html` template, and mirrored `public/` assets) and returns the scanned
44
130
  * routes. Called before every dev/build and on route add/remove during dev.
45
131
  */
46
132
  export function generate(cfg: ResolvedToilConfig): ScannedRoute[] {
@@ -50,40 +136,111 @@ export function generate(cfg: ResolvedToilConfig): ScannedRoute[] {
50
136
  const layoutFile = findLayout(cfg);
51
137
  const notFoundFile = findNotFound(cfg);
52
138
  const routesSrc =
139
+ `// @ts-nocheck\n` +
53
140
  `// AUTO-GENERATED by toil — do not edit.\n` +
54
141
  `import type { RouteDef, LayoutLoader, NotFoundLoader } from 'toiljs/client';\n\n` +
55
142
  `export const routes: RouteDef[] = [\n` +
56
143
  routes
57
- .map(
58
- (r) =>
59
- ` { pattern: ${JSON.stringify(r.pattern)}, load: () => import(${JSON.stringify(relFromToil(cfg, r.file))}) },`,
60
- )
144
+ .map((r) => {
145
+ const imp = (f: string): string => `() => import(${JSON.stringify(relFromToil(cfg, f))})`;
146
+ const layouts = findLayoutChain(cfg, r.file).map(imp).join(', ');
147
+ const parts = [
148
+ `pattern: ${JSON.stringify(r.pattern)}`,
149
+ `load: ${imp(r.file)}`,
150
+ `layouts: [${layouts}]`,
151
+ ];
152
+ const loadingFile = findNearest(cfg, r.file, 'loading');
153
+ if (loadingFile) parts.push(`loading: ${imp(loadingFile)}`);
154
+ const errorFile = findNearest(cfg, r.file, 'error');
155
+ if (errorFile) parts.push(`errorComponent: ${imp(errorFile)}`);
156
+ return ` { ${parts.join(', ')} },`;
157
+ })
61
158
  .join('\n') +
62
159
  `\n];\n\n` +
63
160
  `export const layout: LayoutLoader = ${layoutFile ? `() => import(${JSON.stringify(relFromToil(cfg, layoutFile))})` : 'null'};\n` +
64
161
  `export const notFound: NotFoundLoader = ${notFoundFile ? `() => import(${JSON.stringify(relFromToil(cfg, notFoundFile))})` : 'null'};\n`;
65
162
  fs.writeFileSync(path.join(cfg.toilDir, 'routes.ts'), routesSrc);
66
163
 
67
- const entrySrc =
164
+ const globalsSrc =
165
+ `// @ts-nocheck\n` +
68
166
  `// AUTO-GENERATED by toil — do not edit.\n` +
69
- `import { mount } from 'toiljs/client';\n` +
70
- `import { BinaryWriter, BinaryReader, FastMap, FastSet } from 'toiljs/io';\n` +
71
- `import { routes, layout, notFound } from './routes';\n\n` +
72
- `// Expose toiljs IO as native globals (typed via ./toil-env.d.ts).\n` +
73
- `Object.assign(globalThis, { BinaryWriter, BinaryReader, FastMap, FastSet });\n\n` +
74
- `mount(routes, layout, notFound);\n`;
167
+ `import * as Toil from 'toiljs/client';\n` +
168
+ `import { BinaryWriter, BinaryReader, FastMap, FastSet } from 'toiljs/io';\n\n` +
169
+ `Object.assign(globalThis, { Toil, BinaryWriter, BinaryReader, FastMap, FastSet });\n`;
170
+ fs.writeFileSync(path.join(cfg.toilDir, 'globals.ts'), globalsSrc);
171
+
172
+ const entryFile = findEntry(cfg);
173
+ const entrySrc = entryFile
174
+ ? `// @ts-nocheck\n` +
175
+ `// AUTO-GENERATED by toil — do not edit.\n` +
176
+ `import './globals';\n` +
177
+ `import ${JSON.stringify(relFromToil(cfg, entryFile))};\n`
178
+ : `// @ts-nocheck\n` +
179
+ `// AUTO-GENERATED by toil — do not edit.\n` +
180
+ `import './globals';\n` +
181
+ `import { mount } from 'toiljs/client';\n` +
182
+ `import { routes, layout, notFound } from './routes';\n\n` +
183
+ `mount(routes, layout, notFound);\n`;
75
184
  fs.writeFileSync(path.join(cfg.toilDir, 'entry.tsx'), entrySrc);
76
185
 
77
- // Ambient global types (see TOIL_ENV_DTS). Regenerated each dev/build; also written by
78
- // `toiljs create` so a fresh project type-checks before its first build.
79
186
  fs.writeFileSync(path.join(cfg.root, 'toil-env.d.ts'), TOIL_ENV_DTS);
80
187
 
81
- const htmlSrc =
82
- `<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n` +
83
- ` <meta name="viewport" content="width=device-width, initial-scale=1" />\n` +
84
- ` <title>Toil App</title>\n </head>\n <body>\n <div id="root"></div>\n` +
85
- ` <script type="module" src="./entry.tsx"></script>\n </body>\n</html>\n`;
86
- fs.writeFileSync(path.join(cfg.toilDir, 'index.html'), htmlSrc);
188
+ fs.writeFileSync(path.join(cfg.toilDir, 'index.html'), buildHtml(cfg));
189
+ syncPublicAssets(cfg);
190
+ writeDocs(cfg.toilDir);
87
191
 
88
192
  return routes;
89
193
  }
194
+
195
+ /** Fallback HTML when the project has no `public/index.html` template. The entry script is added
196
+ * by {@link buildHtml}. */
197
+ const DEFAULT_HTML =
198
+ `<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n` +
199
+ ` <meta name="viewport" content="width=device-width, initial-scale=1" />\n` +
200
+ ` <meta name="description" content="" />\n` +
201
+ ` <title>Toil App</title>\n </head>\n <body>\n <div id="root"></div>\n` +
202
+ ` </body>\n</html>\n`;
203
+
204
+ /** The module entry that boots the app, injected into the HTML (resolved relative to `.toil`). */
205
+ const ENTRY_SCRIPT = `<script type="module" src="./entry.tsx"></script>`;
206
+
207
+ /**
208
+ * Produces the `.toil/index.html` Vite entry from the project's `public/index.html` template (or
209
+ * the built-in default if absent), ensuring the generated module entry script is present. Users
210
+ * own the template — toil only guarantees the entry is wired, so it stays the SPA root.
211
+ */
212
+ function buildHtml(cfg: ResolvedToilConfig): string {
213
+ const templatePath = path.join(cfg.publicDir, 'index.html');
214
+ let html = fs.existsSync(templatePath)
215
+ ? fs.readFileSync(templatePath, 'utf8')
216
+ : DEFAULT_HTML;
217
+ // Inject the entry only if the template doesn't already reference it as a module script
218
+ // (matching the literal filename anywhere in the file would be too eager).
219
+ if (!/src=["']\.\/entry\.tsx["']/.test(html)) {
220
+ html = html.includes('</body>')
221
+ ? html.replace('</body>', ` ${ENTRY_SCRIPT}\n </body>`)
222
+ : `${html}\n${ENTRY_SCRIPT}\n`;
223
+ }
224
+ return html;
225
+ }
226
+
227
+ /**
228
+ * Mirrors the project's `public/` assets into `.toil/public/` (Vite's publicDir under the `.toil`
229
+ * root), excluding the `index.html` template — that is processed into the entry above, and copying
230
+ * it here would clobber the built, asset-hashed page. Cleared each run so deletions propagate.
231
+ */
232
+ function syncPublicAssets(cfg: ResolvedToilConfig): void {
233
+ const dest = path.join(cfg.toilDir, 'public');
234
+ fs.rmSync(dest, { recursive: true, force: true });
235
+ if (!fs.existsSync(cfg.publicDir)) return;
236
+
237
+ let copied = 0;
238
+ for (const entry of fs.readdirSync(cfg.publicDir, { withFileTypes: true })) {
239
+ if (entry.name === 'index.html') continue;
240
+ fs.cpSync(path.join(cfg.publicDir, entry.name), path.join(dest, entry.name), {
241
+ recursive: true,
242
+ });
243
+ copied++;
244
+ }
245
+ if (copied === 0) fs.rmSync(dest, { recursive: true, force: true });
246
+ }
@@ -17,7 +17,7 @@ export interface ToilCommandOptions {
17
17
  export async function dev(opts: ToilCommandOptions = {}): Promise<ViteDevServer> {
18
18
  const cfg = await loadConfig(opts);
19
19
  generate(cfg);
20
- const server = await createServer(createViteConfig(cfg));
20
+ const server = await createServer(await createViteConfig(cfg));
21
21
  await server.listen();
22
22
  server.printUrls();
23
23
  return server;
@@ -27,7 +27,7 @@ export async function dev(opts: ToilCommandOptions = {}): Promise<ViteDevServer>
27
27
  export async function build(opts: ToilCommandOptions = {}): Promise<void> {
28
28
  const cfg = await loadConfig(opts);
29
29
  generate(cfg);
30
- await viteBuild(createViteConfig(cfg));
30
+ await viteBuild(await createViteConfig(cfg));
31
31
  }
32
32
 
33
33
  /**
@@ -44,7 +44,9 @@ export async function start(opts: ToilCommandOptions = {}): Promise<RunningBacke
44
44
  return startBackend({ root: outDir, port: cfg.port });
45
45
  }
46
46
 
47
- export { defineConfig } from './config.js';
47
+ export { defineConfig, loadConfig } from './config.js';
48
48
  export { TOIL_ENV_DTS } from './generate.js';
49
- export type { ToilConfig } from './config.js';
49
+ export { AI_HELPERS, AI_HELPER_IDS, aiHelperFiles, TOIL_DOCS } from './docs.js';
50
+ export type { AiHelper } from './docs.js';
51
+ export type { ToilConfig, ResolvedToilConfig } from './config.js';
50
52
  export type { RunningBackend, BackendOptions } from 'toiljs/backend';
@@ -11,9 +11,30 @@ import { generate } from './generate.js';
11
11
  export function toilPlugin(cfg: ResolvedToilConfig): Plugin {
12
12
  return {
13
13
  name: 'toil',
14
+ // Catch empty import specifiers in source and report the file — rolldown otherwise fails
15
+ // resolution with a cryptic "The specifiers must be a non-empty string. Received ''".
16
+ transform(code, id) {
17
+ const file = id.split('?')[0];
18
+ if (id.includes('\0') || file.includes('/node_modules/') || !/\.[mc]?[jt]sx?$/.test(file)) {
19
+ return null;
20
+ }
21
+ const empty =
22
+ /^[ \t]*import\s+(['"])\1\s*;?[ \t]*$/m.test(code) ||
23
+ /^[ \t]*import\b[^'"\n]*\bfrom\s+(['"])\1/m.test(code) ||
24
+ /^[ \t]*export\b[^'"\n]*\bfrom\s+(['"])\1/m.test(code) ||
25
+ /\bimport\s*\(\s*(['"])\1\s*\)/.test(code);
26
+ if (empty) {
27
+ throw new Error(
28
+ `toil: empty import specifier (e.g. \`import '';\`) in ${file} — remove or complete the import.`,
29
+ );
30
+ }
31
+ return null;
32
+ },
14
33
  configureServer(server) {
34
+ // Trailing slash so a sibling like `routes-extra/` doesn't match the `routes/` prefix.
35
+ const routesPrefix = cfg.routesAbsDir.replace(/\\/g, '/').replace(/\/?$/, '/');
15
36
  const onChange = (file: string): void => {
16
- if (file.replace(/\\/g, '/').startsWith(cfg.routesAbsDir.replace(/\\/g, '/'))) {
37
+ if (file.replace(/\\/g, '/').startsWith(routesPrefix)) {
17
38
  generate(cfg);
18
39
  server.ws.send({ type: 'full-reload' });
19
40
  }
@@ -8,14 +8,18 @@ export interface ScannedRoute {
8
8
  }
9
9
 
10
10
  const ROUTE_EXT = /\.(tsx|jsx)$/;
11
+ /** Special files that live alongside routes but are not themselves pages. */
12
+ const SPECIAL_FILE = /^(layout|loading|error|404|not-found)\.(tsx|jsx)$/;
11
13
 
12
14
  /**
13
15
  * Derives a route pattern from a route file path (relative to the routes dir).
14
16
  * index.tsx -> /
15
17
  * about.tsx -> /about
16
18
  * blog/index.tsx -> /blog
17
- * blog/[id].tsx -> /blog/:id
18
- * docs/[...slug].tsx -> /docs/*slug (catch-all)
19
+ * blog/[id].tsx -> /blog/:id
20
+ * docs/[...slug].tsx -> /docs/*slug (catch-all)
21
+ * docs/[[...slug]].tsx -> /docs/**slug (optional catch-all)
22
+ * (marketing)/about.tsx -> /about (route group: parens add no URL segment)
19
23
  */
20
24
  export function filePathToRoute(relPath: string): string {
21
25
  const withoutExt = relPath.replace(/\\/g, '/').replace(ROUTE_EXT, '');
@@ -23,11 +27,13 @@ export function filePathToRoute(relPath: string): string {
23
27
  const out: string[] = [];
24
28
  for (let i = 0; i < segments.length; i++) {
25
29
  const segment = segments[i];
30
+ if (/^\(.+\)$/.test(segment)) continue;
26
31
  if (segment === 'index' && i === segments.length - 1) continue;
27
32
  out.push(
28
33
  segment
29
- .replace(/^\[\.\.\.(.+)\]$/, '*$1') // catch-all [...slug] -> *slug
30
- .replace(/^\[(.+)\]$/, ':$1'), // dynamic [id] -> :id
34
+ .replace(/^\[\[\.\.\.(.+)\]\]$/, '**$1')
35
+ .replace(/^\[\.\.\.(.+)\]$/, '*$1')
36
+ .replace(/^\[(.+)\]$/, ':$1'),
31
37
  );
32
38
  }
33
39
  return '/' + out.join('/');
@@ -41,8 +47,8 @@ function specificity(pattern: string): number {
41
47
  const segments = pattern.split('/').filter(Boolean);
42
48
  let score = segments.length * 10;
43
49
  for (const segment of segments) {
44
- if (segment.startsWith('*')) score -= 5; // catch-all: lowest
45
- else if (!segment.startsWith(':')) score += 5; // static: highest
50
+ if (segment.startsWith('*')) score -= 5;
51
+ else if (!segment.startsWith(':')) score += 5;
46
52
  }
47
53
  return score;
48
54
  }
@@ -56,7 +62,7 @@ export function scanRoutes(routesDir: string): ScannedRoute[] {
56
62
  const full = path.join(dir, entry.name);
57
63
  if (entry.isDirectory()) {
58
64
  walk(full);
59
- } else if (ROUTE_EXT.test(entry.name)) {
65
+ } else if (ROUTE_EXT.test(entry.name) && !SPECIAL_FILE.test(entry.name)) {
60
66
  found.push({
61
67
  file: full,
62
68
  pattern: filePathToRoute(path.relative(routesDir, full)),