toiljs 0.0.3 → 0.0.5

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 (360) 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 +169 -56
  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/error-boundary.d.ts +16 -0
  26. package/build/client/error-boundary.js +19 -0
  27. package/build/client/head.d.ts +26 -0
  28. package/build/client/head.js +87 -0
  29. package/build/client/hooks.d.ts +17 -0
  30. package/build/client/hooks.js +48 -0
  31. package/build/client/index.d.ts +14 -2
  32. package/build/client/index.js +8 -1
  33. package/build/client/lazy.d.ts +16 -0
  34. package/build/client/lazy.js +53 -0
  35. package/build/client/match.js +7 -0
  36. package/build/client/mount.d.ts +2 -0
  37. package/build/client/mount.js +13 -0
  38. package/build/client/navigation.d.ts +13 -0
  39. package/build/client/navigation.js +97 -0
  40. package/build/client/params-context.d.ts +2 -0
  41. package/build/client/params-context.js +2 -0
  42. package/build/client/prefetch.d.ts +11 -0
  43. package/build/client/prefetch.js +100 -0
  44. package/build/client/scroll.d.ts +8 -0
  45. package/build/client/scroll.js +36 -0
  46. package/build/client/types.d.ts +27 -0
  47. package/build/client/types.js +1 -0
  48. package/build/compiler/.tsbuildinfo +1 -1
  49. package/build/compiler/config.d.ts +2 -0
  50. package/build/compiler/config.js +4 -1
  51. package/build/compiler/docs.d.ts +10 -0
  52. package/build/compiler/docs.js +59 -0
  53. package/build/compiler/generate.d.ts +1 -0
  54. package/build/compiler/generate.js +139 -21
  55. package/build/compiler/index.d.ts +5 -2
  56. package/build/compiler/index.js +5 -3
  57. package/build/compiler/plugin.js +2 -1
  58. package/build/compiler/routes.js +5 -1
  59. package/build/compiler/vite.d.ts +1 -1
  60. package/build/compiler/vite.js +17 -1
  61. package/build/io/.tsbuildinfo +1 -1
  62. package/build/io/BinaryWriter.js +2 -2
  63. package/eslint.config.js +1 -1
  64. package/examples/basic/.toil/docs/cli.md +3 -0
  65. package/examples/basic/.toil/docs/client.md +3 -0
  66. package/examples/basic/.toil/docs/index.md +3 -0
  67. package/examples/basic/.toil/docs/routing.md +3 -0
  68. package/examples/basic/.toil/docs/server.md +3 -0
  69. package/examples/basic/.toil/docs/styling.md +3 -0
  70. package/examples/basic/.toil/entry.tsx +3 -8
  71. package/examples/basic/.toil/globals.ts +6 -0
  72. package/examples/basic/.toil/index.html +16 -12
  73. package/examples/basic/.toil/public/images/.gitkeep +1 -0
  74. package/examples/basic/.toil/public/images/logo.svg +37 -0
  75. package/examples/basic/.toil/public/robots.txt +2 -0
  76. package/examples/basic/.toil/routes.ts +9 -7
  77. package/examples/basic/build/client/assets/404-Bq0jNTUo.js +1 -0
  78. package/examples/basic/build/client/assets/_...slug_-CXKf6qnB.js +1 -0
  79. package/examples/basic/build/client/assets/_id_-BadAyQnb.js +1 -0
  80. package/examples/basic/build/client/assets/about-BOhoEcEO.js +1 -0
  81. package/examples/basic/build/client/assets/get-started-BIXpcjkT.js +9 -0
  82. package/examples/basic/build/client/assets/index-BmqcTaBB.js +1 -0
  83. package/examples/basic/build/client/assets/io-DEVjjaJj.js +1 -0
  84. package/examples/basic/build/client/assets/layout-DJegirdz.js +1 -0
  85. package/examples/basic/build/client/assets/react-DEQrz1q7.js +9 -0
  86. package/examples/basic/build/client/assets/rolldown-runtime-KL5VtC6j.js +1 -0
  87. package/examples/basic/build/client/assets/routes-BYWn6TxK.js +1 -0
  88. package/examples/basic/build/client/css/style.css +2 -0
  89. package/examples/basic/build/client/images/.gitkeep +1 -0
  90. package/examples/basic/build/client/images/logo.svg +37 -0
  91. package/examples/basic/build/client/index.html +17 -0
  92. package/examples/basic/build/client/robots.txt +2 -0
  93. package/examples/basic/client/404.tsx +2 -5
  94. package/examples/basic/client/components/.gitkeep +1 -0
  95. package/examples/basic/client/components/Footer.tsx +8 -0
  96. package/examples/basic/client/layout.tsx +39 -26
  97. package/examples/basic/client/public/images/.gitkeep +1 -0
  98. package/examples/basic/client/public/images/logo.svg +37 -0
  99. package/examples/basic/client/public/index.html +15 -0
  100. package/examples/basic/client/public/robots.txt +2 -0
  101. package/examples/basic/client/routes/about.tsx +1 -3
  102. package/examples/basic/client/routes/blog/[id].tsx +2 -4
  103. package/examples/basic/client/routes/docs/[...slug].tsx +3 -6
  104. package/examples/basic/client/routes/get-started.tsx +84 -0
  105. package/examples/basic/client/routes/index.tsx +74 -7
  106. package/examples/basic/client/routes/io.tsx +3 -7
  107. package/examples/basic/client/styles/main.css +461 -0
  108. package/examples/basic/client/toil.tsx +7 -0
  109. package/examples/basic/node_modules/.bin/toilinit +16 -0
  110. package/examples/basic/node_modules/.bin/toilinit.cmd +17 -0
  111. package/examples/basic/node_modules/.bin/toilinit.ps1 +28 -0
  112. package/examples/basic/node_modules/.bin/toilscript +16 -0
  113. package/examples/basic/node_modules/.bin/toilscript.cmd +17 -0
  114. package/examples/basic/node_modules/.bin/toilscript.ps1 +28 -0
  115. package/examples/basic/node_modules/.bin/wasm-as +16 -0
  116. package/examples/basic/node_modules/.bin/wasm-as.cmd +17 -0
  117. package/examples/basic/node_modules/.bin/wasm-as.ps1 +28 -0
  118. package/examples/basic/node_modules/.bin/wasm-ctor-eval +16 -0
  119. package/examples/basic/node_modules/.bin/wasm-ctor-eval.cmd +17 -0
  120. package/examples/basic/node_modules/.bin/wasm-ctor-eval.ps1 +28 -0
  121. package/examples/basic/node_modules/.bin/wasm-dis +16 -0
  122. package/examples/basic/node_modules/.bin/wasm-dis.cmd +17 -0
  123. package/examples/basic/node_modules/.bin/wasm-dis.ps1 +28 -0
  124. package/examples/basic/node_modules/.bin/wasm-merge +16 -0
  125. package/examples/basic/node_modules/.bin/wasm-merge.cmd +17 -0
  126. package/examples/basic/node_modules/.bin/wasm-merge.ps1 +28 -0
  127. package/examples/basic/node_modules/.bin/wasm-metadce +16 -0
  128. package/examples/basic/node_modules/.bin/wasm-metadce.cmd +17 -0
  129. package/examples/basic/node_modules/.bin/wasm-metadce.ps1 +28 -0
  130. package/examples/basic/node_modules/.bin/wasm-opt +16 -0
  131. package/examples/basic/node_modules/.bin/wasm-opt.cmd +17 -0
  132. package/examples/basic/node_modules/.bin/wasm-opt.ps1 +28 -0
  133. package/examples/basic/node_modules/.bin/wasm-reduce +16 -0
  134. package/examples/basic/node_modules/.bin/wasm-reduce.cmd +17 -0
  135. package/examples/basic/node_modules/.bin/wasm-reduce.ps1 +28 -0
  136. package/examples/basic/node_modules/.bin/wasm-shell +16 -0
  137. package/examples/basic/node_modules/.bin/wasm-shell.cmd +17 -0
  138. package/examples/basic/node_modules/.bin/wasm-shell.ps1 +28 -0
  139. package/examples/basic/node_modules/.bin/wasm2js +16 -0
  140. package/examples/basic/node_modules/.bin/wasm2js.cmd +17 -0
  141. package/examples/basic/node_modules/.bin/wasm2js.ps1 +28 -0
  142. package/examples/basic/node_modules/.package-lock.json +49 -1
  143. package/examples/basic/node_modules/.vite/deps/_metadata.json +9 -9
  144. package/examples/basic/node_modules/binaryen/LICENSE +201 -0
  145. package/examples/basic/node_modules/binaryen/README.md +1362 -0
  146. package/examples/basic/node_modules/binaryen/bin/package.json +3 -0
  147. package/examples/basic/node_modules/binaryen/bin/wasm-as +0 -0
  148. package/examples/basic/node_modules/binaryen/bin/wasm-ctor-eval +0 -0
  149. package/examples/basic/node_modules/binaryen/bin/wasm-dis +0 -0
  150. package/examples/basic/node_modules/binaryen/bin/wasm-merge +0 -0
  151. package/examples/basic/node_modules/binaryen/bin/wasm-metadce +0 -0
  152. package/examples/basic/node_modules/binaryen/bin/wasm-opt +0 -0
  153. package/examples/basic/node_modules/binaryen/bin/wasm-reduce +0 -0
  154. package/examples/basic/node_modules/binaryen/bin/wasm-shell +0 -0
  155. package/examples/basic/node_modules/binaryen/bin/wasm2js +0 -0
  156. package/examples/basic/node_modules/binaryen/index.d.ts +2371 -0
  157. package/examples/basic/node_modules/binaryen/index.js +30552 -0
  158. package/examples/basic/node_modules/binaryen/package.json +50 -0
  159. package/examples/basic/node_modules/long/LICENSE +202 -0
  160. package/examples/basic/node_modules/long/README.md +286 -0
  161. package/examples/basic/node_modules/long/index.d.ts +2 -0
  162. package/examples/basic/node_modules/long/index.js +1581 -0
  163. package/examples/basic/node_modules/long/package.json +58 -0
  164. package/examples/basic/node_modules/long/types.d.ts +474 -0
  165. package/examples/basic/node_modules/long/umd/index.d.ts +3 -0
  166. package/examples/basic/node_modules/long/umd/index.js +1622 -0
  167. package/examples/basic/node_modules/long/umd/package.json +3 -0
  168. package/examples/basic/node_modules/long/umd/types.d.ts +474 -0
  169. package/examples/basic/node_modules/toilscript/LICENSE +201 -0
  170. package/examples/basic/node_modules/toilscript/NOTICE +94 -0
  171. package/examples/basic/node_modules/toilscript/README.md +66 -0
  172. package/examples/basic/node_modules/toilscript/bin/toilinit.js +468 -0
  173. package/examples/basic/node_modules/toilscript/bin/toilscript.js +35 -0
  174. package/examples/basic/node_modules/toilscript/dist/cli.d.ts +4 -0
  175. package/examples/basic/node_modules/toilscript/dist/cli.generated.d.ts +10027 -0
  176. package/examples/basic/node_modules/toilscript/dist/cli.js +24474 -0
  177. package/examples/basic/node_modules/toilscript/dist/cli.js.map +7 -0
  178. package/examples/basic/node_modules/toilscript/dist/importmap.json +9 -0
  179. package/examples/basic/node_modules/toilscript/dist/toilscript.d.ts +4 -0
  180. package/examples/basic/node_modules/toilscript/dist/toilscript.generated.d.ts +11242 -0
  181. package/examples/basic/node_modules/toilscript/dist/toilscript.js +337 -0
  182. package/examples/basic/node_modules/toilscript/dist/toilscript.js.map +7 -0
  183. package/examples/basic/node_modules/toilscript/dist/transform.cjs +1 -0
  184. package/examples/basic/node_modules/toilscript/dist/transform.d.ts +1 -0
  185. package/examples/basic/node_modules/toilscript/dist/transform.js +1 -0
  186. package/examples/basic/node_modules/toilscript/dist/web.js +22 -0
  187. package/examples/basic/node_modules/toilscript/lib/binaryen.d.ts +2 -0
  188. package/examples/basic/node_modules/toilscript/lib/binaryen.js +2 -0
  189. package/examples/basic/node_modules/toilscript/package.json +115 -0
  190. package/examples/basic/node_modules/toilscript/std/README.md +6 -0
  191. package/examples/basic/node_modules/toilscript/std/assembly/array.ts +550 -0
  192. package/examples/basic/node_modules/toilscript/std/assembly/arraybuffer.ts +77 -0
  193. package/examples/basic/node_modules/toilscript/std/assembly/atomics.ts +127 -0
  194. package/examples/basic/node_modules/toilscript/std/assembly/bindings/asyncify.ts +16 -0
  195. package/examples/basic/node_modules/toilscript/std/assembly/bindings/dom.ts +291 -0
  196. package/examples/basic/node_modules/toilscript/std/assembly/bindings/node.ts +6 -0
  197. package/examples/basic/node_modules/toilscript/std/assembly/bitflags.ts +53 -0
  198. package/examples/basic/node_modules/toilscript/std/assembly/builtins.ts +2650 -0
  199. package/examples/basic/node_modules/toilscript/std/assembly/byteslice.ts +177 -0
  200. package/examples/basic/node_modules/toilscript/std/assembly/compat.ts +2 -0
  201. package/examples/basic/node_modules/toilscript/std/assembly/console.ts +42 -0
  202. package/examples/basic/node_modules/toilscript/std/assembly/crypto.ts +9 -0
  203. package/examples/basic/node_modules/toilscript/std/assembly/dataview.ts +181 -0
  204. package/examples/basic/node_modules/toilscript/std/assembly/date.ts +375 -0
  205. package/examples/basic/node_modules/toilscript/std/assembly/diagnostics.ts +11 -0
  206. package/examples/basic/node_modules/toilscript/std/assembly/encoding.ts +151 -0
  207. package/examples/basic/node_modules/toilscript/std/assembly/endian.ts +45 -0
  208. package/examples/basic/node_modules/toilscript/std/assembly/error.ts +44 -0
  209. package/examples/basic/node_modules/toilscript/std/assembly/fixedarray.ts +173 -0
  210. package/examples/basic/node_modules/toilscript/std/assembly/fixedmap.ts +326 -0
  211. package/examples/basic/node_modules/toilscript/std/assembly/fixedset.ts +275 -0
  212. package/examples/basic/node_modules/toilscript/std/assembly/function.ts +42 -0
  213. package/examples/basic/node_modules/toilscript/std/assembly/index.d.ts +2892 -0
  214. package/examples/basic/node_modules/toilscript/std/assembly/iterator.ts +35 -0
  215. package/examples/basic/node_modules/toilscript/std/assembly/map.ts +269 -0
  216. package/examples/basic/node_modules/toilscript/std/assembly/math.ts +3289 -0
  217. package/examples/basic/node_modules/toilscript/std/assembly/memory.ts +123 -0
  218. package/examples/basic/node_modules/toilscript/std/assembly/number.ts +388 -0
  219. package/examples/basic/node_modules/toilscript/std/assembly/object.ts +36 -0
  220. package/examples/basic/node_modules/toilscript/std/assembly/performance.ts +9 -0
  221. package/examples/basic/node_modules/toilscript/std/assembly/pointer.ts +80 -0
  222. package/examples/basic/node_modules/toilscript/std/assembly/polyfills.ts +27 -0
  223. package/examples/basic/node_modules/toilscript/std/assembly/process.ts +50 -0
  224. package/examples/basic/node_modules/toilscript/std/assembly/reference.ts +48 -0
  225. package/examples/basic/node_modules/toilscript/std/assembly/regexp.ts +12 -0
  226. package/examples/basic/node_modules/toilscript/std/assembly/rt/README.md +83 -0
  227. package/examples/basic/node_modules/toilscript/std/assembly/rt/common.ts +81 -0
  228. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-incremental.ts +2 -0
  229. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-memory.ts +1 -0
  230. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-minimal.ts +2 -0
  231. package/examples/basic/node_modules/toilscript/std/assembly/rt/index-stub.ts +1 -0
  232. package/examples/basic/node_modules/toilscript/std/assembly/rt/index.d.ts +37 -0
  233. package/examples/basic/node_modules/toilscript/std/assembly/rt/itcms.ts +419 -0
  234. package/examples/basic/node_modules/toilscript/std/assembly/rt/memory-runtime.ts +94 -0
  235. package/examples/basic/node_modules/toilscript/std/assembly/rt/rtrace.ts +15 -0
  236. package/examples/basic/node_modules/toilscript/std/assembly/rt/stub.ts +133 -0
  237. package/examples/basic/node_modules/toilscript/std/assembly/rt/tcms.ts +254 -0
  238. package/examples/basic/node_modules/toilscript/std/assembly/rt/tlsf.ts +592 -0
  239. package/examples/basic/node_modules/toilscript/std/assembly/rt.ts +90 -0
  240. package/examples/basic/node_modules/toilscript/std/assembly/set.ts +225 -0
  241. package/examples/basic/node_modules/toilscript/std/assembly/shared/feature.ts +68 -0
  242. package/examples/basic/node_modules/toilscript/std/assembly/shared/runtime.ts +13 -0
  243. package/examples/basic/node_modules/toilscript/std/assembly/shared/target.ts +11 -0
  244. package/examples/basic/node_modules/toilscript/std/assembly/shared/tsconfig.json +11 -0
  245. package/examples/basic/node_modules/toilscript/std/assembly/shared/typeinfo.ts +72 -0
  246. package/examples/basic/node_modules/toilscript/std/assembly/staticarray.ts +423 -0
  247. package/examples/basic/node_modules/toilscript/std/assembly/string.ts +850 -0
  248. package/examples/basic/node_modules/toilscript/std/assembly/symbol.ts +114 -0
  249. package/examples/basic/node_modules/toilscript/std/assembly/table.ts +16 -0
  250. package/examples/basic/node_modules/toilscript/std/assembly/toilscript.ts +16 -0
  251. package/examples/basic/node_modules/toilscript/std/assembly/tsconfig.json +6 -0
  252. package/examples/basic/node_modules/toilscript/std/assembly/typedarray.ts +1954 -0
  253. package/examples/basic/node_modules/toilscript/std/assembly/uri.ts +17 -0
  254. package/examples/basic/node_modules/toilscript/std/assembly/util/bytes.ts +107 -0
  255. package/examples/basic/node_modules/toilscript/std/assembly/util/casemap.ts +497 -0
  256. package/examples/basic/node_modules/toilscript/std/assembly/util/error.ts +58 -0
  257. package/examples/basic/node_modules/toilscript/std/assembly/util/hash.ts +117 -0
  258. package/examples/basic/node_modules/toilscript/std/assembly/util/math.ts +1922 -0
  259. package/examples/basic/node_modules/toilscript/std/assembly/util/memory.ts +290 -0
  260. package/examples/basic/node_modules/toilscript/std/assembly/util/number.ts +873 -0
  261. package/examples/basic/node_modules/toilscript/std/assembly/util/sort.ts +313 -0
  262. package/examples/basic/node_modules/toilscript/std/assembly/util/string.ts +1202 -0
  263. package/examples/basic/node_modules/toilscript/std/assembly/util/uri.ts +275 -0
  264. package/examples/basic/node_modules/toilscript/std/assembly/vector.ts +4 -0
  265. package/examples/basic/node_modules/toilscript/std/assembly.json +16 -0
  266. package/examples/basic/node_modules/toilscript/std/portable/index.d.ts +461 -0
  267. package/examples/basic/node_modules/toilscript/std/portable/index.js +416 -0
  268. package/examples/basic/node_modules/toilscript/std/portable.json +11 -0
  269. package/examples/basic/node_modules/toilscript/std/types/assembly/index.d.ts +1 -0
  270. package/examples/basic/node_modules/toilscript/std/types/assembly/package.json +3 -0
  271. package/examples/basic/node_modules/toilscript/std/types/portable/index.d.ts +1 -0
  272. package/examples/basic/node_modules/toilscript/std/types/portable/package.json +3 -0
  273. package/examples/basic/node_modules/toilscript/tsconfig-base.json +13 -0
  274. package/examples/basic/node_modules/toilscript/util/README.md +23 -0
  275. package/examples/basic/node_modules/toilscript/util/browser/fs.js +1 -0
  276. package/examples/basic/node_modules/toilscript/util/browser/module.js +5 -0
  277. package/examples/basic/node_modules/toilscript/util/browser/path.js +520 -0
  278. package/examples/basic/node_modules/toilscript/util/browser/process.js +59 -0
  279. package/examples/basic/node_modules/toilscript/util/browser/url.js +23 -0
  280. package/examples/basic/node_modules/toilscript/util/cpu.d.ts +9 -0
  281. package/examples/basic/node_modules/toilscript/util/cpu.js +42 -0
  282. package/examples/basic/node_modules/toilscript/util/find.d.ts +6 -0
  283. package/examples/basic/node_modules/toilscript/util/find.js +20 -0
  284. package/examples/basic/node_modules/toilscript/util/node.d.ts +21 -0
  285. package/examples/basic/node_modules/toilscript/util/node.js +34 -0
  286. package/examples/basic/node_modules/toilscript/util/options.d.ts +70 -0
  287. package/examples/basic/node_modules/toilscript/util/options.js +262 -0
  288. package/examples/basic/node_modules/toilscript/util/terminal.d.ts +52 -0
  289. package/examples/basic/node_modules/toilscript/util/terminal.js +35 -0
  290. package/examples/basic/node_modules/toilscript/util/text.d.ts +26 -0
  291. package/examples/basic/node_modules/toilscript/util/text.js +114 -0
  292. package/examples/basic/node_modules/toilscript/util/tsconfig.json +9 -0
  293. package/examples/basic/node_modules/toilscript/util/web.d.ts +11 -0
  294. package/examples/basic/node_modules/toilscript/util/web.js +33 -0
  295. package/examples/basic/package-lock.json +50 -1
  296. package/examples/basic/package.json +5 -2
  297. package/examples/basic/server/index.ts +3 -0
  298. package/examples/basic/server/main.ts +6 -0
  299. package/examples/basic/server/tsconfig.json +7 -0
  300. package/examples/basic/toil-env.d.ts +20 -1
  301. package/examples/basic/toil.config.ts +2 -5
  302. package/examples/basic/toilconfig.json +30 -0
  303. package/package.json +1 -1
  304. package/presets/eslint.js +2 -7
  305. package/presets/no-uint8array-tostring.js +4 -5
  306. package/presets/prettier.json +8 -1
  307. package/src/backend/index.ts +11 -18
  308. package/src/cli/configure.ts +272 -0
  309. package/src/cli/create.ts +228 -67
  310. package/src/cli/features.ts +128 -0
  311. package/src/cli/index.ts +44 -3
  312. package/src/cli/proc.ts +20 -0
  313. package/src/cli/ui.ts +4 -6
  314. package/src/cli/validate.ts +31 -0
  315. package/src/client/Link.tsx +99 -0
  316. package/src/client/NavLink.tsx +86 -0
  317. package/src/client/Router.tsx +95 -0
  318. package/src/client/error-boundary.tsx +43 -0
  319. package/src/client/head.ts +140 -0
  320. package/src/client/hooks.ts +115 -0
  321. package/src/client/index.ts +35 -5
  322. package/src/client/lazy.ts +93 -0
  323. package/src/client/match.ts +11 -3
  324. package/src/client/mount.tsx +28 -0
  325. package/src/client/navigation.ts +142 -0
  326. package/src/client/params-context.ts +10 -0
  327. package/src/client/prefetch.ts +130 -0
  328. package/src/client/scroll.ts +53 -0
  329. package/src/client/types.ts +36 -0
  330. package/src/compiler/config.ts +15 -8
  331. package/src/compiler/docs.ts +87 -0
  332. package/src/compiler/generate.ts +180 -33
  333. package/src/compiler/index.ts +7 -4
  334. package/src/compiler/plugin.ts +3 -1
  335. package/src/compiler/routes.ts +13 -7
  336. package/src/compiler/vite.ts +28 -5
  337. package/src/io/BinaryReader.ts +1 -5
  338. package/src/io/BinaryWriter.ts +3 -3
  339. package/src/server/index.ts +3 -4
  340. package/src/server/tsconfig.json +4 -0
  341. package/test/configure.test.ts +90 -0
  342. package/test/features.test.ts +111 -0
  343. package/test/head.test.ts +35 -0
  344. package/test/io.test.ts +8 -0
  345. package/test/navlink.test.ts +28 -0
  346. package/test/routes.test.ts +15 -0
  347. package/test/validate.test.ts +42 -0
  348. package/vitest.config.ts +1 -1
  349. package/examples/basic/dist/assets/404-D1bS2aH_.js +0 -1
  350. package/examples/basic/dist/assets/_...slug_-wR3shlWn.js +0 -1
  351. package/examples/basic/dist/assets/_id_-EWYvHfi2.js +0 -1
  352. package/examples/basic/dist/assets/about-Ddvj1tjF.js +0 -1
  353. package/examples/basic/dist/assets/index-CdG0me90.js +0 -1
  354. package/examples/basic/dist/assets/io-CODNJU57.js +0 -1
  355. package/examples/basic/dist/assets/layout-C15ZTPYI.js +0 -1
  356. package/examples/basic/dist/assets/react-JbAfoxYe.js +0 -9
  357. package/examples/basic/dist/assets/rolldown-runtime-1VNLd2iN.js +0 -1
  358. package/examples/basic/dist/assets/routes-GoydenoY.js +0 -1
  359. package/examples/basic/dist/index.html +0 -12
  360. package/src/client/runtime.tsx +0 -190
