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
package/src/cli/create.ts CHANGED
@@ -3,17 +3,61 @@
3
3
  * app to the enforced toiljs presets (tsconfig / eslint / prettier) and file-based routing.
4
4
  * Supports a non-interactive path via flags (`--yes`, `--template`, …) for scripting/CI.
5
5
  */
6
- import { spawn } from 'node:child_process';
7
6
  import fs from 'node:fs/promises';
8
7
  import path from 'node:path';
9
8
 
10
- import { intro, outro, text, select, confirm, isCancel, cancel, spinner, note } from '@clack/prompts';
9
+ import {
10
+ intro,
11
+ outro,
12
+ text,
13
+ select,
14
+ multiselect,
15
+ confirm,
16
+ isCancel,
17
+ cancel,
18
+ spinner,
19
+ note,
20
+ } from '@clack/prompts';
21
+ import { AI_HELPERS, AI_HELPER_IDS, aiHelperFiles, TOIL_DOCS, TOIL_ENV_DTS } from 'toiljs/compiler';
11
22
  import pc from 'picocolors';
12
23
 
24
+ import {
25
+ PKG_VERSION,
26
+ PREPROCESSORS,
27
+ requiredPackages,
28
+ styleEntry,
29
+ styleImportLines,
30
+ TAILWIND_CSS,
31
+ TAILWIND_ENTRY,
32
+ type Preprocessor,
33
+ type StyleFeatures,
34
+ } from './features.js';
35
+ import { run } from './proc.js';
13
36
  import { accent, dim, version } from './ui.js';
37
+ import { isPackageManager, isValidName, resolveProjectDir } from './validate.js';
14
38
 
15
39
  export type Template = 'app' | 'minimal';
16
40
 
41
+ /** Human label for each preprocessor in the styling picker. */
42
+ const PREPROCESSOR_LABEL: Record<Preprocessor, string> = {
43
+ css: 'Plain CSS',
44
+ sass: 'Sass (SCSS)',
45
+ less: 'Less',
46
+ stylus: 'Stylus',
47
+ };
48
+
49
+ /** Default global stylesheet contents (palette base styles), shared by every preprocessor. */
50
+ const DEFAULT_STYLE_CONTENT =
51
+ ':root {\n color-scheme: dark;\n}\n\n' +
52
+ 'body {\n margin: 0;\n background: #080d11;\n color: #f5f6fa;\n' +
53
+ ' font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n}\n\n' +
54
+ 'a {\n color: #2563ff;\n text-decoration: none;\n}\n\n' +
55
+ 'a:hover {\n color: #22e3ab;\n}\n\n' +
56
+ 'code {\n background: #11161f;\n color: #22e3ab;\n padding: 0.1rem 0.4rem;\n' +
57
+ ' border-radius: 4px;\n font-size: 0.9em;\n}\n\n' +
58
+ 'h1 {\n background: linear-gradient(90deg, #2563ff, #7c3aed, #22e3ab);\n' +
59
+ ' -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n';
60
+
17
61
  /** A selectable template in the `create` wizard. */
