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.
- 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 +169 -56
- 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.d.ts +1 -0
- package/build/compiler/generate.js +139 -21
- package/build/compiler/index.d.ts +5 -2
- package/build/compiler/index.js +5 -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 +228 -67
- 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 +180 -33
- package/src/compiler/index.ts +7 -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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router hooks for user route components: read the params / pathname / search params, navigate
|
|
3
|
+
* imperatively, and grab a router handle.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
startTransition,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useReducer,
|
|
11
|
+
useSyncExternalStore,
|
|
12
|
+
} from 'react';
|
|
13
|
+
|
|
14
|
+
import type { RouteParams } from './match.js';
|
|
15
|
+
import {
|
|
16
|
+
back,
|
|
17
|
+
forward,
|
|
18
|
+
isNavigationPending,
|
|
19
|
+
navigate,
|
|
20
|
+
refresh,
|
|
21
|
+
subscribeLocation,
|
|
22
|
+
subscribePending,
|
|
23
|
+
type NavigateOptions,
|
|
24
|
+
} from './navigation.js';
|
|
25
|
+
import { ParamsContext } from './params-context.js';
|
|
26
|
+
import { prefetch } from './prefetch.js';
|
|
27
|
+
|
|
28
|
+
/** Imperative router handle returned by {@link useRouter}. */
|
|
29
|
+
export interface RouterInstance {
|
|
30
|
+
/** Navigate to `href`, pushing a new history entry (or replacing with `{ replace: true }`). */
|
|
31
|
+
push(href: string, options?: NavigateOptions): void;
|
|
32
|
+
/** Navigate to `href`, replacing the current history entry. */
|
|
33
|
+
replace(href: string): void;
|
|
34
|
+
/** Go back one history entry. */
|
|
35
|
+
back(): void;
|
|
36
|
+
/** Go forward one history entry. */
|
|
37
|
+
forward(): void;
|
|
38
|
+
/** Re-render the current route. */
|
|
39
|
+
refresh(): void;
|
|
40
|
+
/** Prefetch a route's chunk ahead of navigation. */
|
|
41
|
+
prefetch(href: string): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ROUTER: RouterInstance = {
|
|
45
|
+
push: (href, options) => {
|
|
46
|
+
navigate(href, options);
|
|
47
|
+
},
|
|
48
|
+
replace: (href) => {
|
|
49
|
+
navigate(href, { replace: true });
|
|
50
|
+
},
|
|
51
|
+
back,
|
|
52
|
+
forward,
|
|
53
|
+
refresh,
|
|
54
|
+
prefetch,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Current dynamic route params, e.g. `{ id }` inside `/blog/:id`. */
|
|
58
|
+
export function useParams(): RouteParams {
|
|
59
|
+
return useContext(ParamsContext);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Returns the imperative `navigate(href, { replace })` function. */
|
|
63
|
+
export function useNavigate(): typeof navigate {
|
|
64
|
+
return navigate;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Returns the router handle (`push` / `replace` / `back` / `forward` / `refresh` / `prefetch`). */
|
|
68
|
+
export function useRouter(): RouterInstance {
|
|
69
|
+
return ROUTER;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Subscribes to location changes (in a transition, so the current page stays on screen while the
|
|
74
|
+
* next chunk loads) and reads the live `window.location` on render. Re-renders on any pathname,
|
|
75
|
+
* search, or hash change.
|
|
76
|
+
*/
|
|
77
|
+
function useLocationSubscription(): void {
|
|
78
|
+
const [, forceUpdate] = useReducer((n: number): number => n + 1, 0);
|
|
79
|
+
useEffect(
|
|
80
|
+
() =>
|
|
81
|
+
subscribeLocation(() => {
|
|
82
|
+
startTransition(() => {
|
|
83
|
+
forceUpdate();
|
|
84
|
+
});
|
|
85
|
+
}),
|
|
86
|
+
[],
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Subscribes to and returns the current `location.pathname`. */
|
|
91
|
+
export function useLocation(): string {
|
|
92
|
+
useLocationSubscription();
|
|
93
|
+
return window.location.pathname;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Alias of {@link useLocation}: the current `location.pathname`. */
|
|
97
|
+
export function usePathname(): string {
|
|
98
|
+
return useLocation();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The current query string as a `URLSearchParams`, re-read on every navigation. */
|
|
102
|
+
export function useSearchParams(): URLSearchParams {
|
|
103
|
+
useLocationSubscription();
|
|
104
|
+
const search = window.location.search;
|
|
105
|
+
return useMemo(() => new URLSearchParams(search), [search]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** True while a navigation is in flight (started but not yet committed) — e.g. for a loading bar. */
|
|
109
|
+
export function useNavigationPending(): boolean {
|
|
110
|
+
return useSyncExternalStore(
|
|
111
|
+
subscribePending,
|
|
112
|
+
isNavigationPending,
|
|
113
|
+
() => false,
|
|
114
|
+
);
|
|
115
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* toiljs client runtime, published as `toiljs/client`.
|
|
3
|
-
* navigation hooks, and route types consumed by the compiler-generated entry.
|
|
4
|
-
* needed in user route files beyond this package.
|
|
2
|
+
* toiljs client runtime, published as `toiljs/client`. Re-exports the router (mount/Router/Link),
|
|
3
|
+
* navigation hooks, prefetching, and the route types consumed by the compiler-generated entry.
|
|
4
|
+
* Zero imports needed in user route files beyond this package.
|
|
5
|
+
*
|
|
6
|
+
* Internals are split by concern: route `types`, history-based `navigation`, the params
|
|
7
|
+
* `params-context` + `hooks`, `lazy` component resolution, the `Link`/`Router` components,
|
|
8
|
+
* `mount`, `match` (pure matcher), `prefetch` (link prefetcher), and `channel` (WebSocket helper).
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
export { mount
|
|
8
|
-
export
|
|
11
|
+
export { mount } from './mount.js';
|
|
12
|
+
export { Router } from './Router.js';
|
|
13
|
+
export { Link } from './Link.js';
|
|
14
|
+
export type { LinkProps } from './Link.js';
|
|
15
|
+
export { NavLink, matchActive } from './NavLink.js';
|
|
16
|
+
export type { NavLinkProps, NavLinkState } from './NavLink.js';
|
|
17
|
+
export { navigate, back, forward, refresh } from './navigation.js';
|
|
18
|
+
export type { NavigateOptions } from './navigation.js';
|
|
19
|
+
export {
|
|
20
|
+
useParams,
|
|
21
|
+
useNavigate,
|
|
22
|
+
useLocation,
|
|
23
|
+
usePathname,
|
|
24
|
+
useSearchParams,
|
|
25
|
+
useRouter,
|
|
26
|
+
useNavigationPending,
|
|
27
|
+
} from './hooks.js';
|
|
28
|
+
export type { RouterInstance } from './hooks.js';
|
|
29
|
+
export { prefetch } from './prefetch.js';
|
|
30
|
+
export type {
|
|
31
|
+
RouteDef,
|
|
32
|
+
LayoutLoader,
|
|
33
|
+
LayoutComponentLoader,
|
|
34
|
+
NotFoundLoader,
|
|
35
|
+
RouteErrorProps,
|
|
36
|
+
} from './types.js';
|
|
9
37
|
export { matchRoute } from './match.js';
|
|
10
38
|
export type { RouteParams } from './match.js';
|
|
11
39
|
export { connectChannel, useChannel, resolveChannelUrl } from './channel.js';
|
|
12
40
|
export type { Channel, ChannelOptions, ChannelHook, ChannelData } from './channel.js';
|
|
41
|
+
export { useHead, useTitle, Head, mergeHead } from './head.js';
|
|
42
|
+
export type { HeadSpec, MetaTag, LinkTag, ResolvedHead } from './head.js';
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy-component resolution and caching. Each page/layout/not-found loader is wrapped in
|
|
3
|
+
* `React.lazy` exactly once and memoized, so re-renders reuse the same component (and React's
|
|
4
|
+
* Suspense cache) instead of re-creating it. Keyed by loader identity.
|
|
5
|
+
*/
|
|
6
|
+
import { lazy, type ComponentType, type ReactNode } from 'react';
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
LayoutComponentLoader,
|
|
10
|
+
LayoutLoader,
|
|
11
|
+
NotFoundLoader,
|
|
12
|
+
RouteDef,
|
|
13
|
+
RouteErrorProps,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
|
|
16
|
+
type Loader<P> = () => Promise<{ default: ComponentType<P> }>;
|
|
17
|
+
|
|
18
|
+
/** Memoizes `lazy()` per loader identity in `cache`. */
|
|
19
|
+
function memoLazy<P>(
|
|
20
|
+
cache: Map<Loader<P>, ComponentType<P>>,
|
|
21
|
+
loader: Loader<P>,
|
|
22
|
+
): ComponentType<P> {
|
|
23
|
+
let component = cache.get(loader);
|
|
24
|
+
if (!component) {
|
|
25
|
+
component = lazy(loader);
|
|
26
|
+
cache.set(loader, component);
|
|
27
|
+
}
|
|
28
|
+
return component;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const loadingCache = new Map<Loader<object>, ComponentType<object>>();
|
|
32
|
+
/** Memoized lazy component for a route's `loading.tsx`. */
|
|
33
|
+
export function loadingComponent(loader: Loader<object>): ComponentType<object> {
|
|
34
|
+
return memoLazy(loadingCache, loader);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const errorCache = new Map<Loader<RouteErrorProps>, ComponentType<RouteErrorProps>>();
|
|
38
|
+
/** Memoized lazy component for a route's `error.tsx`. */
|
|
39
|
+
export function errorComponent(loader: Loader<RouteErrorProps>): ComponentType<RouteErrorProps> {
|
|
40
|
+
return memoLazy(errorCache, loader);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pageCache = new Map<RouteDef, ComponentType>();
|
|
44
|
+
|
|
45
|
+
/** Returns the memoized lazy component for `route`, creating it on first use. */
|
|
46
|
+
export function pageComponent(route: RouteDef): ComponentType {
|
|
47
|
+
let component = pageCache.get(route);
|
|
48
|
+
if (!component) {
|
|
49
|
+
component = lazy(route.load);
|
|
50
|
+
pageCache.set(route, component);
|
|
51
|
+
}
|
|
52
|
+
return component;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let layoutComponent: ComponentType<{ children?: ReactNode }> | null = null;
|
|
56
|
+
let layoutLoader: LayoutLoader = null;
|
|
57
|
+
|
|
58
|
+
/** Returns the memoized lazy root-layout component, rebuilding only if the loader identity changes. */
|
|
59
|
+
export function resolveLayout(
|
|
60
|
+
loader: NonNullable<LayoutLoader>,
|
|
61
|
+
): ComponentType<{ children?: ReactNode }> {
|
|
62
|
+
if (layoutLoader !== loader || !layoutComponent) {
|
|
63
|
+
layoutComponent = lazy(loader);
|
|
64
|
+
layoutLoader = loader;
|
|
65
|
+
}
|
|
66
|
+
return layoutComponent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const nestedLayoutCache = new Map<LayoutComponentLoader, ComponentType<{ children?: ReactNode }>>();
|
|
70
|
+
|
|
71
|
+
/** Returns the memoized lazy component for a nested layout loader, keyed by loader identity. */
|
|
72
|
+
export function nestedLayout(
|
|
73
|
+
loader: LayoutComponentLoader,
|
|
74
|
+
): ComponentType<{ children?: ReactNode }> {
|
|
75
|
+
let component = nestedLayoutCache.get(loader);
|
|
76
|
+
if (!component) {
|
|
77
|
+
component = lazy(loader);
|
|
78
|
+
nestedLayoutCache.set(loader, component);
|
|
79
|
+
}
|
|
80
|
+
return component;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let notFoundComponent: ComponentType | null = null;
|
|
84
|
+
let notFoundLoader: NotFoundLoader = null;
|
|
85
|
+
|
|
86
|
+
/** Returns the memoized lazy not-found component, rebuilding only if the loader identity changes. */
|
|
87
|
+
export function resolveNotFound(loader: NonNullable<NotFoundLoader>): ComponentType {
|
|
88
|
+
if (notFoundLoader !== loader || !notFoundComponent) {
|
|
89
|
+
notFoundComponent = lazy(loader);
|
|
90
|
+
notFoundLoader = loader;
|
|
91
|
+
}
|
|
92
|
+
return notFoundComponent;
|
|
93
|
+
}
|
package/src/client/match.ts
CHANGED
|
@@ -6,7 +6,8 @@ export type RouteParams = Record<string, string>;
|
|
|
6
6
|
* Pure and runtime-agnostic (used by the router and unit-tested directly).
|
|
7
7
|
* matchRoute('/', '/') -> {}
|
|
8
8
|
* matchRoute('/blog/:id', '/blog/42') -> { id: '42' }
|
|
9
|
-
* matchRoute('/docs/*slug', '/docs/a/b') -> { slug: 'a/b' } (catch-all)
|
|
9
|
+
* matchRoute('/docs/*slug', '/docs/a/b') -> { slug: 'a/b' } (catch-all, 1+ segments)
|
|
10
|
+
* matchRoute('/docs/**slug', '/docs') -> { slug: '' } (optional catch-all, 0+ segments)
|
|
10
11
|
* matchRoute('/about', '/x') -> null
|
|
11
12
|
*/
|
|
12
13
|
export function matchRoute(pattern: string, pathname: string): RouteParams | null {
|
|
@@ -17,7 +18,15 @@ export function matchRoute(pattern: string, pathname: string): RouteParams | nul
|
|
|
17
18
|
for (let i = 0; i < patternSegs.length; i++) {
|
|
18
19
|
const p = patternSegs[i];
|
|
19
20
|
|
|
20
|
-
//
|
|
21
|
+
// Optional catch-all (`**slug`): captures the rest of the path, matching zero or more segments.
|
|
22
|
+
if (p.startsWith('**')) {
|
|
23
|
+
params[p.slice(2)] = pathSegs
|
|
24
|
+
.slice(i)
|
|
25
|
+
.map((s) => decodeURIComponent(s))
|
|
26
|
+
.join('/');
|
|
27
|
+
return params;
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
if (p.startsWith('*')) {
|
|
22
31
|
const rest = pathSegs.slice(i);
|
|
23
32
|
if (rest.length === 0) return null;
|
|
@@ -34,6 +43,5 @@ export function matchRoute(pattern: string, pathname: string): RouteParams | nul
|
|
|
34
43
|
}
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
// No catch-all consumed the tail: lengths must match exactly.
|
|
38
46
|
return patternSegs.length === pathSegs.length ? params : null;
|
|
39
47
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRoot } from 'react-dom/client';
|
|
2
|
+
|
|
3
|
+
import { initNavigation } from './navigation.js';
|
|
4
|
+
import { startPrefetcher } from './prefetch.js';
|
|
5
|
+
import { Router } from './Router.js';
|
|
6
|
+
import type { LayoutLoader, NotFoundLoader, RouteDef } from './types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mounts the toil client app into `#root` and starts idle link prefetching. Called by the
|
|
10
|
+
* compiler-generated `.toil/entry.tsx`.
|
|
11
|
+
*/
|
|
12
|
+
export function mount(
|
|
13
|
+
routes: RouteDef[],
|
|
14
|
+
layout: LayoutLoader = null,
|
|
15
|
+
notFound: NotFoundLoader = null,
|
|
16
|
+
): void {
|
|
17
|
+
const el = document.getElementById('root');
|
|
18
|
+
if (!el) throw new Error('toil: #root element not found');
|
|
19
|
+
initNavigation();
|
|
20
|
+
createRoot(el).render(
|
|
21
|
+
<Router
|
|
22
|
+
routes={routes}
|
|
23
|
+
layout={layout}
|
|
24
|
+
notFound={notFound}
|
|
25
|
+
/>,
|
|
26
|
+
);
|
|
27
|
+
startPrefetcher(routes);
|
|
28
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History-based navigation core. Owns the location subscribers, the single `popstate` handler, and
|
|
3
|
+
* the per-entry history keys used for scroll restoration. Consumed by `useLocation` (to re-render),
|
|
4
|
+
* `Link` / `navigate` (to change location), and `Router` (which calls `applyScroll` after commit).
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
enableManualScrollRestoration,
|
|
8
|
+
planScroll,
|
|
9
|
+
rememberScroll,
|
|
10
|
+
} from './scroll.js';
|
|
11
|
+
|
|
12
|
+
const listeners = new Set<() => void>();
|
|
13
|
+
let popstateBound = false;
|
|
14
|
+
|
|
15
|
+
interface ToilHistoryState {
|
|
16
|
+
__toilKey?: string;
|
|
17
|
+
}
|
|
18
|
+
let keyCounter = 0;
|
|
19
|
+
let currentKey = 'initial';
|
|
20
|
+
function nextKey(): string {
|
|
21
|
+
keyCounter += 1;
|
|
22
|
+
return `t${String(keyCounter)}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Notifies every subscriber that the location may have changed. */
|
|
26
|
+
function notify(): void {
|
|
27
|
+
for (const listener of listeners) listener();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Navigation-pending tracking: a navigation is "pending" from when it starts until the new route
|
|
31
|
+
// commits. Drives useNavigationPending() (e.g. a top loading bar).
|
|
32
|
+
let startedTick = 0;
|
|
33
|
+
let committedTick = 0;
|
|
34
|
+
const pendingListeners = new Set<() => void>();
|
|
35
|
+
function emitPending(): void {
|
|
36
|
+
for (const listener of pendingListeners) listener();
|
|
37
|
+
}
|
|
38
|
+
function beginNavigation(): void {
|
|
39
|
+
startedTick += 1;
|
|
40
|
+
emitPending();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Marks the in-flight navigation as committed. Called by `Router` after each commit. */
|
|
44
|
+
export function settleNavigation(): void {
|
|
45
|
+
if (committedTick !== startedTick) {
|
|
46
|
+
committedTick = startedTick;
|
|
47
|
+
emitPending();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Whether a navigation is in flight (started but not yet committed). */
|
|
52
|
+
export function isNavigationPending(): boolean {
|
|
53
|
+
return startedTick !== committedTick;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Subscribes to navigation-pending changes; returns an unsubscribe function. */
|
|
57
|
+
export function subscribePending(listener: () => void): () => void {
|
|
58
|
+
pendingListeners.add(listener);
|
|
59
|
+
return () => {
|
|
60
|
+
pendingListeners.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Options for {@link navigate}. */
|
|
65
|
+
export interface NavigateOptions {
|
|
66
|
+
/** Replace the current history entry instead of pushing a new one. Default `false`. */
|
|
67
|
+
readonly replace?: boolean;
|
|
68
|
+
/** Scroll to the top of the page after navigating. Default `true`. */
|
|
69
|
+
readonly scroll?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Initializes manual scroll restoration and the initial history key. Called once by `mount`. */
|
|
73
|
+
export function initNavigation(): void {
|
|
74
|
+
enableManualScrollRestoration();
|
|
75
|
+
const state = window.history.state as ToilHistoryState | null;
|
|
76
|
+
if (state?.__toilKey) {
|
|
77
|
+
currentKey = state.__toilKey;
|
|
78
|
+
} else {
|
|
79
|
+
currentKey = nextKey();
|
|
80
|
+
window.history.replaceState({ ...state, __toilKey: currentKey }, '');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Navigates to `href` without a full page reload (history push/replace + subscriber re-render). */
|
|
85
|
+
export function navigate(href: string, options?: NavigateOptions): void {
|
|
86
|
+
beginNavigation();
|
|
87
|
+
rememberScroll(currentKey);
|
|
88
|
+
let hash = '';
|
|
89
|
+
try {
|
|
90
|
+
hash = new URL(href, window.location.href).hash;
|
|
91
|
+
} catch {
|
|
92
|
+
hash = '';
|
|
93
|
+
}
|
|
94
|
+
if (options?.replace) {
|
|
95
|
+
window.history.replaceState({ __toilKey: currentKey }, '', href);
|
|
96
|
+
} else {
|
|
97
|
+
currentKey = nextKey();
|
|
98
|
+
window.history.pushState({ __toilKey: currentKey }, '', href);
|
|
99
|
+
}
|
|
100
|
+
planScroll({ hash, toTop: options?.scroll !== false });
|
|
101
|
+
notify();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Goes back one entry in history (fires `popstate`, which notifies subscribers). */
|
|
105
|
+
export function back(): void {
|
|
106
|
+
window.history.back();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Goes forward one entry in history. */
|
|
110
|
+
export function forward(): void {
|
|
111
|
+
window.history.forward();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Re-renders the current route without changing the URL (there is no server data to refetch). */
|
|
115
|
+
export function refresh(): void {
|
|
116
|
+
notify();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Handles browser back/forward: restores the saved scroll for the target entry, then re-renders. */
|
|
120
|
+
function handlePopState(event: PopStateEvent): void {
|
|
121
|
+
beginNavigation();
|
|
122
|
+
rememberScroll(currentKey);
|
|
123
|
+
const state = event.state as ToilHistoryState | null;
|
|
124
|
+
currentKey = state?.__toilKey ?? 'initial';
|
|
125
|
+
planScroll({ restoreKey: currentKey, hash: window.location.hash, toTop: false });
|
|
126
|
+
notify();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Subscribes `listener` to location changes and returns an unsubscribe function. Browser
|
|
131
|
+
* back/forward is wired once, on the first subscription, via a shared `popstate` handler.
|
|
132
|
+
*/
|
|
133
|
+
export function subscribeLocation(listener: () => void): () => void {
|
|
134
|
+
if (!popstateBound) {
|
|
135
|
+
window.addEventListener('popstate', handlePopState);
|
|
136
|
+
popstateBound = true;
|
|
137
|
+
}
|
|
138
|
+
listeners.add(listener);
|
|
139
|
+
return () => {
|
|
140
|
+
listeners.delete(listener);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React context carrying the current route's dynamic params. The provider is set by {@link Router};
|
|
3
|
+
* read it with the {@link useParams} hook rather than consuming the context directly.
|
|
4
|
+
*/
|
|
5
|
+
import { createContext } from 'react';
|
|
6
|
+
|
|
7
|
+
import type { RouteParams } from './match.js';
|
|
8
|
+
|
|
9
|
+
/** Holds the params extracted from the active route (e.g. `{ id }` for `/blog/:id`). */
|
|
10
|
+
export const ParamsContext = createContext<RouteParams>({});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { matchRoute } from './match.js';
|
|
2
|
+
import type { RouteDef } from './types.js';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Navigator {
|
|
6
|
+
/** Non-standard but widely shipped; used to skip prefetch on data-saver / slow links. */
|
|
7
|
+
readonly connection?: {
|
|
8
|
+
readonly saveData?: boolean;
|
|
9
|
+
readonly effectiveType?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let routeTable: RouteDef[] = [];
|
|
15
|
+
const warmed = new WeakSet<RouteDef>();
|
|
16
|
+
let io: IntersectionObserver | null = null;
|
|
17
|
+
let mo: MutationObserver | null = null;
|
|
18
|
+
|
|
19
|
+
/** Resolves a same-origin `href` to a registered route, or `null` for external/unknown targets. */
|
|
20
|
+
function routeForHref(href: string): RouteDef | null {
|
|
21
|
+
let url: URL;
|
|
22
|
+
try {
|
|
23
|
+
url = new URL(href, window.location.href);
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (url.origin !== window.location.origin) return null;
|
|
28
|
+
for (const route of routeTable) {
|
|
29
|
+
if (matchRoute(route.pattern, url.pathname)) return route;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Warms a route's lazy chunk by triggering its loader once. Best-effort: each route loads at most
|
|
36
|
+
* once, and a failed load is forgotten (so the real navigation can retry and surface the error).
|
|
37
|
+
*/
|
|
38
|
+
function warm(route: RouteDef): void {
|
|
39
|
+
if (warmed.has(route)) return;
|
|
40
|
+
warmed.add(route);
|
|
41
|
+
void route.load().catch(() => {
|
|
42
|
+
warmed.delete(route);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Prefetches the route chunk for an internal `href` so a later navigation resolves instantly.
|
|
48
|
+
* No-op for external, unknown, or already-prefetched targets — safe to call from anywhere,
|
|
49
|
+
* including before an imperative {@link navigate} (e.g. `prefetch('/dashboard')` on hover/intent).
|
|
50
|
+
*/
|
|
51
|
+
export function prefetch(href: string): void {
|
|
52
|
+
const route = routeForHref(href);
|
|
53
|
+
if (route) warm(route);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Anchors to skip even when internal: new-tab, downloads, or an explicit `data-no-prefetch` opt-out. */
|
|
57
|
+
function isPrefetchable(a: HTMLAnchorElement): boolean {
|
|
58
|
+
if (a.target && a.target !== '_self') return false;
|
|
59
|
+
if (a.hasAttribute('download')) return false;
|
|
60
|
+
if (a.dataset.noPrefetch !== undefined) return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Observes an anchor for viewport entry if it points at a known internal route. */
|
|
65
|
+
function observeAnchor(a: HTMLAnchorElement): void {
|
|
66
|
+
if (!io || !isPrefetchable(a) || !routeForHref(a.href)) return;
|
|
67
|
+
io.observe(a);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Finds and observes every `<a href>` under `root`. */
|
|
71
|
+
function scan(root: ParentNode): void {
|
|
72
|
+
root.querySelectorAll('a[href]').forEach((el) => {
|
|
73
|
+
if (el instanceof HTMLAnchorElement) observeAnchor(el);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Skip prefetching on data-saver mode or 2g-class connections, where bandwidth is precious. */
|
|
78
|
+
function shouldSkipForConnection(): boolean {
|
|
79
|
+
const c = navigator.connection;
|
|
80
|
+
if (!c) return false;
|
|
81
|
+
return c.saveData === true || c.effectiveType === 'slow-2g' || c.effectiveType === '2g';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Starts idle-time prefetching of internal links. As each `<a>` pointing at a known route scrolls
|
|
86
|
+
* into view (or near it — 200px margin) its chunk is warmed once; links added later by client
|
|
87
|
+
* navigation are picked up via a MutationObserver. Called by {@link mount}; runs once per app.
|
|
88
|
+
*/
|
|
89
|
+
export function startPrefetcher(routes: RouteDef[]): void {
|
|
90
|
+
routeTable = routes;
|
|
91
|
+
if (
|
|
92
|
+
typeof window === 'undefined' ||
|
|
93
|
+
typeof IntersectionObserver === 'undefined' ||
|
|
94
|
+
typeof MutationObserver === 'undefined' ||
|
|
95
|
+
io
|
|
96
|
+
) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (shouldSkipForConnection()) return;
|
|
100
|
+
|
|
101
|
+
io = new IntersectionObserver(
|
|
102
|
+
(entries) => {
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
if (!entry.isIntersecting) continue;
|
|
105
|
+
const a = entry.target;
|
|
106
|
+
if (a instanceof HTMLAnchorElement) {
|
|
107
|
+
io?.unobserve(a);
|
|
108
|
+
prefetch(a.href);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{ rootMargin: '200px' },
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
mo = new MutationObserver((mutations) => {
|
|
116
|
+
for (const m of mutations) {
|
|
117
|
+
for (const node of m.addedNodes) {
|
|
118
|
+
if (node instanceof HTMLAnchorElement) observeAnchor(node);
|
|
119
|
+
else if (node instanceof Element) scan(node);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const begin = (): void => {
|
|
125
|
+
scan(document);
|
|
126
|
+
mo?.observe(document.body, { childList: true, subtree: true });
|
|
127
|
+
};
|
|
128
|
+
if (typeof requestIdleCallback === 'function') requestIdleCallback(begin);
|
|
129
|
+
else setTimeout(begin, 200);
|
|
130
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual scroll management for client navigation: scroll to top on push navigations, restore the
|
|
3
|
+
* saved position on back/forward, and honor in-page `#hash` targets. Positions are keyed by
|
|
4
|
+
* history entry; {@link applyScroll} runs once after the navigation commits.
|
|
5
|
+
*/
|
|
6
|
+
const positions = new Map<string, number>();
|
|
7
|
+
|
|
8
|
+
interface ScrollPlan {
|
|
9
|
+
readonly restore: number | null;
|
|
10
|
+
readonly hash: string;
|
|
11
|
+
readonly toTop: boolean;
|
|
12
|
+
}
|
|
13
|
+
let plan: ScrollPlan | null = null;
|
|
14
|
+
|
|
15
|
+
/** Switches off the browser's automatic scroll restoration so the router can manage it. */
|
|
16
|
+
export function enableManualScrollRestoration(): void {
|
|
17
|
+
if (typeof window !== 'undefined' && 'scrollRestoration' in window.history) {
|
|
18
|
+
window.history.scrollRestoration = 'manual';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Saves the current scroll position for a history key (called before leaving an entry). */
|
|
23
|
+
export function rememberScroll(key: string): void {
|
|
24
|
+
positions.set(key, window.scrollY);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Plans what {@link applyScroll} should do after the next navigation commits. */
|
|
28
|
+
export function planScroll(opts: { restoreKey?: string; hash: string; toTop: boolean }): void {
|
|
29
|
+
plan = {
|
|
30
|
+
restore: opts.restoreKey !== undefined ? (positions.get(opts.restoreKey) ?? 0) : null,
|
|
31
|
+
hash: opts.hash,
|
|
32
|
+
toTop: opts.toTop,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Applies the pending scroll plan once: hash target, else restored position, else top. */
|
|
37
|
+
export function applyScroll(): void {
|
|
38
|
+
const current = plan;
|
|
39
|
+
plan = null;
|
|
40
|
+
if (!current) return;
|
|
41
|
+
if (current.hash) {
|
|
42
|
+
const el = document.getElementById(decodeURIComponent(current.hash.slice(1)));
|
|
43
|
+
if (el) {
|
|
44
|
+
el.scrollIntoView();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (current.restore !== null) {
|
|
49
|
+
window.scrollTo(0, current.restore);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (current.toTop) window.scrollTo(0, 0);
|
|
53
|
+
}
|