@@ -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;
@@ -87,10 +95,6 @@ export async function loadConfig(
87
95
  for (const name of CONFIG_NAMES) {
88
96
  const candidate = path.join(root, name);
89
97
  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
98
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- dynamic import() is typed `any`
95
99
  const loaded: { default?: ToilConfig } = await import(pathToFileURL(candidate).href);
96
100
  if (loaded.default) user = loaded.default;
@@ -108,8 +112,11 @@ export async function loadConfig(
108
112
  srcDir,
109
113
  clientAbsDir,
110
114
  routesAbsDir: path.join(clientAbsDir, routesDir),
115
+ publicDir: client.publicDir
116
+ ? path.resolve(root, client.publicDir)
117
+ : path.join(clientAbsDir, 'public'),
111
118
  toilDir: path.join(root, '.toil'),
112
- outDir: client.outDir ?? 'dist',
119
+ outDir: client.outDir ?? 'build/client',
113
120
  base: client.base ?? '/',
114
121
  port: opts.port ?? client.port ?? 3000,
115
122
  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,11 +2,47 @@ 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
- /** Returns a `./`-prefixed POSIX path from the `.toil` dir to `abs`, for use in generated imports. */
8
+ /**
9
+ * Contents of the root `toil-env.d.ts`: ambient global types so `new BinaryWriter()` etc. resolve
10
+ * in the IDE without an import. Script-mode declaration (no top-level import/export → the
11
+ * `declare const`s are truly global, and it's not a module that could confuse ESLint's project
12
+ * service); the inline `import('toiljs/io')` type only needs the normal `toiljs/io` export.
13
+ * Lives at the project root because TypeScript's `include` globs skip dot-directories.
14
+ * Exported so `toiljs create` can write it during scaffolding, before the first dev/build.
15
+ */
16
+ export const TOIL_ENV_DTS =
17
+ `// AUTO-GENERATED by toil — do not edit.\n` +
18
+ `declare const Toil: typeof import('toiljs/client');\n` +
19
+ `declare const BinaryWriter: typeof import('toiljs/io').BinaryWriter;\n` +
20
+ `declare const BinaryReader: typeof import('toiljs/io').BinaryReader;\n` +
21
+ `declare const FastMap: typeof import('toiljs/io').FastMap;\n` +
22
+ `declare const FastSet: typeof import('toiljs/io').FastSet;\n` +
23
+ `\n` +
24
+ `declare module '*.css' {}\n` +
25
+ `declare module '*.scss' {}\n` +
26
+ `declare module '*.sass' {}\n` +
27
+ `declare module '*.less' {}\n` +
28
+ `declare module '*.styl' {}\n` +
29
+ `declare module '*.stylus' {}\n` +
30
+ `declare module '*.pcss' {}\n` +
31
+ `declare module '*.sss' {}\n` +
32
+ `\n` +
33
+ `declare module 'toiljs/routes' {\n` +
34
+ ` export const routes: import('toiljs/client').RouteDef[];\n` +
35
+ ` export const layout: import('toiljs/client').LayoutLoader;\n` +
36
+ ` export const notFound: import('toiljs/client').NotFoundLoader;\n` +
37
+ `}\n`;
38
+
39
+ /**
40
+ * Returns a `./`-prefixed, **extensionless** POSIX module specifier from `.toil` to `abs`, for use
41
+ * in generated `import(...)` calls. Extensionless so TypeScript doesn't demand
42
+ * `allowImportingTsExtensions` (TS5097) when the generated files are checked; Vite still resolves it.
43
+ */
8
44
  function relFromToil(cfg: ResolvedToilConfig, abs: string): string {
9
- let rel = path.relative(cfg.toilDir, abs).replace(/\\/g, '/');
45
+ let rel = path.relative(cfg.toilDir, abs).replace(/\\/g, '/').replace(/\.(tsx|jsx)$/, '');
10
46
  if (!rel.startsWith('.')) rel = './' + rel;
11
47
  return rel;
12
48
  }
@@ -24,8 +60,57 @@ function findNotFound(cfg: ResolvedToilConfig): string | undefined {
24
60
  .find((p) => fs.existsSync(p));
25
61
  }
26
62
 
63
+ /** Finds the user-owned app entry at `client/toil.{tsx,jsx}` (where `mount` is called). */
64
+ function findEntry(cfg: ResolvedToilConfig): string | undefined {
65
+ return ['toil.tsx', 'toil.jsx']
66
+ .map((name) => path.join(cfg.clientAbsDir, name))
67
+ .find((p) => fs.existsSync(p));
68
+ }
69
+
70
+ /** A `layout.{tsx,jsx}` in `dir`, or undefined. */
71
+ function layoutIn(dir: string): string | undefined {
72
+ return ['layout.tsx', 'layout.jsx']
73
+ .map((name) => path.join(dir, name))
74
+ .find((p) => fs.existsSync(p));
75
+ }
76
+
27
77
  /**
28
- * Generates the `.toil/` working dir (routes table, mount entry, HTML) and returns the scanned
78
+ * Nested layout chain for a route file: `layout.{tsx,jsx}` at the routes root and each ancestor
79
+ * directory down to the file's own, shallowest → deepest. (The project root `client/layout.tsx`
80
+ * is handled separately as the top-level layout.)
81
+ */
82
+ function findLayoutChain(cfg: ResolvedToilConfig, routeFile: string): string[] {
83
+ const relDir = path.dirname(path.relative(cfg.routesAbsDir, routeFile));
84
+ const segments = relDir === '.' ? [] : relDir.split(path.sep);
85
+ const chain: string[] = [];
86
+ let dir = cfg.routesAbsDir;
87
+ for (let i = 0; i <= segments.length; i++) {
88
+ if (i > 0) dir = path.join(dir, segments[i - 1]);
89
+ const layout = layoutIn(dir);
90
+ if (layout) chain.push(layout);
91
+ }
92
+ return chain;
93
+ }
94
+
95
+ /** Nearest special file named `base` (e.g. `loading`/`error`) from the route's dir up to the routes root. */
96
+ function findNearest(cfg: ResolvedToilConfig, routeFile: string, base: string): string | undefined {
97
+ const root = path.resolve(cfg.routesAbsDir);
98
+ let dir = path.dirname(routeFile);
99
+ for (;;) {
100
+ const found = [`${base}.tsx`, `${base}.jsx`]
101
+ .map((name) => path.join(dir, name))
102
+ .find((p) => fs.existsSync(p));
103
+ if (found) return found;
104
+ if (path.resolve(dir) === root) return undefined;
105
+ const parent = path.dirname(dir);
106
+ if (parent === dir) return undefined;
107
+ dir = parent;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Generates the `.toil/` working dir (routes table, mount entry, the HTML entry built from the
113
+ * project's `public/index.html` template, and mirrored `public/` assets) and returns the scanned
29
114
  * routes. Called before every dev/build and on route add/remove during dev.
30
115
  */
31
116
  export function generate(cfg: ResolvedToilConfig): ScannedRoute[] {
@@ -35,49 +120,111 @@ export function generate(cfg: ResolvedToilConfig): ScannedRoute[] {
35
120
  const layoutFile = findLayout(cfg);
36
121
  const notFoundFile = findNotFound(cfg);
37
122
  const routesSrc =
123
+ `// @ts-nocheck\n` +
38
124
  `// AUTO-GENERATED by toil — do not edit.\n` +
39
125
  `import type { RouteDef, LayoutLoader, NotFoundLoader } from 'toiljs/client';\n\n` +
40
126
  `export const routes: RouteDef[] = [\n` +
41
127
  routes
42
- .map(
43
- (r) =>
44
- ` { pattern: ${JSON.stringify(r.pattern)}, load: () => import(${JSON.stringify(relFromToil(cfg, r.file))}) },`,
45
- )
128
+ .map((r) => {
129
+ const imp = (f: string): string => `() => import(${JSON.stringify(relFromToil(cfg, f))})`;
130
+ const layouts = findLayoutChain(cfg, r.file).map(imp).join(', ');
131
+ const parts = [
132
+ `pattern: ${JSON.stringify(r.pattern)}`,
133
+ `load: ${imp(r.file)}`,
134
+ `layouts: [${layouts}]`,
135
+ ];
136
+ const loadingFile = findNearest(cfg, r.file, 'loading');
137
+ if (loadingFile) parts.push(`loading: ${imp(loadingFile)}`);
138
+ const errorFile = findNearest(cfg, r.file, 'error');
139
+ if (errorFile) parts.push(`errorComponent: ${imp(errorFile)}`);
140
+ return ` { ${parts.join(', ')} },`;
141
+ })
46
142
  .join('\n') +
47
143
  `\n];\n\n` +
48
144
  `export const layout: LayoutLoader = ${layoutFile ? `() => import(${JSON.stringify(relFromToil(cfg, layoutFile))})` : 'null'};\n` +
49
145
  `export const notFound: NotFoundLoader = ${notFoundFile ? `() => import(${JSON.stringify(relFromToil(cfg, notFoundFile))})` : 'null'};\n`;
50
146
  fs.writeFileSync(path.join(cfg.toilDir, 'routes.ts'), routesSrc);
51
147
 
52
- const entrySrc =
148
+ const globalsSrc =
149
+ `// @ts-nocheck\n` +
53
150
  `// AUTO-GENERATED by toil — do not edit.\n` +
54
- `import { mount } from 'toiljs/client';\n` +
55
- `import { BinaryWriter, BinaryReader, FastMap, FastSet } from 'toiljs/io';\n` +
56
- `import { routes, layout, notFound } from './routes';\n\n` +
57
- `// Expose toiljs IO as native globals (typed via ./toil-env.d.ts).\n` +
58
- `Object.assign(globalThis, { BinaryWriter, BinaryReader, FastMap, FastSet });\n\n` +
59
- `mount(routes, layout, notFound);\n`;
151
+ `import * as Toil from 'toiljs/client';\n` +
152
+ `import { BinaryWriter, BinaryReader, FastMap, FastSet } from 'toiljs/io';\n\n` +
153
+ `Object.assign(globalThis, { Toil, BinaryWriter, BinaryReader, FastMap, FastSet });\n`;
154
+ fs.writeFileSync(path.join(cfg.toilDir, 'globals.ts'), globalsSrc);
155
+
156
+ const entryFile = findEntry(cfg);
157
+ const entrySrc = entryFile
158
+ ? `// @ts-nocheck\n` +
159
+ `// AUTO-GENERATED by toil — do not edit.\n` +
160
+ `import './globals';\n` +
161
+ `import ${JSON.stringify(relFromToil(cfg, entryFile))};\n`
162
+ : `// @ts-nocheck\n` +
163
+ `// AUTO-GENERATED by toil — do not edit.\n` +
164
+ `import './globals';\n` +
165
+ `import { mount } from 'toiljs/client';\n` +
166
+ `import { routes, layout, notFound } from './routes';\n\n` +
167
+ `mount(routes, layout, notFound);\n`;
60
168
  fs.writeFileSync(path.join(cfg.toilDir, 'entry.tsx'), entrySrc);
61
169
 
62
- // Ambient global types so `new BinaryWriter()` etc. resolve in the IDE without an import.
63
- // Script-mode declaration file (no top-level import/export → the `declare const`s are truly
64
- // global, and it's not a module so it can't confuse ESLint's project service). The inline
65
- // `import('toiljs/io')` type only needs the normal `toiljs/io` export to resolve. At the
66
- // project root because TypeScript's `include` globs skip dot-directories (so it can't live in .toil).
67
- const envSrc =
68
- `// AUTO-GENERATED by toil — do not edit.\n` +
69
- `declare const BinaryWriter: typeof import('toiljs/io').BinaryWriter;\n` +
70
- `declare const BinaryReader: typeof import('toiljs/io').BinaryReader;\n` +
71
- `declare const FastMap: typeof import('toiljs/io').FastMap;\n` +
72
- `declare const FastSet: typeof import('toiljs/io').FastSet;\n`;
73
- fs.writeFileSync(path.join(cfg.root, 'toil-env.d.ts'), envSrc);
74
-
75
- const htmlSrc =
76
- `<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n` +
77
- ` <meta name="viewport" content="width=device-width, initial-scale=1" />\n` +
78
- ` <title>Toil App</title>\n </head>\n <body>\n <div id="root"></div>\n` +
79
- ` <script type="module" src="./entry.tsx"></script>\n </body>\n</html>\n`;
80
- fs.writeFileSync(path.join(cfg.toilDir, 'index.html'), htmlSrc);
170
+ fs.writeFileSync(path.join(cfg.root, 'toil-env.d.ts'), TOIL_ENV_DTS);
171
+
172
+ fs.writeFileSync(path.join(cfg.toilDir, 'index.html'), buildHtml(cfg));
173
+ syncPublicAssets(cfg);
174
+ writeDocs(cfg.toilDir);
81
175
 
82
176
  return routes;
83
177
  }
178
+
179
+ /** Fallback HTML when the project has no `public/index.html` template. The entry script is added
180
+ * by {@link buildHtml}. */
181
+ const DEFAULT_HTML =
182
+ `<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n` +
183
+ ` <meta name="viewport" content="width=device-width, initial-scale=1" />\n` +
184
+ ` <meta name="description" content="" />\n` +
185
+ ` <title>Toil App</title>\n </head>\n <body>\n <div id="root"></div>\n` +
186
+ ` </body>\n</html>\n`;
187
+
188
+ /** The module entry that boots the app, injected into the HTML (resolved relative to `.toil`). */
189
+ const ENTRY_SCRIPT = `<script type="module" src="./entry.tsx"></script>`;
190
+
191
+ /**
192
+ * Produces the `.toil/index.html` Vite entry from the project's `public/index.html` template (or
193
+ * the built-in default if absent), ensuring the generated module entry script is present. Users
194
+ * own the template — toil only guarantees the entry is wired, so it stays the SPA root.
195
+ */
196
+ function buildHtml(cfg: ResolvedToilConfig): string {
197
+ const templatePath = path.join(cfg.publicDir, 'index.html');
198
+ let html = fs.existsSync(templatePath)
199
+ ? fs.readFileSync(templatePath, 'utf8')
200
+ : DEFAULT_HTML;
201
+ // Inject the entry only if the template doesn't already reference it as a module script
202
+ // (matching the literal filename anywhere in the file would be too eager).
203
+ if (!/src=["']\.\/entry\.tsx["']/.test(html)) {
204
+ html = html.includes('</body>')
205
+ ? html.replace('</body>', ` ${ENTRY_SCRIPT}\n </body>`)
206
+ : `${html}\n${ENTRY_SCRIPT}\n`;
207
+ }
208
+ return html;
209
+ }
210
+
211
+ /**
212
+ * Mirrors the project's `public/` assets into `.toil/public/` (Vite's publicDir under the `.toil`
213
+ * root), excluding the `index.html` template — that is processed into the entry above, and copying
214
+ * it here would clobber the built, asset-hashed page. Cleared each run so deletions propagate.
215
+ */
216
+ function syncPublicAssets(cfg: ResolvedToilConfig): void {
217
+ const dest = path.join(cfg.toilDir, 'public');
218
+ fs.rmSync(dest, { recursive: true, force: true });
219
+ if (!fs.existsSync(cfg.publicDir)) return;
220
+
221
+ let copied = 0;
222
+ for (const entry of fs.readdirSync(cfg.publicDir, { withFileTypes: true })) {
223
+ if (entry.name === 'index.html') continue;
224
+ fs.cpSync(path.join(cfg.publicDir, entry.name), path.join(dest, entry.name), {
225
+ recursive: true,
226
+ });
227
+ copied++;
228
+ }
229
+ if (copied === 0) fs.rmSync(dest, { recursive: true, force: true });
230
+ }
@@ -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,6 +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';
48
- export type { ToilConfig } from './config.js';
47
+ export { defineConfig, loadConfig } from './config.js';
48
+ export { TOIL_ENV_DTS } from './generate.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';
49
52
  export type { RunningBackend, BackendOptions } from 'toiljs/backend';
@@ -12,8 +12,10 @@ export function toilPlugin(cfg: ResolvedToilConfig): Plugin {
12
12
  return {
13
13
  name: 'toil',
14
14
  configureServer(server) {
15
+ // Trailing slash so a sibling like `routes-extra/` doesn't match the `routes/` prefix.
16
+ const routesPrefix = cfg.routesAbsDir.replace(/\\/g, '/').replace(/\/?$/, '/');
15
17
  const onChange = (file: string): void => {
16
- if (file.replace(/\\/g, '/').startsWith(cfg.routesAbsDir.replace(/\\/g, '/'))) {
18
+ if (file.replace(/\\/g, '/').startsWith(routesPrefix)) {
17
19
  generate(cfg);
18
20
  server.ws.send({ type: 'full-reload' });
19
21
  }
@@ -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)),
@@ -1,8 +1,10 @@
1
+ import { createRequire } from 'node:module';
1
2
  import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
2
4
 
3
5
  import react from '@vitejs/plugin-react';
4
6
  import { nodePolyfills } from 'vite-plugin-node-polyfills';
5
- import { mergeConfig, type InlineConfig } from 'vite';
7
+ import { mergeConfig, type InlineConfig, type PluginOption } from 'vite';
6
8
 
7
9
  import { type ResolvedToilConfig } from './config.js';
8
10
  import { toilPlugin } from './plugin.js';
@@ -21,6 +23,23 @@ function assetFileName(name: string): string {
21
23
  return 'assets/[name][extname]';
22
24
  }
23
25
 
26
+ /**
27
+ * Loads the Tailwind v4 Vite plugin if the project has `@tailwindcss/vite` installed (added by
28
+ * `toiljs create`/`configure` when Tailwind is enabled). Resolved from the project root so it picks
29
+ * up the project's copy; returns `undefined` when Tailwind is off, so the plugin simply isn't added.
30
+ */
31
+ async function tailwindPlugin(root: string): Promise<PluginOption | undefined> {
32
+ let resolved: string;
33
+ try {
34
+ resolved = createRequire(path.join(root, 'package.json')).resolve('@tailwindcss/vite');
35
+ } catch {
36
+ return undefined;
37
+ }
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- dynamic import() is typed any
39
+ const mod: { default?: () => PluginOption } = await import(pathToFileURL(resolved).href);
40
+ return mod.default?.();
41
+ }
42
+
24
43
  /** Splits React's runtime into its own long-lived chunk for better caching. */
25
44
  function manualChunks(id: string): string | undefined {
26
45
  if (!id.includes('node_modules')) return undefined;
@@ -36,21 +55,24 @@ function manualChunks(id: string): string | undefined {
36
55
 
37
56
  /**
38
57
  * Builds the framework-owned Vite config. Vite's `root` is the generated `.toil` dir so its
39
- * `index.html` emits at the output root (assets resolve correctly); `fs.allow` opens the
40
- * project (for `client/`) and the framework runtime. The opinionated default Node polyfills
58
+ * `index.html` (built from the project's `public/index.html` template) emits at the output root
59
+ * with assets resolving correctly; static `public/` assets are mirrored to `.toil/public` and
60
+ * picked up via Vite's default publicDir. `fs.allow` opens the project (for `client/`) and the
61
+ * framework runtime. The opinionated default — Node polyfills
41
62
  * (Buffer/global/process), React plugin, toil route plugin, typed asset folders, React chunk
42
63
  * splitting and tuned build options — is applied here; `toiljs/client` is aliased to the
43
64
  * runtime, and the user's `client.vite` overrides deep-merge on top.
44
65
  */
45
- export function createViteConfig(cfg: ResolvedToilConfig): InlineConfig {
46
- // .../build/client/index.js -> framework package root (covers build/ + node_modules in dev)
66
+ export async function createViteConfig(cfg: ResolvedToilConfig): Promise<InlineConfig> {
47
67
  const frameworkRoot = path.resolve(path.dirname(cfg.runtimePath), '..', '..');
68
+ const tailwind = await tailwindPlugin(cfg.root);
48
69
 
49
70
  const base: InlineConfig = {
50
71
  root: cfg.toilDir,
51
72
  base: cfg.base,
52
73
  configFile: false,
53
74
  plugins: [
75
+ tailwind,
54
76
  nodePolyfills({ globals: { Buffer: true, global: true, process: true } }),
55
77
  react(),
56
78
  toilPlugin(cfg),
@@ -58,6 +80,7 @@ export function createViteConfig(cfg: ResolvedToilConfig): InlineConfig {
58
80
  resolve: {
59
81
  alias: {
60
82
  'toiljs/client': cfg.runtimePath,
83
+ 'toiljs/routes': path.join(cfg.toilDir, 'routes.ts'),
61
84
  },
62
85
  dedupe: ['react', 'react-dom'],
63
86
  },
@@ -25,7 +25,6 @@ export class BinaryReader {
25
25
  return this.buffer.byteLength;
26
26
  }
27
27
 
28
- // Helpers for comparisons; unchanged
29
28
  public static stringCompare(a: string, b: string): number {
30
29
  return a.localeCompare(b);
31
30
  }
@@ -123,7 +122,6 @@ export class BinaryReader {
123
122
  public readU128(be: boolean = true): bigint {
124
123
  const raw = this.readBytes(U128_BYTE_LENGTH);
125
124
  let bytes = raw;
126
- // If data was written in little-endian, we reverse before interpreting
127
125
  if (!be) {
128
126
  bytes = this.reverseBytes(raw);
129
127
  }
@@ -150,10 +148,8 @@ export class BinaryReader {
150
148
  bytes = this.reverseBytes(raw);
151
149
  }
152
150
 
153
- // Construct as a 128-bit two's complement
154
151
  let value = BigInt('0x' + this.toHexString(bytes));
155
152
 
156
- // If the top bit is set (sign bit in big-endian), interpret negative
157
153
  const signBitMask = 0x80;
158
154
  if ((bytes[0] as number) & signBitMask) {
159
155
  const twoTo128 = BigInt(1) << BigInt(128);
@@ -281,7 +277,7 @@ export class BinaryReader {
281
277
  }
282
278
 
283
279
  public readU8Array(): u8[] {
284
- const length = this.readU16(true); // by default big-endian
280
+ const length = this.readU16(true);
285
281
  const result: u8[] = new Array<u8>(length);
286
282
  for (let i = 0; i < length; i++) {
287
283
  result[i] = this.readU8();