18
62
  interface TemplateOption {
19
63
  readonly value: Template;
@@ -24,6 +68,10 @@ interface TemplateOption {
24
68
  export interface CreateOptions {
25
69
  readonly name?: string;
26
70
  readonly template?: Template;
71
+ readonly preprocessor?: Preprocessor;
72
+ readonly tailwind?: boolean;
73
+ /** AI assistant files to scaffold: `true` = all, `false` = none, omitted = ask. */
74
+ readonly ai?: boolean;
27
75
  readonly install?: boolean;
28
76
  readonly git?: boolean;
29
77
  readonly pm?: string;
@@ -39,77 +87,165 @@ function bail<T>(value: T | symbol): asserts value is T {
39
87
  }
40
88
  }
41
89
 
42
- function isValidName(name: string): true | string {
43
- if (!name.trim()) return 'Please enter a project name.';
44
- if (!/^[a-z0-9._@/-]+$/i.test(name)) return 'Use letters, numbers, dashes, dots or slashes.';
45
- return true;
46
- }
47
-
48
90
  async function isEmptyDir(dir: string): Promise<boolean> {
49
91
  try {
50
92
  const entries = await fs.readdir(dir);
51
93
  return entries.length === 0;
52
94
  } catch {
53
- return true; // doesn't exist yet
95
+ return true;
54
96
  }
55
97
  }
56
98
 
57
99
  /** Builds the full file map (relative path → contents) for a scaffolded project. */
58
- function scaffold(name: string, template: Template): Record<string, string> {
100
+ function scaffold(
101
+ name: string,
102
+ template: Template,
103
+ features: StyleFeatures,
104
+ aiTools: readonly string[],
105
+ ): Record<string, string> {
59
106
  const toilVersion = version();
107
+ const devDependencies: Record<string, string> = {
108
+ '@types/react': '^19.2.15',
109
+ '@types/react-dom': '^19.2.3',
110
+ eslint: '^10.2.0',
111
+ prettier: '^3.8.1',
112
+ toilscript: '^0.1.2',
113
+ typescript: '^6.0.3',
114
+ };
115
+ for (const dep of requiredPackages(features).sort()) {
116
+ devDependencies[dep] = PKG_VERSION[dep] ?? 'latest';
117
+ }
60
118
  const pkg = {
61
119
  name: path.basename(name),
62
120
  private: true,
63
121
  type: 'module',
64
122
  scripts: {
65
123
  dev: 'toiljs dev',
66
- build: 'toiljs build',
124
+ build: 'toiljs build && toilscript --target release',
125
+ 'build:client': 'toiljs build',
126
+ 'build:server': 'toilscript --target release',
67
127
  lint: 'eslint client',
68
128
  typecheck: 'tsc --noEmit',
69
- format: 'prettier --write "client/**/*.{ts,tsx}"',
129
+ format: 'prettier --write "client/**/*.{ts,tsx,css,scss,less}" "client/public/**/*.html"',
70
130
  },
71
131
  dependencies: {
72
132
  toiljs: `^${toilVersion}`,
73
133
  react: '^19.2.6',
74
134
  'react-dom': '^19.2.6',
75
135
  },
76
- devDependencies: {
77
- '@types/react': '^19.2.15',
78
- '@types/react-dom': '^19.2.3',
79
- eslint: '^10.2.0',
80
- prettier: '^3.8.1',
81
- typescript: '^6.0.3',
82
- },
136
+ devDependencies,
83
137
  };
84
138
 
85
139
  const files: Record<string, string> = {
86
140
  'package.json': JSON.stringify(pkg, null, 4) + '\n',
87
141
  'toil.config.ts':
88
142
  "import { defineConfig } from 'toiljs/compiler';\n\n" +
89
- 'export default defineConfig({\n client: {\n outDir: \'dist\',\n },\n});\n',
143
+ 'export default defineConfig({});\n',
90
144
  'tsconfig.json':
91
145
  '{\n "extends": "toiljs/tsconfig",\n "include": ["client", "toil-env.d.ts"]\n}\n',
92
146
  'eslint.config.js': "import toiljs from 'toiljs/eslint';\n\nexport default toiljs;\n",
93
147
  '.prettierrc': '"toiljs/prettier"\n',
94
- '.gitignore': 'node_modules\ndist\n.toil\ntoil-env.d.ts\n',
148
+ '.gitignore': 'node_modules\nbuild\n.toil\ntoil-env.d.ts\n',
149
+ 'client/public/index.html':
150
+ '<!doctype html>\n<html lang="en">\n <head>\n' +
151
+ ' <meta charset="utf-8" />\n' +
152
+ ' <meta name="viewport" content="width=device-width, initial-scale=1" />\n' +
153
+ ' <meta name="theme-color" content="#080D11" />\n' +
154
+ ' <meta name="description" content="" />\n' +
155
+ ' <link rel="icon" type="image/svg+xml" href="/favicon.svg" />\n' +
156
+ ' <link rel="manifest" href="/manifest.webmanifest" />\n' +
157
+ ` <title>${path.basename(name)}</title>\n` +
158
+ ' </head>\n <body>\n <div id="root"></div>\n </body>\n</html>\n',
159
+ 'client/public/favicon.svg':
160
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">\n' +
161
+ ' <defs>\n' +
162
+ ' <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">\n' +
163
+ ' <stop offset="0" stop-color="#2563FF" />\n' +
164
+ ' <stop offset="0.5" stop-color="#7C3AED" />\n' +
165
+ ' <stop offset="1" stop-color="#22E3AB" />\n' +
166
+ ' </linearGradient>\n' +
167
+ ' </defs>\n' +
168
+ ' <rect width="32" height="32" rx="7" fill="#080D11" />\n' +
169
+ ' <path d="M9 10h14v3.2h-5.4V24h-3.2V13.2H9z" fill="url(#g)" />\n' +
170
+ '</svg>\n',
171
+ 'client/public/robots.txt': 'User-agent: *\nAllow: /\n',
172
+ 'client/public/manifest.webmanifest':
173
+ JSON.stringify(
174
+ {
175
+ name: path.basename(name),
176
+ short_name: path.basename(name),
177
+ start_url: '/',
178
+ display: 'standalone',
179
+ background_color: '#080D11',
180
+ theme_color: '#080D11',
181
+ icons: [{ src: '/favicon.svg', type: 'image/svg+xml', sizes: 'any' }],
182
+ },
183
+ null,
184
+ 4,
185
+ ) + '\n',
186
+ 'client/public/images/.gitkeep': '# Place images and other static assets here; served at /images/*.\n',
187
+ 'client/toil.tsx':
188
+ "import { routes, layout, notFound } from 'toiljs/routes';\n\n" +
189
+ styleImportLines(features).join('\n') +
190
+ '\n\n' +
191
+ 'Toil.mount(routes, layout, notFound);\n',
192
+ [`client/${styleEntry(features.preprocessor)}`]: DEFAULT_STYLE_CONTENT,
193
+ 'client/components/.gitkeep': '# Place shared React components here.\n',
194
+ 'toil-env.d.ts': TOIL_ENV_DTS,
195
+ 'toilconfig.json':
196
+ JSON.stringify(
197
+ {
198
+ entries: ['server/main.ts'],
199
+ targets: {
200
+ release: {
201
+ outFile: 'build/server/release.wasm',
202
+ textFile: 'build/server/release.wat',
203
+ },
204
+ },
205
+ options: {
206
+ sourceMap: false,
207
+ optimizeLevel: 3,
208
+ shrinkLevel: 1,
209
+ converge: true,
210
+ noAssert: false,
211
+ enable: [
212
+ 'sign-extension',
213
+ 'mutable-globals',
214
+ 'nontrapping-f2i',
215
+ 'bulk-memory',
216
+ 'simd',
217
+ 'reference-types',
218
+ 'multi-value',
219
+ ],
220
+ runtime: 'stub',
221
+ memoryBase: 0,
222
+ initialMemory: 1,
223
+ debug: false,
224
+ trapMode: 'allow',
225
+ },
226
+ },
227
+ null,
228
+ 4,
229
+ ) + '\n',
230
+ 'server/tsconfig.json':
231
+ JSON.stringify(
232
+ {
233
+ extends: 'toilscript/std/assembly.json',
234
+ include: ['./**/*.ts'],
235
+ },
236
+ null,
237
+ 4,
238
+ ) + '\n',
239
+ 'server/index.ts': 'export function add(a: i32, b: i32): i32 {\n return a + b;\n}\n',
240
+ 'server/main.ts':
241
+ "import { add } from './index';\n\n" +
242
+ '@main\nfunction run(): i32 {\n return add(40, 2);\n}\n',
95
243
  'README.md': ['# ' + path.basename(name), '', 'A [toiljs](https://toil.org) app.', '', '## Develop', '', ' npm install', ' npm run dev', '', '## Build', '', ' npm run build', ''].join('\n'),
96
244
  'client/layout.tsx': `import { type ReactNode } from 'react';
97
245
 
98
- import { Link } from 'toiljs/client';
99
-
100
- const styles = \`
101
- :root { color-scheme: dark; }
102
- body { margin: 0; background: #080D11; color: #F5F6FA; font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; }
103
- a { color: #2563FF; text-decoration: none; }
104
- a:hover { color: #22E3AB; }
105
- code { background: #11161f; color: #22E3AB; padding: 0.1rem 0.4rem; border-radius: 4px; font-size: 0.9em; }
106
- h1 { background: linear-gradient(90deg, #2563FF, #7C3AED, #22E3AB); -webkit-background-clip: text; background-clip: text; color: transparent; }
107
- \`;
108
-
109
246
  export default function Layout({ children }: { children?: ReactNode }) {
110
247
  return (
111
248
  <div style={{ maxWidth: 680, margin: '0 auto', padding: '3rem 1.5rem' }}>
112
- <style>{styles}</style>
113
249
  <header
114
250
  style={{
115
251
  display: 'flex',
@@ -121,7 +257,7 @@ export default function Layout({ children }: { children?: ReactNode }) {
121
257
  }}>
122
258
  <strong style={{ color: '#2563FF', fontSize: '1.1rem' }}>${path.basename(name)}</strong>
123
259
  <nav style={{ display: 'flex', gap: '1rem' }}>
124
- <Link href="/">home</Link>${template === 'app' ? '\n <Link href="/about">about</Link>' : ''}
260
+ <Toil.Link href="/">home</Toil.Link>${template === 'app' ? '\n <Toil.Link href="/about">about</Toil.Link>' : ''}
125
261
  </nav>
126
262
  </header>
127
263
  {children}
@@ -130,31 +266,39 @@ export default function Layout({ children }: { children?: ReactNode }) {
130
266
  }
131
267
  `,
132
268
  'client/routes/index.tsx':
133
- "import { Link } from 'toiljs/client';\n\n" +
134
269
  'export default function Home() {\n' +
135
270
  ' return (\n <main>\n' +
136
271
  ' <h1>Welcome to toiljs</h1>\n' +
137
272
  ' <p>File-based routing, bundled by Vite, zero config.</p>\n' +
138
273
  (template === 'app'
139
- ? ' <p>\n <Link href="/about">About</Link> · <Link href="/blog/42">Blog post 42</Link>\n </p>\n'
274
+ ? ' <p>\n <Toil.Link href="/about">About</Toil.Link> · <Toil.Link href="/blog/42">Blog post 42</Toil.Link>\n </p>\n'
140
275
  : '') +
141
276
  ' </main>\n );\n}\n',
142
277
  };
143
278
 
144
279
  if (template === 'app') {
145
280
  files['client/routes/about.tsx'] =
146
- "import { Link } from 'toiljs/client';\n\n" +
147
281
  'export default function About() {\n' +
148
282
  ' return (\n <main>\n <h1>About</h1>\n' +
149
283
  ' <p>\n This page is served by <code>client/routes/about.tsx</code>.\n </p>\n' +
150
- ' <Link href="/">Back home</Link>\n </main>\n );\n}\n';
284
+ ' <Toil.Link href="/">Back home</Toil.Link>\n </main>\n );\n}\n';
151
285
  files['client/routes/blog/[id].tsx'] =
152
- "import { Link, useParams } from 'toiljs/client';\n\n" +
153
286
  'export default function BlogPost() {\n' +
154
- ' const { id } = useParams();\n' +
287
+ ' const { id } = Toil.useParams();\n' +
155
288
  ' return (\n <main>\n <h1>Blog post {id}</h1>\n' +
156
289
  ' <p>\n Dynamic route from <code>client/routes/blog/[id].tsx</code>.\n </p>\n' +
157
- ' <Link href="/">Back home</Link>\n </main>\n );\n}\n';
290
+ ' <Toil.Link href="/">Back home</Toil.Link>\n </main>\n );\n}\n';
291
+ }
292
+
293
+ if (features.tailwind) {
294
+ files[`client/${TAILWIND_ENTRY}`] = TAILWIND_CSS;
295
+ }
296
+
297
+ // Selected AI-assistant pointer files at the root (committed). The real docs are always seeded
298
+ // under .toil/docs (gitignored; regenerated by dev/build) since the framework manages them.
299
+ Object.assign(files, aiHelperFiles(aiTools));
300
+ for (const [name, content] of Object.entries(TOIL_DOCS)) {
301
+ files[`.toil/docs/${name}`] = content;
158
302
  }
159
303
 
160
304
  return files;
@@ -168,27 +312,10 @@ async function writeFiles(dir: string, files: Record<string, string>): Promise<v
168
312
  }
169
313
  }
170
314
 
171
- function run(cmd: string, args: string[], cwd: string): Promise<void> {
172
- return new Promise((resolve, reject) => {
173
- // On Windows `npm`/`pnpm`/`yarn` are `.cmd` shims that need a shell to launch. Passing an
174
- // args array together with `shell: true` is deprecated (DEP0190), so pass the whole command
175
- // as one string there (the args are fixed, not user input). POSIX runs it directly.
176
- const onWindows = process.platform === 'win32';
177
- const child = onWindows
178
- ? spawn([cmd, ...args].join(' '), { cwd, stdio: 'ignore', shell: true })
179
- : spawn(cmd, args, { cwd, stdio: 'ignore' });
180
- child.on('error', reject);
181
- child.on('close', (code) =>
182
- code === 0 ? resolve() : reject(new Error(`${cmd} exited with code ${String(code)}`)),
183
- );
184
- });
185
- }
186
-
187
315
  /** Runs the create flow (interactive unless `--yes`). */
188
316
  export async function runCreate(opts: CreateOptions): Promise<void> {
189
317
  intro(accent(' toiljs create '));
190
318
 
191
- // 1. Project name
192
319
  let name = opts.name;
193
320
  if (!name) {
194
321
  if (opts.yes) {
@@ -213,10 +340,13 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
213
340
  process.exit(1);
214
341
  }
215
342
 
216
- const targetDir = path.resolve(opts.cwd, name);
343
+ const targetDir = resolveProjectDir(opts.cwd, name);
344
+ if (targetDir === null) {
345
+ cancel('Project name must stay inside the current directory (no "..", no absolute paths).');
346
+ process.exit(1);
347
+ }
217
348
  const rel = path.relative(opts.cwd, targetDir) || '.';
218
349
 
219
- // 2. Guard against clobbering a non-empty dir
220
350
  if (!(await isEmptyDir(targetDir))) {
221
351
  if (opts.yes) {
222
352
  cancel(`Directory ${pc.cyan(rel)} is not empty.`);
@@ -233,7 +363,6 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
233
363
  }
234
364
  }
235
365
 
236
- // 3. Template
237
366
  let template: Template = opts.template ?? 'app';
238
367
  if (!opts.template && !opts.yes) {
239
368
  const templateOptions: TemplateOption[] = [
@@ -245,10 +374,46 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
245
374
  template = choice === 'minimal' ? 'minimal' : 'app';
246
375
  }
247
376
 
248
- // 4. Options: git + install
377
+ let preprocessor: Preprocessor = opts.preprocessor ?? 'css';
378
+ let tailwind = opts.tailwind ?? false;
379
+ if (!opts.yes) {
380
+ if (opts.preprocessor === undefined) {
381
+ const choice = await select<Preprocessor>({
382
+ message: 'Styling',
383
+ options: PREPROCESSORS.map((value) => ({ value, label: PREPROCESSOR_LABEL[value] })),
384
+ initialValue: 'css',
385
+ });
386
+ bail(choice);
387
+ preprocessor = choice;
388
+ }
389
+ if (opts.tailwind === undefined) {
390
+ const tw = await confirm({ message: 'Add Tailwind CSS?', initialValue: false });
391
+ bail(tw);
392
+ tailwind = tw;
393
+ }
394
+ }
395
+ const features: StyleFeatures = { preprocessor, tailwind };
396
+
397
+ // AI assistant files: --ai = all, --no-ai = none, otherwise ask (default: all selected).
398
+ let aiTools: string[] = opts.ai === false ? [] : [...AI_HELPER_IDS];
399
+ if (opts.ai === undefined && !opts.yes) {
400
+ const picked = await multiselect<string>({
401
+ message: 'AI assistant files (read by Claude, Cursor, Codex, Copilot)',
402
+ options: AI_HELPERS.map((h) => ({ value: h.id, label: h.label })),
403
+ initialValues: [...AI_HELPER_IDS],
404
+ required: false,
405
+ });
406
+ bail(picked);
407
+ aiTools = picked;
408
+ }
409
+
249
410
  let initGit = opts.git ?? false;
250
411
  let install = opts.install ?? false;
251
412
  const pm = opts.pm ?? 'npm';
413
+ if (!isPackageManager(pm)) {
414
+ cancel(`Unsupported package manager: ${pm} (use npm, pnpm, yarn, or bun).`);
415
+ process.exit(1);
416
+ }
252
417
  if (!opts.yes) {
253
418
  if (opts.git === undefined) {
254
419
  const g = await confirm({ message: 'Initialize a git repository?', initialValue: true });
@@ -262,13 +427,11 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
262
427
  }
263
428
  }
264
429
 
265
- // 5. Scaffold
266
430
  const s = spinner();
267
431
  s.start('Scaffolding project');
268
- await writeFiles(targetDir, scaffold(name, template));
432
+ await writeFiles(targetDir, scaffold(name, template, features, aiTools));
269
433
  s.stop(`Scaffolded ${pc.cyan(rel)}`);
270
434
 
271
- // 6. git init (best-effort)
272
435
  if (initGit) {
273
436
  const g = spinner();
274
437
  g.start('Initializing git repository');
@@ -281,7 +444,6 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
281
444
  }
282
445
  }
283
446
 
284
- // 7. Install (best-effort)
285
447
  if (install) {
286
448
  const i = spinner();
287
449
  i.start(`Installing dependencies with ${pm}`);
@@ -294,7 +456,6 @@ export async function runCreate(opts: CreateOptions): Promise<void> {
294
456
  }
295
457
  }
296
458
 
297
- // 8. Next steps
298
459
  const steps: string[] = [];
299
460
  if (rel !== '.') steps.push(`cd ${rel}`);
300
461
  if (!install) steps.push('npm install');
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Pure description of toiljs's optional client styling features — a CSS preprocessor and Tailwind —
3
+ * shared by `create` (scaffold) and `configure` (toggle on existing projects). Dependency-light
4
+ * (no node IO) so it can be unit-tested; the file writes and package-manager calls live in the
5
+ * commands. Preprocessor and Tailwind are independent: Tailwind lives in its own `.css` entry so
6
+ * it never passes through (and breaks on) a preprocessor's `@import` resolution.
7
+ */
8
+
9
+ /** Supported CSS preprocessor (`css` = none). */
10
+ export type Preprocessor = 'css' | 'sass' | 'less' | 'stylus';
11
+
12
+ /** The two independently-toggleable styling features of a project. */
13
+ export interface StyleFeatures {
14
+ readonly preprocessor: Preprocessor;
15
+ readonly tailwind: boolean;
16
+ }
17
+
18
+ export const PREPROCESSORS: readonly Preprocessor[] = ['css', 'sass', 'less', 'stylus'];
19
+
20
+ /** Main stylesheet extension for each preprocessor. */
21
+ export const STYLE_EXT: Record<Preprocessor, string> = {
22
+ css: 'css',
23
+ sass: 'scss',
24
+ less: 'less',
25
+ stylus: 'styl',
26
+ };
27
+
28
+ /** npm package that enables each preprocessor in Vite (plain CSS needs none). */
29
+ export const PREPROCESSOR_PKG: Record<Preprocessor, string | null> = {
30
+ css: null,
31
+ sass: 'sass',
32
+ less: 'less',
33
+ stylus: 'stylus',
34
+ };
35
+
36
+ /** Tailwind v4 packages. The framework auto-wires the Vite plugin when `@tailwindcss/vite` resolves. */
37
+ export const TAILWIND_PKGS: readonly string[] = ['tailwindcss', '@tailwindcss/vite'];
38
+
39
+ /** Pinned versions for every package these features may install. */
40
+ export const PKG_VERSION: Record<string, string> = {
41
+ sass: '^1.83.0',
42
+ less: '^4.2.1',
43
+ stylus: '^0.64.0',
44
+ tailwindcss: '^4.0.0',
45
+ '@tailwindcss/vite': '^4.0.0',
46
+ };
47
+
48
+ /** Dedicated Tailwind entry (kept `.css` so no preprocessor touches its `@import`). */
49
+ export const TAILWIND_ENTRY = 'styles/tailwind.css';
50
+ export const TAILWIND_CSS = `@import 'tailwindcss';\n`;
51
+
52
+ /** Path (relative to `client/`) of the main stylesheet for a preprocessor. */
53
+ export function styleEntry(p: Preprocessor): string {
54
+ return `styles/main.${STYLE_EXT[p]}`;
55
+ }
56
+
57
+ /** The preprocessor whose main stylesheet uses `ext` (with or without a leading dot), or null. */
58
+ export function preprocessorForExt(ext: string): Preprocessor | null {
59
+ const e = ext.replace(/^\./, '');
60
+ if (e === 'sass') return 'sass';
61
+ return PREPROCESSORS.find((p) => STYLE_EXT[p] === e) ?? null;
62
+ }
63
+
64
+ /** Packages required by a feature set (preprocessor package + Tailwind packages). */
65
+ export function requiredPackages(f: StyleFeatures): string[] {
66
+ const pkgs: string[] = [];
67
+ const pp = PREPROCESSOR_PKG[f.preprocessor];
68
+ if (pp) pkgs.push(pp);
69
+ if (f.tailwind) pkgs.push(...TAILWIND_PKGS);
70
+ return pkgs;
71
+ }
72
+
73
+ /** Managed packages to add and remove when moving from `from` to `to`. */
74
+ export function packageDiff(
75
+ from: StyleFeatures,
76
+ to: StyleFeatures,
77
+ ): { add: string[]; remove: string[] } {
78
+ const want = new Set(requiredPackages(to));
79
+ const had = new Set(requiredPackages(from));
80
+ return {
81
+ add: [...want].filter((p) => !had.has(p)),
82
+ remove: [...had].filter((p) => !want.has(p)),
83
+ };
84
+ }
85
+
86
+ /** The side-effect style imports for the app entry, Tailwind first so app CSS can override it. */
87
+ export function styleImportLines(f: StyleFeatures): string[] {
88
+ const lines: string[] = [];
89
+ if (f.tailwind) lines.push(`import './${TAILWIND_ENTRY}';`);
90
+ lines.push(`import './${styleEntry(f.preprocessor)}';`);
91
+ return lines;
92
+ }
93
+
94
+ /**
95
+ * Rewrites the `./styles/*` side-effect imports in an app entry (`client/toil.tsx`) to match
96
+ * `features`, preserving the rest of the file. Existing style imports are removed and the new
97
+ * block is placed after the `toiljs/routes` import (or the last import, or the top).
98
+ */
99
+ export function setStyleImports(source: string, f: StyleFeatures): string {
100
+ const stripped = source.replace(/^[ \t]*import\s+['"]\.\/styles\/[^'"]+['"];?[ \t]*\r?\n/gm, '');
101
+ const block = styleImportLines(f).join('\n') + '\n';
102
+
103
+ const lines = stripped.split('\n');
104
+ const routesIdx = lines.findIndex((l) => /from\s+['"]toiljs\/routes['"]/.test(l));
105
+ let insertAt: number;
106
+ if (routesIdx !== -1) {
107
+ insertAt = routesIdx + 1;
108
+ } else {
109
+ const lastImport = lines.reduce((acc, l, i) => (/^\s*import\s/.test(l) ? i : acc), -1);
110
+ insertAt = lastImport + 1;
111
+ }
112
+ const head = lines.slice(0, insertAt).join('\n');
113
+ const tail = lines.slice(insertAt).join('\n');
114
+ return `${head}\n\n${block}\n${tail}`.replace(/\n{3,}/g, '\n\n');
115
+ }
116
+
117
+ /** Detects the active preprocessor from a project's combined dependency map. */
118
+ export function detectPreprocessor(deps: Record<string, string>): Preprocessor {
119
+ if ('sass' in deps) return 'sass';
120
+ if ('less' in deps) return 'less';
121
+ if ('stylus' in deps) return 'stylus';
122
+ return 'css';
123
+ }
124
+
125
+ /** Whether Tailwind is installed in a project's combined dependency map. */
126
+ export function detectTailwind(deps: Record<string, string>): boolean {
127
+ return '@tailwindcss/vite' in deps || 'tailwindcss' in deps;
128
+ }