toiljs 0.0.4 → 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.
- package/.idea/prettier.xml +1 -0
- package/as-pect.config.js +1 -1
- package/build/backend/.tsbuildinfo +1 -1
- package/build/backend/index.js +1 -2
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/configure.d.ts +15 -0
- package/build/cli/configure.js +201 -0
- package/build/cli/create.d.ts +4 -0
- package/build/cli/create.js +168 -57
- package/build/cli/features.d.ts +23 -0
- package/build/cli/features.js +85 -0
- package/build/cli/index.js +42 -2
- package/build/cli/proc.d.ts +1 -0
- package/build/cli/proc.js +11 -0
- package/build/cli/ui.js +1 -2
- package/build/cli/validate.d.ts +4 -0
- package/build/cli/validate.js +19 -0
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/Link.d.ts +8 -0
- package/build/client/Link.js +44 -0
- package/build/client/NavLink.d.ts +14 -0
- package/build/client/NavLink.js +37 -0
- package/build/client/Router.d.ts +7 -0
- package/build/client/Router.js +55 -0
- package/build/client/error-boundary.d.ts +16 -0
- package/build/client/error-boundary.js +19 -0
- package/build/client/head.d.ts +26 -0
- package/build/client/head.js +87 -0
- package/build/client/hooks.d.ts +17 -0
- package/build/client/hooks.js +48 -0
- package/build/client/index.d.ts +14 -2
- package/build/client/index.js +8 -1
- package/build/client/lazy.d.ts +16 -0
- package/build/client/lazy.js +53 -0
- package/build/client/match.js +7 -0
- package/build/client/mount.d.ts +2 -0
- package/build/client/mount.js +13 -0
- package/build/client/navigation.d.ts +13 -0
- package/build/client/navigation.js +97 -0
- package/build/client/params-context.d.ts +2 -0
- package/build/client/params-context.js +2 -0
- package/build/client/prefetch.d.ts +11 -0
- package/build/client/prefetch.js +100 -0
- package/build/client/scroll.d.ts +8 -0
- package/build/client/scroll.js +36 -0
- package/build/client/types.d.ts +27 -0
- package/build/client/types.js +1 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +2 -0
- package/build/compiler/config.js +4 -1
- package/build/compiler/docs.d.ts +10 -0
- package/build/compiler/docs.js +59 -0
- package/build/compiler/generate.js +134 -16
- package/build/compiler/index.d.ts +4 -2
- package/build/compiler/index.js +4 -3
- package/build/compiler/plugin.js +2 -1
- package/build/compiler/routes.js +5 -1
- package/build/compiler/vite.d.ts +1 -1
- package/build/compiler/vite.js +17 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/io/BinaryWriter.js +2 -2
- package/eslint.config.js +1 -1
- package/examples/basic/.toil/docs/cli.md +3 -0
- package/examples/basic/.toil/docs/client.md +3 -0
- package/examples/basic/.toil/docs/index.md +3 -0
- package/examples/basic/.toil/docs/routing.md +3 -0
- package/examples/basic/.toil/docs/server.md +3 -0
- package/examples/basic/.toil/docs/styling.md +3 -0
- package/examples/basic/.toil/entry.tsx +3 -8
- package/examples/basic/.toil/globals.ts +6 -0
- package/examples/basic/.toil/index.html +16 -12
- package/examples/basic/.toil/public/images/.gitkeep +1 -0
- package/examples/basic/.toil/public/images/logo.svg +37 -0
- package/examples/basic/.toil/public/robots.txt +2 -0
- package/examples/basic/.toil/routes.ts +9 -7
- package/examples/basic/build/client/assets/404-Bq0jNTUo.js +1 -0
- package/examples/basic/build/client/assets/_...slug_-CXKf6qnB.js +1 -0
- package/examples/basic/build/client/assets/_id_-BadAyQnb.js +1 -0
- package/examples/basic/build/client/assets/about-BOhoEcEO.js +1 -0
- package/examples/basic/build/client/assets/get-started-BIXpcjkT.js +9 -0
- package/examples/basic/build/client/assets/index-BmqcTaBB.js +1 -0
- package/examples/basic/build/client/assets/io-DEVjjaJj.js +1 -0
- package/examples/basic/build/client/assets/layout-DJegirdz.js +1 -0
- package/examples/basic/build/client/assets/react-DEQrz1q7.js +9 -0
- package/examples/basic/build/client/assets/rolldown-runtime-KL5VtC6j.js +1 -0
- package/examples/basic/build/client/assets/routes-BYWn6TxK.js +1 -0
- package/examples/basic/build/client/css/style.css +2 -0
- package/examples/basic/build/client/images/.gitkeep +1 -0
- package/examples/basic/build/client/images/logo.svg +37 -0
- package/examples/basic/build/client/index.html +17 -0
- package/examples/basic/build/client/robots.txt +2 -0
- package/examples/basic/client/404.tsx +2 -5
- package/examples/basic/client/components/.gitkeep +1 -0
- package/examples/basic/client/components/Footer.tsx +8 -0
- package/examples/basic/client/layout.tsx +39 -26
- package/examples/basic/client/public/images/.gitkeep +1 -0
- package/examples/basic/client/public/images/logo.svg +37 -0
- package/examples/basic/client/public/index.html +15 -0
- package/examples/basic/client/public/robots.txt +2 -0
- package/examples/basic/client/routes/about.tsx +1 -3
- package/examples/basic/client/routes/blog/[id].tsx +2 -4
- package/examples/basic/client/routes/docs/[...slug].tsx +3 -6
- package/examples/basic/client/routes/get-started.tsx +84 -0
- package/examples/basic/client/routes/index.tsx +74 -7
- package/examples/basic/client/routes/io.tsx +3 -7
- package/examples/basic/client/styles/main.css +461 -0
- package/examples/basic/client/toil.tsx +7 -0
- package/examples/basic/node_modules/.bin/toilinit +16 -0
- package/examples/basic/node_modules/.bin/toilinit.cmd +17 -0
- package/examples/basic/node_modules/.bin/toilinit.ps1 +28 -0
- package/examples/basic/node_modules/.bin/toilscript +16 -0
- package/examples/basic/node_modules/.bin/toilscript.cmd +17 -0
- package/examples/basic/node_modules/.bin/toilscript.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-as +16 -0
- package/examples/basic/node_modules/.bin/wasm-as.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-as.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-ctor-eval +16 -0
- package/examples/basic/node_modules/.bin/wasm-ctor-eval.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-ctor-eval.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-dis +16 -0
- package/examples/basic/node_modules/.bin/wasm-dis.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-dis.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-merge +16 -0
- package/examples/basic/node_modules/.bin/wasm-merge.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-merge.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-metadce +16 -0
- package/examples/basic/node_modules/.bin/wasm-metadce.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-metadce.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-opt +16 -0
- package/examples/basic/node_modules/.bin/wasm-opt.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-opt.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-reduce +16 -0
- package/examples/basic/node_modules/.bin/wasm-reduce.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-reduce.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm-shell +16 -0
- package/examples/basic/node_modules/.bin/wasm-shell.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm-shell.ps1 +28 -0
- package/examples/basic/node_modules/.bin/wasm2js +16 -0
- package/examples/basic/node_modules/.bin/wasm2js.cmd +17 -0
- package/examples/basic/node_modules/.bin/wasm2js.ps1 +28 -0
- package/examples/basic/node_modules/.package-lock.json +49 -1
- package/examples/basic/node_modules/.vite/deps/_metadata.json +9 -9
- package/examples/basic/node_modules/binaryen/LICENSE +201 -0
- package/examples/basic/node_modules/binaryen/README.md +1362 -0
- package/examples/basic/node_modules/binaryen/bin/package.json +3 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-as +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-ctor-eval +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-dis +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-merge +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-metadce +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-opt +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-reduce +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm-shell +0 -0
- package/examples/basic/node_modules/binaryen/bin/wasm2js +0 -0
- package/examples/basic/node_modules/binaryen/index.d.ts +2371 -0
- package/examples/basic/node_modules/binaryen/index.js +30552 -0
- package/examples/basic/node_modules/binaryen/package.json +50 -0
- package/examples/basic/node_modules/long/LICENSE +202 -0
- package/examples/basic/node_modules/long/README.md +286 -0
- package/examples/basic/node_modules/long/index.d.ts +2 -0
- package/examples/basic/node_modules/long/index.js +1581 -0
- package/examples/basic/node_modules/long/package.json +58 -0
- package/examples/basic/node_modules/long/types.d.ts +474 -0
- package/examples/basic/node_modules/long/umd/index.d.ts +3 -0
- package/examples/basic/node_modules/long/umd/index.js +1622 -0
- package/examples/basic/node_modules/long/umd/package.json +3 -0
- package/examples/basic/node_modules/long/umd/types.d.ts +474 -0
- package/examples/basic/node_modules/toilscript/LICENSE +201 -0
- package/examples/basic/node_modules/toilscript/NOTICE +94 -0
- package/examples/basic/node_modules/toilscript/README.md +66 -0
- package/examples/basic/node_modules/toilscript/bin/toilinit.js +468 -0
- package/examples/basic/node_modules/toilscript/bin/toilscript.js +35 -0
- package/examples/basic/node_modules/toilscript/dist/cli.d.ts +4 -0
- package/examples/basic/node_modules/toilscript/dist/cli.generated.d.ts +10027 -0
- package/examples/basic/node_modules/toilscript/dist/cli.js +24474 -0
- package/examples/basic/node_modules/toilscript/dist/cli.js.map +7 -0
- package/examples/basic/node_modules/toilscript/dist/importmap.json +9 -0
- package/examples/basic/node_modules/toilscript/dist/toilscript.d.ts +4 -0
- package/examples/basic/node_modules/toilscript/dist/toilscript.generated.d.ts +11242 -0
- package/examples/basic/node_modules/toilscript/dist/toilscript.js +337 -0
- package/examples/basic/node_modules/toilscript/dist/toilscript.js.map +7 -0
- package/examples/basic/node_modules/toilscript/dist/transform.cjs +1 -0
- package/examples/basic/node_modules/toilscript/dist/transform.d.ts +1 -0
- package/examples/basic/node_modules/toilscript/dist/transform.js +1 -0
- package/examples/basic/node_modules/toilscript/dist/web.js +22 -0
- package/examples/basic/node_modules/toilscript/lib/binaryen.d.ts +2 -0
- package/examples/basic/node_modules/toilscript/lib/binaryen.js +2 -0
- package/examples/basic/node_modules/toilscript/package.json +115 -0
- package/examples/basic/node_modules/toilscript/std/README.md +6 -0
- package/examples/basic/node_modules/toilscript/std/assembly/array.ts +550 -0
- package/examples/basic/node_modules/toilscript/std/assembly/arraybuffer.ts +77 -0
- package/examples/basic/node_modules/toilscript/std/assembly/atomics.ts +127 -0
- package/examples/basic/node_modules/toilscript/std/assembly/bindings/asyncify.ts +16 -0
- package/examples/basic/node_modules/toilscript/std/assembly/bindings/dom.ts +291 -0
- package/examples/basic/node_modules/toilscript/std/assembly/bindings/node.ts +6 -0
- package/examples/basic/node_modules/toilscript/std/assembly/bitflags.ts +53 -0
- package/examples/basic/node_modules/toilscript/std/assembly/builtins.ts +2650 -0
- package/examples/basic/node_modules/toilscript/std/assembly/byteslice.ts +177 -0
- package/examples/basic/node_modules/toilscript/std/assembly/compat.ts +2 -0
- package/examples/basic/node_modules/toilscript/std/assembly/console.ts +42 -0
- package/examples/basic/node_modules/toilscript/std/assembly/crypto.ts +9 -0
- package/examples/basic/node_modules/toilscript/std/assembly/dataview.ts +181 -0
- package/examples/basic/node_modules/toilscript/std/assembly/date.ts +375 -0
- package/examples/basic/node_modules/toilscript/std/assembly/diagnostics.ts +11 -0
- package/examples/basic/node_modules/toilscript/std/assembly/encoding.ts +151 -0
- package/examples/basic/node_modules/toilscript/std/assembly/endian.ts +45 -0
- package/examples/basic/node_modules/toilscript/std/assembly/error.ts +44 -0
- package/examples/basic/node_modules/toilscript/std/assembly/fixedarray.ts +173 -0
- package/examples/basic/node_modules/toilscript/std/assembly/fixedmap.ts +326 -0
- package/examples/basic/node_modules/toilscript/std/assembly/fixedset.ts +275 -0
- package/examples/basic/node_modules/toilscript/std/assembly/function.ts +42 -0
- package/examples/basic/node_modules/toilscript/std/assembly/index.d.ts +2892 -0
- package/examples/basic/node_modules/toilscript/std/assembly/iterator.ts +35 -0
- package/examples/basic/node_modules/toilscript/std/assembly/map.ts +269 -0
- package/examples/basic/node_modules/toilscript/std/assembly/math.ts +3289 -0
- package/examples/basic/node_modules/toilscript/std/assembly/memory.ts +123 -0
- package/examples/basic/node_modules/toilscript/std/assembly/number.ts +388 -0
- package/examples/basic/node_modules/toilscript/std/assembly/object.ts +36 -0
- package/examples/basic/node_modules/toilscript/std/assembly/performance.ts +9 -0
- package/examples/basic/node_modules/toilscript/std/assembly/pointer.ts +80 -0
- package/examples/basic/node_modules/toilscript/std/assembly/polyfills.ts +27 -0
- package/examples/basic/node_modules/toilscript/std/assembly/process.ts +50 -0
- package/examples/basic/node_modules/toilscript/std/assembly/reference.ts +48 -0
- package/examples/basic/node_modules/toilscript/std/assembly/regexp.ts +12 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/README.md +83 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/common.ts +81 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/index-incremental.ts +2 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/index-memory.ts +1 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/index-minimal.ts +2 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/index-stub.ts +1 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/index.d.ts +37 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/itcms.ts +419 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/memory-runtime.ts +94 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/rtrace.ts +15 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/stub.ts +133 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/tcms.ts +254 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt/tlsf.ts +592 -0
- package/examples/basic/node_modules/toilscript/std/assembly/rt.ts +90 -0
- package/examples/basic/node_modules/toilscript/std/assembly/set.ts +225 -0
- package/examples/basic/node_modules/toilscript/std/assembly/shared/feature.ts +68 -0
- package/examples/basic/node_modules/toilscript/std/assembly/shared/runtime.ts +13 -0
- package/examples/basic/node_modules/toilscript/std/assembly/shared/target.ts +11 -0
- package/examples/basic/node_modules/toilscript/std/assembly/shared/tsconfig.json +11 -0
- package/examples/basic/node_modules/toilscript/std/assembly/shared/typeinfo.ts +72 -0
- package/examples/basic/node_modules/toilscript/std/assembly/staticarray.ts +423 -0
- package/examples/basic/node_modules/toilscript/std/assembly/string.ts +850 -0
- package/examples/basic/node_modules/toilscript/std/assembly/symbol.ts +114 -0
- package/examples/basic/node_modules/toilscript/std/assembly/table.ts +16 -0
- package/examples/basic/node_modules/toilscript/std/assembly/toilscript.ts +16 -0
- package/examples/basic/node_modules/toilscript/std/assembly/tsconfig.json +6 -0
- package/examples/basic/node_modules/toilscript/std/assembly/typedarray.ts +1954 -0
- package/examples/basic/node_modules/toilscript/std/assembly/uri.ts +17 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/bytes.ts +107 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/casemap.ts +497 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/error.ts +58 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/hash.ts +117 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/math.ts +1922 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/memory.ts +290 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/number.ts +873 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/sort.ts +313 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/string.ts +1202 -0
- package/examples/basic/node_modules/toilscript/std/assembly/util/uri.ts +275 -0
- package/examples/basic/node_modules/toilscript/std/assembly/vector.ts +4 -0
- package/examples/basic/node_modules/toilscript/std/assembly.json +16 -0
- package/examples/basic/node_modules/toilscript/std/portable/index.d.ts +461 -0
- package/examples/basic/node_modules/toilscript/std/portable/index.js +416 -0
- package/examples/basic/node_modules/toilscript/std/portable.json +11 -0
- package/examples/basic/node_modules/toilscript/std/types/assembly/index.d.ts +1 -0
- package/examples/basic/node_modules/toilscript/std/types/assembly/package.json +3 -0
- package/examples/basic/node_modules/toilscript/std/types/portable/index.d.ts +1 -0
- package/examples/basic/node_modules/toilscript/std/types/portable/package.json +3 -0
- package/examples/basic/node_modules/toilscript/tsconfig-base.json +13 -0
- package/examples/basic/node_modules/toilscript/util/README.md +23 -0
- package/examples/basic/node_modules/toilscript/util/browser/fs.js +1 -0
- package/examples/basic/node_modules/toilscript/util/browser/module.js +5 -0
- package/examples/basic/node_modules/toilscript/util/browser/path.js +520 -0
- package/examples/basic/node_modules/toilscript/util/browser/process.js +59 -0
- package/examples/basic/node_modules/toilscript/util/browser/url.js +23 -0
- package/examples/basic/node_modules/toilscript/util/cpu.d.ts +9 -0
- package/examples/basic/node_modules/toilscript/util/cpu.js +42 -0
- package/examples/basic/node_modules/toilscript/util/find.d.ts +6 -0
- package/examples/basic/node_modules/toilscript/util/find.js +20 -0
- package/examples/basic/node_modules/toilscript/util/node.d.ts +21 -0
- package/examples/basic/node_modules/toilscript/util/node.js +34 -0
- package/examples/basic/node_modules/toilscript/util/options.d.ts +70 -0
- package/examples/basic/node_modules/toilscript/util/options.js +262 -0
- package/examples/basic/node_modules/toilscript/util/terminal.d.ts +52 -0
- package/examples/basic/node_modules/toilscript/util/terminal.js +35 -0
- package/examples/basic/node_modules/toilscript/util/text.d.ts +26 -0
- package/examples/basic/node_modules/toilscript/util/text.js +114 -0
- package/examples/basic/node_modules/toilscript/util/tsconfig.json +9 -0
- package/examples/basic/node_modules/toilscript/util/web.d.ts +11 -0
- package/examples/basic/node_modules/toilscript/util/web.js +33 -0
- package/examples/basic/package-lock.json +50 -1
- package/examples/basic/package.json +5 -2
- package/examples/basic/server/index.ts +3 -0
- package/examples/basic/server/main.ts +6 -0
- package/examples/basic/server/tsconfig.json +7 -0
- package/examples/basic/toil-env.d.ts +20 -1
- package/examples/basic/toil.config.ts +2 -5
- package/examples/basic/toilconfig.json +30 -0
- package/package.json +1 -1
- package/presets/eslint.js +2 -7
- package/presets/no-uint8array-tostring.js +4 -5
- package/presets/prettier.json +8 -1
- package/src/backend/index.ts +11 -18
- package/src/cli/configure.ts +272 -0
- package/src/cli/create.ts +227 -69
- package/src/cli/features.ts +128 -0
- package/src/cli/index.ts +44 -3
- package/src/cli/proc.ts +20 -0
- package/src/cli/ui.ts +4 -6
- package/src/cli/validate.ts +31 -0
- package/src/client/Link.tsx +99 -0
- package/src/client/NavLink.tsx +86 -0
- package/src/client/Router.tsx +95 -0
- package/src/client/error-boundary.tsx +43 -0
- package/src/client/head.ts +140 -0
- package/src/client/hooks.ts +115 -0
- package/src/client/index.ts +35 -5
- package/src/client/lazy.ts +93 -0
- package/src/client/match.ts +11 -3
- package/src/client/mount.tsx +28 -0
- package/src/client/navigation.ts +142 -0
- package/src/client/params-context.ts +10 -0
- package/src/client/prefetch.ts +130 -0
- package/src/client/scroll.ts +53 -0
- package/src/client/types.ts +36 -0
- package/src/compiler/config.ts +15 -8
- package/src/compiler/docs.ts +87 -0
- package/src/compiler/generate.ts +164 -23
- package/src/compiler/index.ts +6 -4
- package/src/compiler/plugin.ts +3 -1
- package/src/compiler/routes.ts +13 -7
- package/src/compiler/vite.ts +28 -5
- package/src/io/BinaryReader.ts +1 -5
- package/src/io/BinaryWriter.ts +3 -3
- package/src/server/index.ts +3 -4
- package/src/server/tsconfig.json +4 -0
- package/test/configure.test.ts +90 -0
- package/test/features.test.ts +111 -0
- package/test/head.test.ts +35 -0
- package/test/io.test.ts +8 -0
- package/test/navlink.test.ts +28 -0
- package/test/routes.test.ts +15 -0
- package/test/validate.test.ts +42 -0
- package/vitest.config.ts +1 -1
- package/examples/basic/dist/assets/404-D1bS2aH_.js +0 -1
- package/examples/basic/dist/assets/_...slug_-wR3shlWn.js +0 -1
- package/examples/basic/dist/assets/_id_-EWYvHfi2.js +0 -1
- package/examples/basic/dist/assets/about-Ddvj1tjF.js +0 -1
- package/examples/basic/dist/assets/index-CdG0me90.js +0 -1
- package/examples/basic/dist/assets/io-CODNJU57.js +0 -1
- package/examples/basic/dist/assets/layout-C15ZTPYI.js +0 -1
- package/examples/basic/dist/assets/react-JbAfoxYe.js +0 -9
- package/examples/basic/dist/assets/rolldown-runtime-1VNLd2iN.js +0 -1
- package/examples/basic/dist/assets/routes-GoydenoY.js +0 -1
- package/examples/basic/dist/index.html +0 -12
- package/src/client/runtime.tsx +0 -190
package/src/cli/index.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { build, dev, start } from 'toiljs/compiler';
|
|
8
8
|
|
|
9
|
+
import { runConfigure } from './configure.js';
|
|
9
10
|
import { runCreate, type Template } from './create.js';
|
|
11
|
+
import { PREPROCESSORS, type Preprocessor } from './features.js';
|
|
10
12
|
import { accent, banner, bold, danger, dim, success, version } from './ui.js';
|
|
11
13
|
|
|
12
14
|
interface Flags {
|
|
@@ -14,6 +16,9 @@ interface Flags {
|
|
|
14
16
|
port?: number;
|
|
15
17
|
name?: string;
|
|
16
18
|
template?: Template;
|
|
19
|
+
preprocessor?: Preprocessor;
|
|
20
|
+
tailwind?: boolean;
|
|
21
|
+
ai?: boolean;
|
|
17
22
|
install?: boolean;
|
|
18
23
|
git?: boolean;
|
|
19
24
|
pm?: string;
|
|
@@ -28,9 +33,11 @@ function parseArgs(argv: string[]): Flags {
|
|
|
28
33
|
case '--root':
|
|
29
34
|
flags.root = argv[++i];
|
|
30
35
|
break;
|
|
31
|
-
case '--port':
|
|
32
|
-
|
|
36
|
+
case '--port': {
|
|
37
|
+
const port = Number(argv[++i]);
|
|
38
|
+
if (!Number.isNaN(port)) flags.port = port;
|
|
33
39
|
break;
|
|
40
|
+
}
|
|
34
41
|
case '--template':
|
|
35
42
|
case '-t': {
|
|
36
43
|
const t = argv[++i];
|
|
@@ -40,6 +47,23 @@ function parseArgs(argv: string[]): Flags {
|
|
|
40
47
|
case '--pm':
|
|
41
48
|
flags.pm = argv[++i];
|
|
42
49
|
break;
|
|
50
|
+
case '--style': {
|
|
51
|
+
const s = argv[++i];
|
|
52
|
+
if ((PREPROCESSORS as readonly string[]).includes(s)) flags.preprocessor = s as Preprocessor;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case '--tailwind':
|
|
56
|
+
flags.tailwind = true;
|
|
57
|
+
break;
|
|
58
|
+
case '--no-tailwind':
|
|
59
|
+
flags.tailwind = false;
|
|
60
|
+
break;
|
|
61
|
+
case '--ai':
|
|
62
|
+
flags.ai = true;
|
|
63
|
+
break;
|
|
64
|
+
case '--no-ai':
|
|
65
|
+
flags.ai = false;
|
|
66
|
+
break;
|
|
43
67
|
case '--install':
|
|
44
68
|
flags.install = true;
|
|
45
69
|
break;
|
|
@@ -57,7 +81,6 @@ function parseArgs(argv: string[]): Flags {
|
|
|
57
81
|
flags.yes = true;
|
|
58
82
|
break;
|
|
59
83
|
default:
|
|
60
|
-
// First bare (non-flag) token is the positional project name.
|
|
61
84
|
if (!arg.startsWith('-') && flags.name === undefined) flags.name = arg;
|
|
62
85
|
}
|
|
63
86
|
}
|
|
@@ -72,6 +95,7 @@ function printHelp(): void {
|
|
|
72
95
|
'',
|
|
73
96
|
bold('Commands'),
|
|
74
97
|
cmd('create [name]', 'scaffold a new toiljs app'),
|
|
98
|
+
cmd('configure', 'toggle styling features (Sass/Less/Stylus, Tailwind)'),
|
|
75
99
|
cmd('dev', 'start the dev server with HMR'),
|
|
76
100
|
cmd('build', 'build the optimized production bundle'),
|
|
77
101
|
cmd('start', 'self-host the built app (hyper-express / uWS)'),
|
|
@@ -80,6 +104,9 @@ function printHelp(): void {
|
|
|
80
104
|
cmd('--root <dir>', 'project root (default: current directory)'),
|
|
81
105
|
cmd('--port <n>', 'dev server port'),
|
|
82
106
|
cmd('-t, --template', 'create: app | minimal'),
|
|
107
|
+
cmd('--style <name>', 'create/configure: css | sass | less | stylus'),
|
|
108
|
+
cmd('--tailwind', 'create/configure: enable Tailwind (--no-tailwind to remove)'),
|
|
109
|
+
cmd('--no-ai', 'create: skip AI assistant files (CLAUDE.md, etc.)'),
|
|
83
110
|
cmd('-y, --yes', 'create: accept defaults (non-interactive)'),
|
|
84
111
|
cmd('--no-install', "create: don't install dependencies"),
|
|
85
112
|
cmd('-v, --version', 'print the toiljs version'),
|
|
@@ -104,6 +131,9 @@ async function main(): Promise<void> {
|
|
|
104
131
|
await runCreate({
|
|
105
132
|
name: flags.name,
|
|
106
133
|
template: flags.template,
|
|
134
|
+
preprocessor: flags.preprocessor,
|
|
135
|
+
tailwind: flags.tailwind,
|
|
136
|
+
ai: flags.ai,
|
|
107
137
|
install: flags.install,
|
|
108
138
|
git: flags.git,
|
|
109
139
|
pm: flags.pm,
|
|
@@ -112,6 +142,17 @@ async function main(): Promise<void> {
|
|
|
112
142
|
});
|
|
113
143
|
break;
|
|
114
144
|
|
|
145
|
+
case 'configure':
|
|
146
|
+
banner();
|
|
147
|
+
await runConfigure({
|
|
148
|
+
root: flags.root,
|
|
149
|
+
preprocessor: flags.preprocessor,
|
|
150
|
+
tailwind: flags.tailwind,
|
|
151
|
+
install: flags.install,
|
|
152
|
+
cwd: process.cwd(),
|
|
153
|
+
});
|
|
154
|
+
break;
|
|
155
|
+
|
|
115
156
|
case 'dev':
|
|
116
157
|
banner();
|
|
117
158
|
process.stdout.write(dim(' starting dev server…') + '\n\n');
|
package/src/cli/proc.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Spawns `cmd args` in `cwd`, resolving on a 0 exit code and rejecting otherwise. On Windows the
|
|
5
|
+
* `npm`/`pnpm`/`yarn` shims are `.cmd` files that need a shell; passing an args array with
|
|
6
|
+
* `shell: true` is deprecated (DEP0190), so the whole command is passed as one string there
|
|
7
|
+
* (args are fixed/allowlisted, never raw user input). POSIX spawns directly.
|
|
8
|
+
*/
|
|
9
|
+
export function run(cmd: string, args: string[], cwd: string): Promise<void> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const onWindows = process.platform === 'win32';
|
|
12
|
+
const child = onWindows
|
|
13
|
+
? spawn([cmd, ...args].join(' '), { cwd, stdio: 'ignore', shell: true })
|
|
14
|
+
: spawn(cmd, args, { cwd, stdio: 'ignore' });
|
|
15
|
+
child.on('error', reject);
|
|
16
|
+
child.on('close', (code) =>
|
|
17
|
+
code === 0 ? resolve() : reject(new Error(`${cmd} exited with code ${String(code)}`)),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
}
|
package/src/cli/ui.ts
CHANGED
|
@@ -12,9 +12,9 @@ import pc from 'picocolors';
|
|
|
12
12
|
type RGB = readonly [number, number, number];
|
|
13
13
|
|
|
14
14
|
/** toiljs brand palette. */
|
|
15
|
-
const PRIMARY: RGB = [37, 99, 255];
|
|
16
|
-
const SECONDARY: RGB = [124, 58, 237];
|
|
17
|
-
const ACCENT: RGB = [34, 227, 171];
|
|
15
|
+
const PRIMARY: RGB = [37, 99, 255];
|
|
16
|
+
const SECONDARY: RGB = [124, 58, 237];
|
|
17
|
+
const ACCENT: RGB = [34, 227, 171];
|
|
18
18
|
|
|
19
19
|
/** Logo gradient stops: blue → purple → teal. */
|
|
20
20
|
const GRADIENT: readonly RGB[] = [PRIMARY, SECONDARY, ACCENT];
|
|
@@ -90,9 +90,7 @@ export function version(): string {
|
|
|
90
90
|
const raw = fs.readFileSync(pkgPath, 'utf8');
|
|
91
91
|
const match = /"version"\s*:\s*"([^"]+)"/.exec(raw);
|
|
92
92
|
if (match && match[1]) return match[1];
|
|
93
|
-
} catch {
|
|
94
|
-
/* fall through to default */
|
|
95
|
-
}
|
|
93
|
+
} catch {}
|
|
96
94
|
return '0.0.0';
|
|
97
95
|
}
|
|
98
96
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure input validation for `toiljs create` — kept dependency-light (only node:path) so it can be
|
|
3
|
+
* unit-tested without pulling in the rest of the CLI.
|
|
4
|
+
*/
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
/** Package managers the scaffolder may invoke. Allowlisted so a hostile `--pm` can't inject a shell command. */
|
|
8
|
+
export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
9
|
+
|
|
10
|
+
/** Validates a project name's characters. Returns `true`, or an error message. */
|
|
11
|
+
export function isValidName(name: string): true | string {
|
|
12
|
+
if (!name.trim()) return 'Please enter a project name.';
|
|
13
|
+
if (!/^[a-z0-9._@/-]+$/i.test(name)) return 'Use letters, numbers, dashes, dots or slashes.';
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolves `name` to an absolute directory under `cwd`, refusing to escape it (a name like
|
|
19
|
+
* `../x` or an absolute path). Returns the resolved dir, or `null` if it would escape `cwd`.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveProjectDir(cwd: string, name: string): string | null {
|
|
22
|
+
const target = path.resolve(cwd, name);
|
|
23
|
+
const rel = path.relative(cwd, target);
|
|
24
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) return null;
|
|
25
|
+
return target;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Whether `pm` is a supported package manager (guards shell use of `--pm`). */
|
|
29
|
+
export function isPackageManager(pm: string): boolean {
|
|
30
|
+
return PACKAGE_MANAGERS.includes(pm);
|
|
31
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ComponentPropsWithRef,
|
|
3
|
+
FocusEvent,
|
|
4
|
+
MouseEvent,
|
|
5
|
+
PointerEvent,
|
|
6
|
+
ReactNode,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
import { navigate } from './navigation.js';
|
|
10
|
+
import { prefetch } from './prefetch.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for {@link Link}: every standard `<a>` attribute (`rel`, `target`, `download`,
|
|
14
|
+
* `referrerPolicy`, `hrefLang`, `className`, `style`, `ref`, `data-*`, `aria-*`, event handlers …)
|
|
15
|
+
* plus toil's `replace` and `prefetch` controls. `href` is required.
|
|
16
|
+
*/
|
|
17
|
+
export interface LinkProps extends Omit<ComponentPropsWithRef<'a'>, 'href'> {
|
|
18
|
+
/** Destination. Same-origin hrefs navigate client-side; external / `target` / `download` / `#hash` use the browser. */
|
|
19
|
+
href: string;
|
|
20
|
+
/** Replace the current history entry instead of pushing a new one. Default `false`. */
|
|
21
|
+
replace?: boolean;
|
|
22
|
+
/** Scroll to top after navigating. Default `true`. */
|
|
23
|
+
scroll?: boolean;
|
|
24
|
+
/** Prefetch the route chunk on hover/focus. Default `true`; `false` opts this link out. */
|
|
25
|
+
prefetch?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** True for cross-origin, opaque (`mailto:` / `tel:`), or otherwise non-same-origin hrefs. */
|
|
29
|
+
function isExternalHref(href: string): boolean {
|
|
30
|
+
try {
|
|
31
|
+
return new URL(href, window.location.href).origin !== window.location.origin;
|
|
32
|
+
} catch {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Client-side navigation link. Forwards all anchor attributes to the underlying `<a>`, and
|
|
39
|
+
* prefetches the target route's chunk on hover/focus. Intercepts only plain same-origin clicks —
|
|
40
|
+
* modified clicks, `target=_blank`, `download`, in-page `#hash`, and external URLs fall through to
|
|
41
|
+
* native browser behavior.
|
|
42
|
+
*/
|
|
43
|
+
export function Link(props: LinkProps): ReactNode {
|
|
44
|
+
const {
|
|
45
|
+
href,
|
|
46
|
+
replace = false,
|
|
47
|
+
scroll = true,
|
|
48
|
+
prefetch: prefetchProp = true,
|
|
49
|
+
onClick,
|
|
50
|
+
onPointerEnter,
|
|
51
|
+
onFocus,
|
|
52
|
+
children,
|
|
53
|
+
...rest
|
|
54
|
+
} = props;
|
|
55
|
+
|
|
56
|
+
const handleClick = (event: MouseEvent<HTMLAnchorElement>): void => {
|
|
57
|
+
onClick?.(event);
|
|
58
|
+
if (
|
|
59
|
+
event.defaultPrevented ||
|
|
60
|
+
event.button !== 0 ||
|
|
61
|
+
event.metaKey ||
|
|
62
|
+
event.ctrlKey ||
|
|
63
|
+
event.shiftKey ||
|
|
64
|
+
event.altKey ||
|
|
65
|
+
(rest.target !== undefined && rest.target !== '_self') ||
|
|
66
|
+
rest.download !== undefined ||
|
|
67
|
+
href.startsWith('#') ||
|
|
68
|
+
isExternalHref(href)
|
|
69
|
+
) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
navigate(href, { replace, scroll });
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const warm = (): void => {
|
|
77
|
+
if (prefetchProp) prefetch(href);
|
|
78
|
+
};
|
|
79
|
+
const handlePointerEnter = (event: PointerEvent<HTMLAnchorElement>): void => {
|
|
80
|
+
onPointerEnter?.(event);
|
|
81
|
+
warm();
|
|
82
|
+
};
|
|
83
|
+
const handleFocus = (event: FocusEvent<HTMLAnchorElement>): void => {
|
|
84
|
+
onFocus?.(event);
|
|
85
|
+
warm();
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<a
|
|
90
|
+
{...rest}
|
|
91
|
+
{...(prefetchProp ? {} : { 'data-no-prefetch': '' })}
|
|
92
|
+
href={href}
|
|
93
|
+
onClick={handleClick}
|
|
94
|
+
onPointerEnter={handlePointerEnter}
|
|
95
|
+
onFocus={handleFocus}>
|
|
96
|
+
{children}
|
|
97
|
+
</a>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useLocation } from './hooks.js';
|
|
4
|
+
import { Link, type LinkProps } from './Link.js';
|
|
5
|
+
|
|
6
|
+
/** State passed to `NavLink`'s function-form `className` / `style` / `children`. */
|
|
7
|
+
export interface NavLinkState {
|
|
8
|
+
readonly isActive: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for {@link NavLink}: all {@link LinkProps}, but `className` / `style` / `children` may also
|
|
13
|
+
* be functions of the active state.
|
|
14
|
+
*/
|
|
15
|
+
export interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {
|
|
16
|
+
className?: string | ((state: NavLinkState) => string | undefined);
|
|
17
|
+
style?: CSSProperties | ((state: NavLinkState) => CSSProperties | undefined);
|
|
18
|
+
children?: ReactNode | ((state: NavLinkState) => ReactNode);
|
|
19
|
+
/** Match `href` exactly; without it, sub-paths are also active. Default `false`. */
|
|
20
|
+
end?: boolean;
|
|
21
|
+
/** Class added when active (used with a string `className`). Default `"active"`. */
|
|
22
|
+
activeClassName?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizePath(p: string): string {
|
|
26
|
+
return p.length > 1 ? p.replace(/\/+$/, '') : p;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Whether a link to `linkPath` is active for `currentPath`. Exact when `end`; otherwise a parent
|
|
31
|
+
* path is active for its sub-paths (and `/` is active everywhere, matching React Router).
|
|
32
|
+
*/
|
|
33
|
+
export function matchActive(linkPath: string, currentPath: string, end: boolean): boolean {
|
|
34
|
+
const link = normalizePath(linkPath);
|
|
35
|
+
const current = normalizePath(currentPath);
|
|
36
|
+
if (current === link) return true;
|
|
37
|
+
if (end) return false;
|
|
38
|
+
if (link === '/') return true;
|
|
39
|
+
return current.startsWith(link + '/');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A {@link Link} that knows whether it points at the current location. Applies an active class
|
|
44
|
+
* (default `"active"`) and `aria-current="page"` when active; `className` / `style` / `children`
|
|
45
|
+
* may be functions of `{ isActive }`. Inherits Link's full anchor API and prefetching.
|
|
46
|
+
*/
|
|
47
|
+
export function NavLink(props: NavLinkProps): ReactNode {
|
|
48
|
+
const {
|
|
49
|
+
href,
|
|
50
|
+
className,
|
|
51
|
+
style,
|
|
52
|
+
children,
|
|
53
|
+
end = false,
|
|
54
|
+
activeClassName = 'active',
|
|
55
|
+
...rest
|
|
56
|
+
} = props;
|
|
57
|
+
const pathname = useLocation();
|
|
58
|
+
|
|
59
|
+
let linkPath = href;
|
|
60
|
+
try {
|
|
61
|
+
linkPath = new URL(href, window.location.href).pathname;
|
|
62
|
+
} catch {
|
|
63
|
+
linkPath = href;
|
|
64
|
+
}
|
|
65
|
+
const isActive = matchActive(linkPath, pathname, end);
|
|
66
|
+
const state: NavLinkState = { isActive };
|
|
67
|
+
|
|
68
|
+
const resolvedClassName =
|
|
69
|
+
typeof className === 'function'
|
|
70
|
+
? className(state)
|
|
71
|
+
: [className, isActive ? activeClassName : undefined].filter(Boolean).join(' ') ||
|
|
72
|
+
undefined;
|
|
73
|
+
const resolvedStyle = typeof style === 'function' ? style(state) : style;
|
|
74
|
+
const resolvedChildren = typeof children === 'function' ? children(state) : children;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Link
|
|
78
|
+
{...rest}
|
|
79
|
+
href={href}
|
|
80
|
+
className={resolvedClassName}
|
|
81
|
+
style={resolvedStyle}
|
|
82
|
+
aria-current={isActive ? 'page' : undefined}>
|
|
83
|
+
{resolvedChildren}
|
|
84
|
+
</Link>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createElement, Suspense, useLayoutEffect, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ErrorBoundary } from './error-boundary.js';
|
|
4
|
+
import { useLocation } from './hooks.js';
|
|
5
|
+
import {
|
|
6
|
+
errorComponent,
|
|
7
|
+
loadingComponent,
|
|
8
|
+
nestedLayout,
|
|
9
|
+
pageComponent,
|
|
10
|
+
resolveLayout,
|
|
11
|
+
resolveNotFound,
|
|
12
|
+
} from './lazy.js';
|
|
13
|
+
import { matchRoute, type RouteParams } from './match.js';
|
|
14
|
+
import { ParamsContext } from './params-context.js';
|
|
15
|
+
import { settleNavigation } from './navigation.js';
|
|
16
|
+
import { applyScroll } from './scroll.js';
|
|
17
|
+
import type { LayoutLoader, NotFoundLoader, RouteDef } from './types.js';
|
|
18
|
+
|
|
19
|
+
/** Matches the current location to a route and renders it, optionally wrapped in the root layout. */
|
|
20
|
+
export function Router(props: {
|
|
21
|
+
routes: RouteDef[];
|
|
22
|
+
layout?: LayoutLoader;
|
|
23
|
+
notFound?: NotFoundLoader;
|
|
24
|
+
}): ReactNode {
|
|
25
|
+
const { routes, layout = null, notFound = null } = props;
|
|
26
|
+
const pathname = useLocation();
|
|
27
|
+
|
|
28
|
+
// After each navigation commits, apply the planned scroll (top / restore / #hash) and mark the
|
|
29
|
+
// navigation settled. A layout effect runs before paint, so the scroll lands without a flash.
|
|
30
|
+
useLayoutEffect(() => {
|
|
31
|
+
applyScroll();
|
|
32
|
+
settleNavigation();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let matched: RouteDef | undefined;
|
|
36
|
+
let params: RouteParams = {};
|
|
37
|
+
for (const route of routes) {
|
|
38
|
+
const result = matchRoute(route.pattern, pathname);
|
|
39
|
+
if (result) {
|
|
40
|
+
matched = route;
|
|
41
|
+
params = result;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let content: ReactNode;
|
|
47
|
+
if (matched) {
|
|
48
|
+
const Page = pageComponent(matched);
|
|
49
|
+
const fallback: ReactNode = matched.loading
|
|
50
|
+
? createElement(Suspense, { fallback: null }, createElement(loadingComponent(matched.loading)))
|
|
51
|
+
: null;
|
|
52
|
+
content = (
|
|
53
|
+
<Suspense fallback={fallback}>
|
|
54
|
+
<Page />
|
|
55
|
+
</Suspense>
|
|
56
|
+
);
|
|
57
|
+
// Wrap in nested layouts, deepest first so the shallowest ends up outermost.
|
|
58
|
+
const chain = matched.layouts ?? [];
|
|
59
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
60
|
+
const NestedLayout = nestedLayout(chain[i]);
|
|
61
|
+
content = (
|
|
62
|
+
<Suspense fallback={null}>
|
|
63
|
+
<NestedLayout>{content}</NestedLayout>
|
|
64
|
+
</Suspense>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (matched.errorComponent) {
|
|
68
|
+
content = (
|
|
69
|
+
<ErrorBoundary fallback={errorComponent(matched.errorComponent)}>
|
|
70
|
+
{content}
|
|
71
|
+
</ErrorBoundary>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
} else if (notFound) {
|
|
75
|
+
const NotFound = resolveNotFound(notFound);
|
|
76
|
+
content = (
|
|
77
|
+
<Suspense fallback={null}>
|
|
78
|
+
<NotFound />
|
|
79
|
+
</Suspense>
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
content = <div style={{ padding: 24, fontFamily: 'system-ui' }}>404 — Not found</div>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (layout) {
|
|
86
|
+
const Layout = resolveLayout(layout);
|
|
87
|
+
content = (
|
|
88
|
+
<Suspense fallback={null}>
|
|
89
|
+
<Layout>{content}</Layout>
|
|
90
|
+
</Suspense>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return <ParamsContext.Provider value={params}>{content}</ParamsContext.Provider>;
|
|
95
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Component, Suspense, type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { RouteErrorProps } from './types.js';
|
|
4
|
+
|
|
5
|
+
interface ErrorBoundaryProps {
|
|
6
|
+
readonly fallback: ComponentType<RouteErrorProps>;
|
|
7
|
+
readonly children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
interface ErrorBoundaryState {
|
|
10
|
+
readonly error: Error | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Catches render errors in its subtree and shows the route's `error.tsx` (with a `reset` to retry).
|
|
15
|
+
* Error boundaries must be class components — React has no hook equivalent.
|
|
16
|
+
*/
|
|
17
|
+
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
18
|
+
state: ErrorBoundaryState = { error: null };
|
|
19
|
+
|
|
20
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
21
|
+
return { error };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
reset = (): void => {
|
|
25
|
+
this.setState({ error: null });
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
render(): ReactNode {
|
|
29
|
+
const { error } = this.state;
|
|
30
|
+
if (error) {
|
|
31
|
+
const Fallback = this.props.fallback;
|
|
32
|
+
return (
|
|
33
|
+
<Suspense fallback={null}>
|
|
34
|
+
<Fallback
|
|
35
|
+
error={error}
|
|
36
|
+
reset={this.reset}
|
|
37
|
+
/>
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return this.props.children;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side document `<head>` management. `useHead` / `useTitle` / `<Head>` let any component
|
|
3
|
+
* (layout or page) set the title and `<meta>` / `<link>` tags; entries compose across the tree
|
|
4
|
+
* (later/deeper entries win per key) and are reverted when the component unmounts. Pure
|
|
5
|
+
* `mergeHead` resolves the active entries; the manager reconciles `document.head`.
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
/** A `<meta>` tag. Use `name` or `property` (OpenGraph) as the dedup key; extra attrs pass through. */
|
|
10
|
+
export interface MetaTag {
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
readonly property?: string;
|
|
13
|
+
readonly content: string;
|
|
14
|
+
readonly [attr: string]: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A `<link>` tag (deduped by `rel` + `href`); extra attrs pass through. */
|
|
18
|
+
export interface LinkTag {
|
|
19
|
+
readonly rel: string;
|
|
20
|
+
readonly href: string;
|
|
21
|
+
readonly [attr: string]: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A head contribution from one component. */
|
|
25
|
+
export interface HeadSpec {
|
|
26
|
+
/** Document title. */
|
|
27
|
+
readonly title?: string;
|
|
28
|
+
/** Template applied to a child's title, `%s` = the title (e.g. `'%s · toiljs'`). */
|
|
29
|
+
readonly titleTemplate?: string;
|
|
30
|
+
readonly meta?: readonly MetaTag[];
|
|
31
|
+
readonly link?: readonly LinkTag[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The resolved head after merging all active specs. */
|
|
35
|
+
export interface ResolvedHead {
|
|
36
|
+
readonly title?: string;
|
|
37
|
+
readonly meta: MetaTag[];
|
|
38
|
+
readonly link: LinkTag[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function metaKey(m: MetaTag): string {
|
|
42
|
+
if (m.name !== undefined) return `name:${m.name}`;
|
|
43
|
+
if (m.property !== undefined) return `property:${m.property}`;
|
|
44
|
+
return `meta:${JSON.stringify(m)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Merges head specs in order: the last `title`/`titleTemplate` wins, `meta` dedupes by name/property
|
|
49
|
+
* and `link` by rel+href (last wins). A `titleTemplate` formats the resolved title via `%s`.
|
|
50
|
+
*/
|
|
51
|
+
export function mergeHead(specs: readonly HeadSpec[]): ResolvedHead {
|
|
52
|
+
let title: string | undefined;
|
|
53
|
+
let titleTemplate: string | undefined;
|
|
54
|
+
const meta = new Map<string, MetaTag>();
|
|
55
|
+
const link = new Map<string, LinkTag>();
|
|
56
|
+
for (const spec of specs) {
|
|
57
|
+
if (spec.title !== undefined) title = spec.title;
|
|
58
|
+
if (spec.titleTemplate !== undefined) titleTemplate = spec.titleTemplate;
|
|
59
|
+
for (const m of spec.meta ?? []) meta.set(metaKey(m), m);
|
|
60
|
+
for (const l of spec.link ?? []) link.set(`${l.rel}:${l.href}`, l);
|
|
61
|
+
}
|
|
62
|
+
const resolvedTitle =
|
|
63
|
+
title !== undefined && titleTemplate !== undefined
|
|
64
|
+
? titleTemplate.replace('%s', title)
|
|
65
|
+
: title;
|
|
66
|
+
return { title: resolvedTitle, meta: [...meta.values()], link: [...link.values()] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const entries = new Map<number, HeadSpec>();
|
|
70
|
+
let order: number[] = [];
|
|
71
|
+
let seq = 0;
|
|
72
|
+
let baseTitle: string | null = null;
|
|
73
|
+
|
|
74
|
+
function setAttrs(el: Element, attrs: Record<string, string | undefined>): void {
|
|
75
|
+
el.setAttribute('data-toil-head', '');
|
|
76
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
77
|
+
if (value !== undefined) el.setAttribute(key, value);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Reconciles `document.head` with the merged active specs. */
|
|
82
|
+
function apply(): void {
|
|
83
|
+
if (typeof document === 'undefined') return;
|
|
84
|
+
if (baseTitle === null) baseTitle = document.title;
|
|
85
|
+
|
|
86
|
+
const resolved = mergeHead(order.map((id) => entries.get(id)).filter((s): s is HeadSpec => !!s));
|
|
87
|
+
|
|
88
|
+
document.title = resolved.title ?? baseTitle;
|
|
89
|
+
|
|
90
|
+
for (const stale of document.head.querySelectorAll('[data-toil-head]')) stale.remove();
|
|
91
|
+
for (const m of resolved.meta) {
|
|
92
|
+
const el = document.createElement('meta');
|
|
93
|
+
setAttrs(el, m);
|
|
94
|
+
document.head.appendChild(el);
|
|
95
|
+
}
|
|
96
|
+
for (const l of resolved.link) {
|
|
97
|
+
const el = document.createElement('link');
|
|
98
|
+
setAttrs(el, l);
|
|
99
|
+
document.head.appendChild(el);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function addHead(spec: HeadSpec): number {
|
|
104
|
+
const id = ++seq;
|
|
105
|
+
entries.set(id, spec);
|
|
106
|
+
order.push(id);
|
|
107
|
+
apply();
|
|
108
|
+
return id;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function removeHead(id: number): void {
|
|
112
|
+
entries.delete(id);
|
|
113
|
+
order = order.filter((x) => x !== id);
|
|
114
|
+
apply();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Applies a head contribution for the lifetime of the calling component: title, `<meta>`, `<link>`.
|
|
119
|
+
* Reverts on unmount. Compose freely — a root layout can set defaults a page overrides.
|
|
120
|
+
*/
|
|
121
|
+
export function useHead(spec: HeadSpec): void {
|
|
122
|
+
const json = JSON.stringify(spec);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
const id = addHead(JSON.parse(json) as HeadSpec);
|
|
125
|
+
return () => {
|
|
126
|
+
removeHead(id);
|
|
127
|
+
};
|
|
128
|
+
}, [json]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Sets `document.title` for the calling component's lifetime. */
|
|
132
|
+
export function useTitle(title: string): void {
|
|
133
|
+
useHead({ title });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Declarative form of {@link useHead}: `<Head title="…" meta={[…]} />`. Renders nothing. */
|
|
137
|
+
export function Head(props: HeadSpec): null {
|
|
138
|
+
useHead(props);
|
|
139
|
+
return null;
|
|
140
|
+
}
|