@nifrajs/web-svelte 2.10.0 → 2.12.0
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/dist/Router.svelte +11 -1
- package/dist/client.d.ts +9 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +41 -7
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -8
- package/dist/index.js.map +1 -1
- package/dist/mdx.d.ts.map +1 -1
- package/dist/mdx.js +2 -1
- package/dist/mdx.js.map +1 -1
- package/dist/plugin.d.ts +24 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +49 -1
- package/dist/plugin.js.map +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -1
- package/dist/query.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +3 -1
- package/dist/router.js.map +1 -1
- package/package.json +11 -9
- package/src/Router.svelte +11 -1
- package/src/client.ts +47 -6
- package/src/index.ts +85 -9
- package/src/mdx.ts +2 -1
- package/src/plugin.ts +50 -1
- package/src/query.ts +4 -6
- package/src/router.ts +15 -7
package/dist/Router.svelte
CHANGED
|
@@ -9,10 +9,20 @@
|
|
|
9
9
|
reactive root is a component, not a function in `client.ts`.
|
|
10
10
|
-->
|
|
11
11
|
<script>
|
|
12
|
-
|
|
12
|
+
// `/client`, not the package root: the root's module graph reaches server-only code (the public-dir
|
|
13
|
+
// reader and friends), and a bundler that externalizes node builtins for the browser turns that into
|
|
14
|
+
// a runtime `Module "node:fs/promises" has been externalized` on the first import. `/client` is the
|
|
15
|
+
// browser-safe half, and it is where every other adapter takes this from.
|
|
16
|
+
import { searchOfChain } from "@nifrajs/web/client"
|
|
13
17
|
import Chain from "./Chain.svelte"
|
|
14
18
|
let { router, routes, searchSchemas } = $props()
|
|
15
19
|
|
|
20
|
+
// Capturing the INITIAL `router` is the intent, not an oversight: one router instance is created
|
|
21
|
+
// once per page and mounted here, so it never changes identity for this component's lifetime, and
|
|
22
|
+
// the reactivity that matters is the subscription below - the store pushes, this does not re-read.
|
|
23
|
+
// Wrapping it in a `$derived` would re-run `snapshot()` and hand `$state` a fresh object on every
|
|
24
|
+
// change, discarding the very state the subscription is maintaining.
|
|
25
|
+
// svelte-ignore state_referenced_locally
|
|
16
26
|
let snapshot = $state(router.snapshot())
|
|
17
27
|
$effect(() => router.subscribe(() => { snapshot = router.snapshot() }))
|
|
18
28
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { MountRouterOptions, RenderProps } from "@nifrajs/web";
|
|
2
2
|
export { errorBoundary } from "./error.js";
|
|
3
3
|
/** Hydrate a server-rendered Svelte layout `chain` (with the loader `props`) inside `container`. */
|
|
4
|
-
export
|
|
4
|
+
export interface HydrationAssuranceOptions {
|
|
5
|
+
readonly onWarning?: (message: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function hydrate(chain: readonly unknown[], props: RenderProps, container: unknown, options?: HydrationAssuranceOptions): void;
|
|
8
|
+
/** Testing hook used by verification runners to observe Svelte warnings and identity. */
|
|
9
|
+
export declare const hydrationAssuranceHook: Readonly<{
|
|
10
|
+
framework: "svelte";
|
|
11
|
+
runtimeIdentity: () => object;
|
|
12
|
+
}>;
|
|
5
13
|
/**
|
|
6
14
|
* Hydrate a stateful Svelte Router. The `Router` component holds the store snapshot in `$state` and
|
|
7
15
|
* re-renders the matched layout chain on each store change - so client navigations swap routes without
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAenE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,wBAAgB,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAenE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/C;AAWD,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,OAAO,EAClB,OAAO,CAAC,EAAE,yBAAyB,GAClC,IAAI,CAYN;AAED,yFAAyF;AACzF,eAAO,MAAM,sBAAsB;;2BAEZ,MAAM;EAC3B,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAc7D"}
|
package/dist/client.js
CHANGED
|
@@ -12,13 +12,34 @@ import Router from "./Router.svelte";
|
|
|
12
12
|
// The `_error` boundary marker - Chain.svelte renders it as a <svelte:boundary>. Re-exported here so
|
|
13
13
|
// nifra's client codegen resolves it from `@nifrajs/web-svelte/client` alongside `mountRouter`.
|
|
14
14
|
export { errorBoundary } from "./error.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
function assuranceWarning() {
|
|
16
|
+
const value = globalThis[Symbol.for("nifra.hydration.assurance")];
|
|
17
|
+
if (typeof value !== "object" || value === null)
|
|
18
|
+
return undefined;
|
|
19
|
+
const callback = value.onWarning;
|
|
20
|
+
return typeof callback === "function" ? callback : undefined;
|
|
21
21
|
}
|
|
22
|
+
export function hydrate(chain, props, container, options) {
|
|
23
|
+
const warning = options?.onWarning ?? assuranceWarning();
|
|
24
|
+
const originalWarn = console.warn;
|
|
25
|
+
if (warning !== undefined)
|
|
26
|
+
console.warn = (...args) => warning(args.map(String).join(" "));
|
|
27
|
+
try {
|
|
28
|
+
svelteHydrate(Chain, {
|
|
29
|
+
target: container,
|
|
30
|
+
props: { chain, props, layoutData: props.layoutData },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
if (warning !== undefined)
|
|
35
|
+
console.warn = originalWarn;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Testing hook used by verification runners to observe Svelte warnings and identity. */
|
|
39
|
+
export const hydrationAssuranceHook = Object.freeze({
|
|
40
|
+
framework: "svelte",
|
|
41
|
+
runtimeIdentity: () => svelteHydrate,
|
|
42
|
+
});
|
|
22
43
|
/**
|
|
23
44
|
* Hydrate a stateful Svelte Router. The `Router` component holds the store snapshot in `$state` and
|
|
24
45
|
* re-renders the matched layout chain on each store change - so client navigations swap routes without
|
|
@@ -28,6 +49,19 @@ export function hydrate(chain, props, container) {
|
|
|
28
49
|
export function mountRouter(options) {
|
|
29
50
|
const { router, routes, searchSchemas, container } = options;
|
|
30
51
|
setMountedRouter(router); // expose it to useFetcher/useFetchers (same page, client-only)
|
|
31
|
-
|
|
52
|
+
const warning = assuranceWarning();
|
|
53
|
+
const originalWarn = console.warn;
|
|
54
|
+
if (warning !== undefined)
|
|
55
|
+
console.warn = (...args) => warning(args.map(String).join(" "));
|
|
56
|
+
try {
|
|
57
|
+
svelteHydrate(Router, {
|
|
58
|
+
target: container,
|
|
59
|
+
props: { router, routes, searchSchemas },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
if (warning !== undefined)
|
|
64
|
+
console.warn = originalWarn;
|
|
65
|
+
}
|
|
32
66
|
}
|
|
33
67
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAEpC,qGAAqG;AACrG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAEpC,qGAAqG;AACrG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAO1C,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAI,UAAsD,CACnE,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CACxC,CAAA;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,QAAQ,GAAI,KAAiC,CAAC,SAAS,CAAA;IAC7D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAE,QAAsC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC7F,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,KAAyB,EACzB,KAAkB,EAClB,SAAkB,EAClB,OAAmC;IAEnC,MAAM,OAAO,GAAG,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,CAAA;IACxD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAA;IACjC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1F,IAAI,CAAC;QACH,aAAa,CAAC,KAAK,EAAE;YACnB,MAAM,EAAE,SAAoB;YAC5B,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE;SACtD,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,GAAG,YAAY,CAAA;IACxD,CAAC;AACH,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC;IAClD,SAAS,EAAE,QAAiB;IAC5B,eAAe,EAAE,GAAW,EAAE,CAAC,aAAa;CAC7C,CAAC,CAAA;AAEF;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC5D,gBAAgB,CAAC,MAAM,CAAC,CAAA,CAAC,+DAA+D;IACxF,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAA;IAClC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAA;IACjC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1F,IAAI,CAAC;QACH,aAAa,CAAC,MAAM,EAAE;YACpB,MAAM,EAAE,SAAoB;YAC5B,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE;SACzC,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,GAAG,YAAY,CAAA;IACxD,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @nifrajs/web-svelte - the Svelte 5 render adapter for @nifrajs/web (server side) + the `.svelte` compiler
|
|
3
|
-
* Bun-plugin. SSR via Svelte's `render` (svelte/server), which returns HTML strings (Svelte SSR is
|
|
4
|
-
* string-based, not a stream), wrapped in a one-chunk Web `ReadableStream` (the seam's shape). The
|
|
5
|
-
* layout-chain fold is the recursive `Chain.svelte`. Client hydration lives in `@nifrajs/web-svelte/client`.
|
|
6
|
-
* Svelte components compile from `.svelte` files, so this adapter ships a build plugin (like
|
|
7
|
-
* `@nifrajs/web-solid`'s Babel plugin) - there is no callable-component runtime.
|
|
8
|
-
*/
|
|
9
1
|
import type { RenderAdapter } from "@nifrajs/web";
|
|
10
2
|
export { svelteBunPlugin } from "./plugin.js";
|
|
11
3
|
/** The Svelte server render adapter - pass to @nifrajs/web's `renderPage`. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,cAAc,CAAA;AAM9D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAgF7C,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,aA8B3B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @nifrajs/web-svelte - the Svelte 5 render adapter for @nifrajs/web (server side) + the `.svelte` compiler
|
|
3
|
+
* Bun-plugin. SSR via Svelte's `render` (svelte/server), which returns HTML strings (Svelte SSR is
|
|
4
|
+
* string-based, not a stream), wrapped in a one-chunk Web `ReadableStream` (the seam's shape). The
|
|
5
|
+
* layout-chain fold is the recursive `Chain.svelte`. Client hydration lives in `@nifrajs/web-svelte/client`.
|
|
6
|
+
* Svelte components compile from `.svelte` files, so this adapter ships a build plugin (like
|
|
7
|
+
* `@nifrajs/web-solid`'s Babel plugin) - there is no callable-component runtime.
|
|
8
|
+
*/
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { ssrModuleLoader } from "@nifrajs/web";
|
|
11
|
+
import { render as runtimeRender } from "svelte/server";
|
|
12
|
+
// Re-export the compiler plugin for convenience.
|
|
6
13
|
export { svelteBunPlugin } from "./plugin.js";
|
|
14
|
+
let chainComponent;
|
|
15
|
+
let chainPromise;
|
|
16
|
+
/**
|
|
17
|
+
* Svelte's server renderer. Normally the runtime's, but it is swapped for the dev server's copy when
|
|
18
|
+
* `Chain` comes from there - a component compiled by one toolchain has to render through the renderer
|
|
19
|
+
* that toolchain resolved, or `setContext` writes into a renderer the children never read.
|
|
20
|
+
*/
|
|
21
|
+
let render = runtimeRender;
|
|
22
|
+
/** Resolved `Chain`, or the in-flight load on the very first call. */
|
|
23
|
+
const loadChain = () => {
|
|
24
|
+
if (chainComponent !== undefined)
|
|
25
|
+
return chainComponent;
|
|
26
|
+
// A dev server that owns SSR resolution compiles the app's `.svelte` routes itself, and `Chain` has
|
|
27
|
+
// to come from that same graph: the runtime has no `.svelte` loader on that pipeline, so a plain
|
|
28
|
+
// `import` yields the file PATH and SSR dies at `component is not a function`. Registering a second
|
|
29
|
+
// compiler in the runtime instead is the trap - it gives the tree two Svelte runtimes, and the
|
|
30
|
+
// renderer `Chain` sets context on is not the one the routes read it from. Taking BOTH the component
|
|
31
|
+
// and `svelte/server` from the dev server keeps it at one compiler and one runtime.
|
|
32
|
+
const load = ssrModuleLoader();
|
|
33
|
+
chainPromise ??= (load !== undefined
|
|
34
|
+
? Promise.all([
|
|
35
|
+
load(fileURLToPath(new URL("./Chain.svelte", import.meta.url))),
|
|
36
|
+
load("svelte/server"),
|
|
37
|
+
]).then(([mod, server]) => {
|
|
38
|
+
render = server.render;
|
|
39
|
+
return mod.default;
|
|
40
|
+
})
|
|
41
|
+
: import("./Chain.svelte").then((m) => m.default)).then((c) => {
|
|
42
|
+
chainComponent = c;
|
|
43
|
+
return c;
|
|
44
|
+
});
|
|
45
|
+
return chainPromise;
|
|
46
|
+
};
|
|
7
47
|
// Svelte SSR yields a complete HTML string; the seam wants a stream, so emit it as one chunk.
|
|
8
48
|
function oneChunk(html) {
|
|
9
49
|
const bytes = new TextEncoder().encode(html);
|
|
@@ -20,16 +60,25 @@ export const svelteAdapter = {
|
|
|
20
60
|
// returns the body directly (renderPage buffers it on the non-deferred fast path), and
|
|
21
61
|
// `renderToStream` wraps the same string in a one-chunk stream for the deferred path. No streaming
|
|
22
62
|
// renderer to skip here - but going straight to a string avoids the per-request stream allocation.
|
|
63
|
+
// The seam allows `string | Promise<string>`, so the first render can resolve `Chain` while every
|
|
64
|
+
// render after it stays a plain synchronous call - see `loadChain`.
|
|
23
65
|
renderToString(chain, props) {
|
|
24
|
-
|
|
66
|
+
const Chain = loadChain();
|
|
67
|
+
const props$ = { chain, props, layoutData: props.layoutData };
|
|
68
|
+
return Chain instanceof Promise
|
|
69
|
+
? Chain.then((C) => render(C, { props: props$ }).body)
|
|
70
|
+
: render(Chain, { props: props$ }).body;
|
|
25
71
|
},
|
|
26
72
|
renderToStream(chain, props) {
|
|
27
73
|
// `Chain` folds the layout chain (page innermost gets `props`; layouts wrap via their `children`
|
|
28
74
|
// snippet). Svelte's `render` returns { head, body }; the body goes into #root. (Svelte's dynamic
|
|
29
75
|
// `head` - from <svelte:head> - isn't surfaced through the seam's static `hydrationHead`; nifra's
|
|
30
76
|
// own meta/head API manages the document head instead.)
|
|
31
|
-
const
|
|
32
|
-
|
|
77
|
+
const Chain = loadChain();
|
|
78
|
+
const props$ = { chain, props, layoutData: props.layoutData };
|
|
79
|
+
return Chain instanceof Promise
|
|
80
|
+
? Chain.then((C) => oneChunk(render(C, { props: props$ }).body))
|
|
81
|
+
: oneChunk(render(Chain, { props: props$ }).body);
|
|
33
82
|
},
|
|
34
83
|
// Svelte's client `hydrate` reconciles against the existing DOM; no per-document bootstrap script is
|
|
35
84
|
// needed (contrast Solid's generateHydrationScript) - the seam allows the empty string.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mdx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAsBpC;uFACuF;AACvF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CA+CrE"}
|
package/dist/mdx.js
CHANGED
|
@@ -6,6 +6,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
+
import { rewriteSsrImports } from "@nifrajs/web/plugins/kit";
|
|
9
10
|
import { compile } from "svelte/compiler";
|
|
10
11
|
/**
|
|
11
12
|
* `@nifrajs/web-svelte/mdx` - compile `.mdx` routes/files to **Svelte** components. Svelte has no JSX
|
|
@@ -58,7 +59,7 @@ export function svelteMdxBunPlugin(generate) {
|
|
|
58
59
|
loader: "js",
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
return { contents: js.code, loader: "js" };
|
|
62
|
+
return { contents: rewriteSsrImports(js.code, path, generate), loader: "js" };
|
|
62
63
|
});
|
|
63
64
|
build.onResolve({ filter: /\?svelte-mdx-css$/ }, (args) => ({
|
|
64
65
|
path: args.path,
|
package/dist/mdx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdx.js","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"mdx.js","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
1
|
import type { BunPlugin } from "bun";
|
|
2
|
+
/**
|
|
3
|
+
* The same hot-patch boundary as the Bun pipeline's, in the shape `@sveltejs/vite-plugin-svelte` takes:
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* svelte({ dynamicCompileOptions: svelteHmrBoundary })
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* Svelte's HMR wrapper resolves the component through a signal, which survives hydration only where
|
|
10
|
+
* the component is a plain child in a template. A layout or a page is not: the adapter's `Chain` enters
|
|
11
|
+
* chain members as dynamic components and through snippets, so a wrapper on one leaves the hydration
|
|
12
|
+
* cursor pointing at markup the wrapped component never claims. Svelte then throws the server's tree
|
|
13
|
+
* away and re-renders on the client - `hydration_mismatch` in the console, on first load, before any
|
|
14
|
+
* edit. Left on, the dev server quietly stops exercising hydration at all.
|
|
15
|
+
*
|
|
16
|
+
* Views keep hot-patching; route modules fall back to a full reload, which is what they want anyway
|
|
17
|
+
* (a route module carries the loader and meta the server ran, and re-running those is a navigation).
|
|
18
|
+
* Outside a nifra dev server nothing is announced and the answer is "no boundary" - a full reload is
|
|
19
|
+
* always correct, a mispatched tree is not.
|
|
20
|
+
*/
|
|
21
|
+
export declare function svelteHmrBoundary({ filename }: {
|
|
22
|
+
filename: string;
|
|
23
|
+
}): {
|
|
24
|
+
hmr: boolean;
|
|
25
|
+
};
|
|
2
26
|
export declare function svelteBunPlugin(generate: "dom" | "ssr"): BunPlugin;
|
|
3
27
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAsBpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,CAEtF;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CA8DlE"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devHotComponent, devServerCompile, rewriteSsrImports } from "@nifrajs/web/plugins/kit";
|
|
1
2
|
import { compile } from "svelte/compiler";
|
|
2
3
|
/**
|
|
3
4
|
* `@nifrajs/web-svelte/plugin` - the `.svelte` compiler Bun-plugin, in its OWN module (no `.svelte`
|
|
@@ -17,6 +18,28 @@ import { compile } from "svelte/compiler";
|
|
|
17
18
|
*/
|
|
18
19
|
const STYLE_SUFFIX = "?svelte-css";
|
|
19
20
|
const STYLE_NS = "nifra-svelte-css";
|
|
21
|
+
/**
|
|
22
|
+
* The same hot-patch boundary as the Bun pipeline's, in the shape `@sveltejs/vite-plugin-svelte` takes:
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* svelte({ dynamicCompileOptions: svelteHmrBoundary })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Svelte's HMR wrapper resolves the component through a signal, which survives hydration only where
|
|
29
|
+
* the component is a plain child in a template. A layout or a page is not: the adapter's `Chain` enters
|
|
30
|
+
* chain members as dynamic components and through snippets, so a wrapper on one leaves the hydration
|
|
31
|
+
* cursor pointing at markup the wrapped component never claims. Svelte then throws the server's tree
|
|
32
|
+
* away and re-renders on the client - `hydration_mismatch` in the console, on first load, before any
|
|
33
|
+
* edit. Left on, the dev server quietly stops exercising hydration at all.
|
|
34
|
+
*
|
|
35
|
+
* Views keep hot-patching; route modules fall back to a full reload, which is what they want anyway
|
|
36
|
+
* (a route module carries the loader and meta the server ran, and re-running those is a navigation).
|
|
37
|
+
* Outside a nifra dev server nothing is announced and the answer is "no boundary" - a full reload is
|
|
38
|
+
* always correct, a mispatched tree is not.
|
|
39
|
+
*/
|
|
40
|
+
export function svelteHmrBoundary({ filename }) {
|
|
41
|
+
return { hmr: devHotComponent(filename) };
|
|
42
|
+
}
|
|
20
43
|
export function svelteBunPlugin(generate) {
|
|
21
44
|
// Compiled scoped CSS per `.svelte` file (client build only) - read back by the virtual-module loader.
|
|
22
45
|
const cssByPath = new Map();
|
|
@@ -28,10 +51,35 @@ export function svelteBunPlugin(generate) {
|
|
|
28
51
|
build.onLoad({ filter: /\.svelte(\?|$)/ }, async (args) => {
|
|
29
52
|
const path = args.path.split("?")[0] ?? args.path;
|
|
30
53
|
const source = await Bun.file(path).text();
|
|
54
|
+
// Per compile, not per registration: the CLI registers the SSR plugin before the dev server
|
|
55
|
+
// exists, and the flag is set by the dev server.
|
|
56
|
+
//
|
|
57
|
+
// Svelte's HMR is a compiler feature (`$.hmr` + a self-accepting `import.meta.hot.accept`), and
|
|
58
|
+
// it is only available under `dev`. `dev` is set on BOTH halves so the server and client builds
|
|
59
|
+
// stay the same Svelte, while `hmr` stays client-only, there being no `import.meta.hot` on the
|
|
60
|
+
// server. `nifra build` sets neither, so nothing here reaches a production bundle.
|
|
61
|
+
//
|
|
62
|
+
// The server half additionally needs Svelte's own runtime in its dev shape - `dev` output calls
|
|
63
|
+
// `push_element`, which reads state the runtime only records when ITS `DEV` is true. That flag
|
|
64
|
+
// resolves through `esm-env`, which falls back to `NODE_ENV` off the export conditions; the dev
|
|
65
|
+
// server sets it for exactly this reason. Getting that half wrong throws on the first element:
|
|
66
|
+
// `undefined is not an object (evaluating 'context.function[FILENAME]')`.
|
|
67
|
+
//
|
|
68
|
+
// `hmr` narrows further, to the app's own non-route components ({@link devHotComponent}).
|
|
69
|
+
// Svelte's wrapper replaces the component with a block that resolves it through a signal, and
|
|
70
|
+
// that survives hydration only where the component is a plain child in a template. A chain
|
|
71
|
+
// member is not: the adapter's `Chain` enters layouts and pages as dynamic components and
|
|
72
|
+
// through snippets, and a wrapper on either side of that leaves the hydration cursor pointing
|
|
73
|
+
// at markup the wrapped component never claims - the whole tree is then thrown away and
|
|
74
|
+
// re-rendered on the client, silently, before any edit. A view component is the boundary that
|
|
75
|
+
// works, and it is also the one worth patching.
|
|
76
|
+
const dev = devServerCompile();
|
|
31
77
|
const { js, css } = compile(source, {
|
|
32
78
|
generate: generate === "ssr" ? "server" : "client",
|
|
33
79
|
filename: path,
|
|
34
80
|
css: "external", // emit the scoped stylesheet separately (not injected into the JS)
|
|
81
|
+
dev,
|
|
82
|
+
hmr: dev && generate === "dom" && devHotComponent(path),
|
|
35
83
|
});
|
|
36
84
|
if (generate === "dom" && css?.code) {
|
|
37
85
|
cssByPath.set(path, css.code);
|
|
@@ -41,7 +89,7 @@ export function svelteBunPlugin(generate) {
|
|
|
41
89
|
loader: "js",
|
|
42
90
|
};
|
|
43
91
|
}
|
|
44
|
-
return { contents: js.code, loader: "js" };
|
|
92
|
+
return { contents: rewriteSsrImports(js.code, path, generate), loader: "js" };
|
|
45
93
|
});
|
|
46
94
|
// Virtual CSS module: `<file>.svelte?svelte-css` → the compiled scoped stylesheet (css loader).
|
|
47
95
|
build.onResolve({ filter: /\?svelte-css$/ }, (args) => ({
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QueryClient, QueryState } from "@nifrajs/web";
|
|
2
2
|
/**
|
|
3
3
|
* `@nifrajs/web-svelte/query` - Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
|
|
4
4
|
* `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` - read it
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAe,UAAU,EAAE,MAAM,cAAc,CAAA;AAIxE;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAA;AAStD,wGAAwG;AACxG,wBAAgB,cAAc,IAAI,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAEvE;AAWD,gFAAgF;AAChF,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG;IACpD,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CACnC,CAAA;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAe7E"}
|
package/dist/query.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// `/client`, not the root - these are DOM values, and the root's graph carries the
|
|
2
|
+
// server, which Vite's dev server evaluates rather than tree-shakes.
|
|
3
|
+
import { createQueryClient } from "@nifrajs/web/client";
|
|
2
4
|
/**
|
|
3
5
|
* `@nifrajs/web-svelte/query` - Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
|
|
4
6
|
* `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` - read it
|
package/dist/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AACA,mFAAmF;AACnF,qEAAqE;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD;;;;;;;;;;GAUG;AACH,OAAO,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEtD,IAAI,MAA+B,CAAA;AACnC,SAAS,SAAS;IAChB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAA,CAAC,wCAAwC;IAC5F,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,GAAG,iBAAiB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC/E,OAAO,MAAM,CAAA;AACf,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,cAAc;IAC5B,OAAO,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;AACvD,CAAC;AAED,0DAA0D;AAC1D,MAAM,IAAI,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,UAAU,EAAE,KAAK;IACjB,SAAS,EAAE,MAAM,CAAC,iBAAiB;CACpC,CAAC,CAAA;AAQF;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAI,GAAY,EAAE,EAAoB;IAC5D,MAAM,MAAM,GAA+B,SAAS,EAAE,EAAE,KAAK,CAAI,GAAG,EAAE,EAAE,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,QAAQ,CAAgB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;QAC/E,IAAI,MAAM,KAAK,SAAS;YAAE,OAAM;QAChC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QACtB,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;QAClE,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA,CAAC,iDAAiD;QAChF,OAAO,WAAW,CAAA;IACpB,CAAC,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,MAAM;QACpB,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,CAAC,CAAE,CAAC,KAAK,IAAI,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAA;QAC9F,CAAC,CAAsB,CAAA;IAC3B,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;AAC1C,CAAC"}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server";
|
|
2
|
-
import {
|
|
2
|
+
import type { Blocker, BlockerFunction, BlockerState, NavigateFunction } from "@nifrajs/web";
|
|
3
3
|
/**
|
|
4
4
|
* `@nifrajs/web-svelte/router` - Svelte routing bindings over the agnostic `@nifrajs/web` history layer,
|
|
5
5
|
* as plain `.ts` (no runes/compiler needed): `useNavigate` (programmatic navigation), `useBlocker` (the
|
|
@@ -9,7 +9,7 @@ import { type Blocker, type BlockerFunction, type NavigateFunction } from "@nifr
|
|
|
9
9
|
* context on SSR + client alike. Idle before hydration (native `<a>` navigation still works). SSR-safe.
|
|
10
10
|
*/
|
|
11
11
|
import { type Readable } from "svelte/store";
|
|
12
|
-
export type { Blocker, BlockerFunction, BlockerState, NavigateFunction }
|
|
12
|
+
export type { Blocker, BlockerFunction, BlockerState, NavigateFunction };
|
|
13
13
|
/**
|
|
14
14
|
* The route's typed, validated search params as a reactive accessor - the SAME value the loader received
|
|
15
15
|
* as `ctx.search`. SSR-correct: `Chain.svelte` provides it from the URL server-side and from the
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAIzE,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,YAAY,EACZ,gBAAgB,EAGjB,MAAM,cAAc,CAAA;AAUrB;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAA;AAEtD,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAQxE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,gBAAgB,GAAG,SAAS,GAAG,SAAS,KACpD,MAAM,MAAM,SAAS,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzF;AAED;;kCAEkC;AAClC,wBAAgB,WAAW,IAAI,gBAAgB,CAO9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,CAOpF"}
|
package/dist/router.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// `/client`, not the root - these are DOM values, and the root's graph carries the
|
|
2
|
+
// server, which Vite's dev server evaluates rather than tree-shakes.
|
|
3
|
+
import { getBrowserNavigate, IDLE_BLOCKER, registerBlocker, resolveNavigate, } from "@nifrajs/web/client";
|
|
2
4
|
import { getContext } from "svelte";
|
|
3
5
|
/**
|
|
4
6
|
* `@nifrajs/web-svelte/router` - Svelte routing bindings over the agnostic `@nifrajs/web` history layer,
|
package/dist/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAYA,mFAAmF;AACnF,qEAAqE;AACrE,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC;;;;;;;GAOG;AACH,OAAO,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAItD,uGAAuG;AACvG,oEAAoE;AACpE,MAAM,UAAU,GAAG,4BAA4B,CAAA;AAC/C,MAAM,YAAY,GAAsC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AACzE,MAAM,qBAAqB,GAAG,GAA4B,EAAE,CAAC,YAAY,CAAA;AAEzE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS;IAGvB,MAAM,GAAG,GAAG,UAAU,CAA8C,UAAU,CAAC,CAAA;IAC/E,OAAO,CAAC,GAAG,IAAI,qBAAqB,CAET,CAAA;AAC7B,CAAC;AAED;;kCAEkC;AAClC,MAAM,UAAU,WAAW;IACzB,OAAO,CAAC,CAAC,EAAyC,EAAE,OAAyB,EAAE,EAAE;QAC/E,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAM;QAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;QAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;IACzC,CAAC,CAAqB,CAAA;AACxB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,WAAsC;IAC/D,OAAO,QAAQ,CAAU,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAC7C,eAAe,CACb,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,EAC/E,GAAG,CACJ,CACF,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/web-svelte",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Svelte 5 render adapter for @nifrajs/web - SSR + hydration + the .svelte compiler Bun-plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
7
7
|
"typescript",
|
|
8
8
|
"nifra",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
9
|
+
"svelte",
|
|
10
|
+
"svelte5",
|
|
11
|
+
"ssr",
|
|
12
|
+
"hydration",
|
|
13
|
+
"render-adapter",
|
|
14
|
+
"full-stack"
|
|
13
15
|
],
|
|
14
16
|
"type": "module",
|
|
15
17
|
"main": "./dist/index.js",
|
|
@@ -98,10 +100,10 @@
|
|
|
98
100
|
"access": "public"
|
|
99
101
|
},
|
|
100
102
|
"dependencies": {
|
|
101
|
-
"@nifrajs/core": "2.
|
|
102
|
-
"@nifrajs/i18n": "2.
|
|
103
|
-
"@nifrajs/image": "2.
|
|
104
|
-
"@nifrajs/web": "2.
|
|
103
|
+
"@nifrajs/core": "2.12.0",
|
|
104
|
+
"@nifrajs/i18n": "2.12.0",
|
|
105
|
+
"@nifrajs/image": "2.12.0",
|
|
106
|
+
"@nifrajs/web": "2.12.0"
|
|
105
107
|
},
|
|
106
108
|
"peerDependencies": {
|
|
107
109
|
"mdsvex": "^0.12",
|
package/src/Router.svelte
CHANGED
|
@@ -9,10 +9,20 @@
|
|
|
9
9
|
reactive root is a component, not a function in `client.ts`.
|
|
10
10
|
-->
|
|
11
11
|
<script>
|
|
12
|
-
|
|
12
|
+
// `/client`, not the package root: the root's module graph reaches server-only code (the public-dir
|
|
13
|
+
// reader and friends), and a bundler that externalizes node builtins for the browser turns that into
|
|
14
|
+
// a runtime `Module "node:fs/promises" has been externalized` on the first import. `/client` is the
|
|
15
|
+
// browser-safe half, and it is where every other adapter takes this from.
|
|
16
|
+
import { searchOfChain } from "@nifrajs/web/client"
|
|
13
17
|
import Chain from "./Chain.svelte"
|
|
14
18
|
let { router, routes, searchSchemas } = $props()
|
|
15
19
|
|
|
20
|
+
// Capturing the INITIAL `router` is the intent, not an oversight: one router instance is created
|
|
21
|
+
// once per page and mounted here, so it never changes identity for this component's lifetime, and
|
|
22
|
+
// the reactivity that matters is the subscription below - the store pushes, this does not re-read.
|
|
23
|
+
// Wrapping it in a `$derived` would re-run `snapshot()` and hand `$state` a fresh object on every
|
|
24
|
+
// change, discarding the very state the subscription is maintaining.
|
|
25
|
+
// svelte-ignore state_referenced_locally
|
|
16
26
|
let snapshot = $state(router.snapshot())
|
|
17
27
|
$effect(() => router.subscribe(() => { snapshot = router.snapshot() }))
|
|
18
28
|
|
package/src/client.ts
CHANGED
|
@@ -16,13 +16,44 @@ import Router from "./Router.svelte"
|
|
|
16
16
|
export { errorBoundary } from "./error.ts"
|
|
17
17
|
|
|
18
18
|
/** Hydrate a server-rendered Svelte layout `chain` (with the loader `props`) inside `container`. */
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
target: container as Element,
|
|
22
|
-
props: { chain, props, layoutData: props.layoutData },
|
|
23
|
-
})
|
|
19
|
+
export interface HydrationAssuranceOptions {
|
|
20
|
+
readonly onWarning?: (message: string) => void
|
|
24
21
|
}
|
|
25
22
|
|
|
23
|
+
function assuranceWarning(): ((message: string) => void) | undefined {
|
|
24
|
+
const value = (globalThis as unknown as Record<PropertyKey, unknown>)[
|
|
25
|
+
Symbol.for("nifra.hydration.assurance")
|
|
26
|
+
]
|
|
27
|
+
if (typeof value !== "object" || value === null) return undefined
|
|
28
|
+
const callback = (value as { onWarning?: unknown }).onWarning
|
|
29
|
+
return typeof callback === "function" ? (callback as (message: string) => void) : undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function hydrate(
|
|
33
|
+
chain: readonly unknown[],
|
|
34
|
+
props: RenderProps,
|
|
35
|
+
container: unknown,
|
|
36
|
+
options?: HydrationAssuranceOptions,
|
|
37
|
+
): void {
|
|
38
|
+
const warning = options?.onWarning ?? assuranceWarning()
|
|
39
|
+
const originalWarn = console.warn
|
|
40
|
+
if (warning !== undefined) console.warn = (...args) => warning(args.map(String).join(" "))
|
|
41
|
+
try {
|
|
42
|
+
svelteHydrate(Chain, {
|
|
43
|
+
target: container as Element,
|
|
44
|
+
props: { chain, props, layoutData: props.layoutData },
|
|
45
|
+
})
|
|
46
|
+
} finally {
|
|
47
|
+
if (warning !== undefined) console.warn = originalWarn
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Testing hook used by verification runners to observe Svelte warnings and identity. */
|
|
52
|
+
export const hydrationAssuranceHook = Object.freeze({
|
|
53
|
+
framework: "svelte" as const,
|
|
54
|
+
runtimeIdentity: (): object => svelteHydrate,
|
|
55
|
+
})
|
|
56
|
+
|
|
26
57
|
/**
|
|
27
58
|
* Hydrate a stateful Svelte Router. The `Router` component holds the store snapshot in `$state` and
|
|
28
59
|
* re-renders the matched layout chain on each store change - so client navigations swap routes without
|
|
@@ -32,5 +63,15 @@ export function hydrate(chain: readonly unknown[], props: RenderProps, container
|
|
|
32
63
|
export function mountRouter(options: MountRouterOptions): void {
|
|
33
64
|
const { router, routes, searchSchemas, container } = options
|
|
34
65
|
setMountedRouter(router) // expose it to useFetcher/useFetchers (same page, client-only)
|
|
35
|
-
|
|
66
|
+
const warning = assuranceWarning()
|
|
67
|
+
const originalWarn = console.warn
|
|
68
|
+
if (warning !== undefined) console.warn = (...args) => warning(args.map(String).join(" "))
|
|
69
|
+
try {
|
|
70
|
+
svelteHydrate(Router, {
|
|
71
|
+
target: container as Element,
|
|
72
|
+
props: { router, routes, searchSchemas },
|
|
73
|
+
})
|
|
74
|
+
} finally {
|
|
75
|
+
if (warning !== undefined) console.warn = originalWarn
|
|
76
|
+
}
|
|
36
77
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,15 +6,82 @@
|
|
|
6
6
|
* Svelte components compile from `.svelte` files, so this adapter ships a build plugin (like
|
|
7
7
|
* `@nifrajs/web-solid`'s Babel plugin) - there is no callable-component runtime.
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
9
|
+
import { fileURLToPath } from "node:url"
|
|
10
|
+
import type { RenderAdapter, RenderProps } from "@nifrajs/web"
|
|
11
|
+
import { ssrModuleLoader } from "@nifrajs/web"
|
|
12
|
+
import type { Component } from "svelte"
|
|
13
|
+
import { render as runtimeRender } from "svelte/server"
|
|
12
14
|
|
|
13
|
-
// Re-export the compiler plugin for convenience.
|
|
14
|
-
// `@nifrajs/web-svelte/plugin` (not from here) - importing this module eagerly loads `Chain.svelte`,
|
|
15
|
-
// which must be compiled by the already-registered plugin.
|
|
15
|
+
// Re-export the compiler plugin for convenience.
|
|
16
16
|
export { svelteBunPlugin } from "./plugin.ts"
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* `Chain.svelte` is loaded on FIRST RENDER, not at module load.
|
|
20
|
+
*
|
|
21
|
+
* A static `import Chain from "./Chain.svelte"` here is unloadable in dev. The CLI's `loadApp` imports
|
|
22
|
+
* the app's config, which re-exports this adapter, so this module evaluates before any Svelte compiler
|
|
23
|
+
* is registered in the runtime - and a raw `.svelte` file then loads as a path string, so SSR dies at
|
|
24
|
+
* render with `component is not a function`, naming the asset rather than the ordering. Bundled builds
|
|
25
|
+
* never saw it: `Bun.build` compiles the whole graph up front.
|
|
26
|
+
*
|
|
27
|
+
* Deferring to first render moves the load AFTER the dev server has registered the app's
|
|
28
|
+
* `serverPlugins`, which is the only point at which a compiler is guaranteed to exist.
|
|
29
|
+
*
|
|
30
|
+
* Exactly ONE render pays for that. `chain` is memoized as a plain value, so every later call takes the
|
|
31
|
+
* synchronous branch below and allocates no promise - awaiting on the hot path measured as a real 3%
|
|
32
|
+
* throughput loss on the SSR benchmark, which is not a price worth paying for a one-time import.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* What `Chain.svelte` destructures from `$props()`.
|
|
36
|
+
*
|
|
37
|
+
* A type alias rather than an `interface` on purpose. `.svelte` files are compiled by a bundler, not
|
|
38
|
+
* by tsc, so they arrive through the ambient shim as `Component<Record<string, unknown>>` and the load
|
|
39
|
+
* below narrows that to this shape. An interface has no implicit index signature, so that narrowing is
|
|
40
|
+
* a comparison between two unrelated types and `svelte-check` rejects it; a type alias does have one,
|
|
41
|
+
* which makes the narrowing legal to state directly instead of laundering it through `unknown`.
|
|
42
|
+
*/
|
|
43
|
+
type ChainProps = {
|
|
44
|
+
readonly chain: readonly unknown[]
|
|
45
|
+
readonly props: RenderProps
|
|
46
|
+
/** Explicitly `| undefined`: the adapter always passes the key, and `RenderProps.layoutData` is
|
|
47
|
+
* optional, so under `exactOptionalPropertyTypes` the absent case has to be part of the type. */
|
|
48
|
+
readonly layoutData: readonly unknown[] | undefined
|
|
49
|
+
}
|
|
50
|
+
let chainComponent: Component<ChainProps> | undefined
|
|
51
|
+
let chainPromise: Promise<Component<ChainProps>> | undefined
|
|
52
|
+
/**
|
|
53
|
+
* Svelte's server renderer. Normally the runtime's, but it is swapped for the dev server's copy when
|
|
54
|
+
* `Chain` comes from there - a component compiled by one toolchain has to render through the renderer
|
|
55
|
+
* that toolchain resolved, or `setContext` writes into a renderer the children never read.
|
|
56
|
+
*/
|
|
57
|
+
let render: typeof runtimeRender = runtimeRender
|
|
58
|
+
/** Resolved `Chain`, or the in-flight load on the very first call. */
|
|
59
|
+
const loadChain = (): Component<ChainProps> | Promise<Component<ChainProps>> => {
|
|
60
|
+
if (chainComponent !== undefined) return chainComponent
|
|
61
|
+
// A dev server that owns SSR resolution compiles the app's `.svelte` routes itself, and `Chain` has
|
|
62
|
+
// to come from that same graph: the runtime has no `.svelte` loader on that pipeline, so a plain
|
|
63
|
+
// `import` yields the file PATH and SSR dies at `component is not a function`. Registering a second
|
|
64
|
+
// compiler in the runtime instead is the trap - it gives the tree two Svelte runtimes, and the
|
|
65
|
+
// renderer `Chain` sets context on is not the one the routes read it from. Taking BOTH the component
|
|
66
|
+
// and `svelte/server` from the dev server keeps it at one compiler and one runtime.
|
|
67
|
+
const load = ssrModuleLoader()
|
|
68
|
+
chainPromise ??= (
|
|
69
|
+
load !== undefined
|
|
70
|
+
? Promise.all([
|
|
71
|
+
load(fileURLToPath(new URL("./Chain.svelte", import.meta.url))),
|
|
72
|
+
load("svelte/server"),
|
|
73
|
+
]).then(([mod, server]) => {
|
|
74
|
+
render = (server as { render: typeof runtimeRender }).render
|
|
75
|
+
return (mod as { default: Component<ChainProps> }).default
|
|
76
|
+
})
|
|
77
|
+
: import("./Chain.svelte").then((m) => m.default as Component<ChainProps>)
|
|
78
|
+
).then((c) => {
|
|
79
|
+
chainComponent = c
|
|
80
|
+
return c
|
|
81
|
+
})
|
|
82
|
+
return chainPromise
|
|
83
|
+
}
|
|
84
|
+
|
|
18
85
|
// Svelte SSR yields a complete HTML string; the seam wants a stream, so emit it as one chunk.
|
|
19
86
|
function oneChunk(html: string): ReadableStream<Uint8Array> {
|
|
20
87
|
const bytes = new TextEncoder().encode(html)
|
|
@@ -32,16 +99,25 @@ export const svelteAdapter: RenderAdapter = {
|
|
|
32
99
|
// returns the body directly (renderPage buffers it on the non-deferred fast path), and
|
|
33
100
|
// `renderToStream` wraps the same string in a one-chunk stream for the deferred path. No streaming
|
|
34
101
|
// renderer to skip here - but going straight to a string avoids the per-request stream allocation.
|
|
102
|
+
// The seam allows `string | Promise<string>`, so the first render can resolve `Chain` while every
|
|
103
|
+
// render after it stays a plain synchronous call - see `loadChain`.
|
|
35
104
|
renderToString(chain, props) {
|
|
36
|
-
|
|
105
|
+
const Chain = loadChain()
|
|
106
|
+
const props$ = { chain, props, layoutData: props.layoutData }
|
|
107
|
+
return Chain instanceof Promise
|
|
108
|
+
? Chain.then((C) => render(C, { props: props$ }).body)
|
|
109
|
+
: render(Chain, { props: props$ }).body
|
|
37
110
|
},
|
|
38
111
|
renderToStream(chain, props) {
|
|
39
112
|
// `Chain` folds the layout chain (page innermost gets `props`; layouts wrap via their `children`
|
|
40
113
|
// snippet). Svelte's `render` returns { head, body }; the body goes into #root. (Svelte's dynamic
|
|
41
114
|
// `head` - from <svelte:head> - isn't surfaced through the seam's static `hydrationHead`; nifra's
|
|
42
115
|
// own meta/head API manages the document head instead.)
|
|
43
|
-
const
|
|
44
|
-
|
|
116
|
+
const Chain = loadChain()
|
|
117
|
+
const props$ = { chain, props, layoutData: props.layoutData }
|
|
118
|
+
return Chain instanceof Promise
|
|
119
|
+
? Chain.then((C) => oneChunk(render(C, { props: props$ }).body))
|
|
120
|
+
: oneChunk(render(Chain, { props: props$ }).body)
|
|
45
121
|
},
|
|
46
122
|
// Svelte's client `hydrate` reconciles against the existing DOM; no per-document bootstrap script is
|
|
47
123
|
// needed (contrast Solid's generateHydrationScript) - the seam allows the empty string.
|
package/src/mdx.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rewriteSsrImports } from "@nifrajs/web/plugins/kit"
|
|
1
2
|
import type { BunPlugin } from "bun"
|
|
2
3
|
import { compile } from "svelte/compiler"
|
|
3
4
|
|
|
@@ -57,7 +58,7 @@ export function svelteMdxBunPlugin(generate: "dom" | "ssr"): BunPlugin {
|
|
|
57
58
|
loader: "js",
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
return { contents: js.code, loader: "js" }
|
|
61
|
+
return { contents: rewriteSsrImports(js.code, path, generate), loader: "js" }
|
|
61
62
|
})
|
|
62
63
|
build.onResolve({ filter: /\?svelte-mdx-css$/ }, (args) => ({
|
|
63
64
|
path: args.path,
|
package/src/plugin.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devHotComponent, devServerCompile, rewriteSsrImports } from "@nifrajs/web/plugins/kit"
|
|
1
2
|
import type { BunPlugin } from "bun"
|
|
2
3
|
import { compile } from "svelte/compiler"
|
|
3
4
|
|
|
@@ -20,6 +21,29 @@ import { compile } from "svelte/compiler"
|
|
|
20
21
|
const STYLE_SUFFIX = "?svelte-css"
|
|
21
22
|
const STYLE_NS = "nifra-svelte-css"
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The same hot-patch boundary as the Bun pipeline's, in the shape `@sveltejs/vite-plugin-svelte` takes:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* svelte({ dynamicCompileOptions: svelteHmrBoundary })
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Svelte's HMR wrapper resolves the component through a signal, which survives hydration only where
|
|
32
|
+
* the component is a plain child in a template. A layout or a page is not: the adapter's `Chain` enters
|
|
33
|
+
* chain members as dynamic components and through snippets, so a wrapper on one leaves the hydration
|
|
34
|
+
* cursor pointing at markup the wrapped component never claims. Svelte then throws the server's tree
|
|
35
|
+
* away and re-renders on the client - `hydration_mismatch` in the console, on first load, before any
|
|
36
|
+
* edit. Left on, the dev server quietly stops exercising hydration at all.
|
|
37
|
+
*
|
|
38
|
+
* Views keep hot-patching; route modules fall back to a full reload, which is what they want anyway
|
|
39
|
+
* (a route module carries the loader and meta the server ran, and re-running those is a navigation).
|
|
40
|
+
* Outside a nifra dev server nothing is announced and the answer is "no boundary" - a full reload is
|
|
41
|
+
* always correct, a mispatched tree is not.
|
|
42
|
+
*/
|
|
43
|
+
export function svelteHmrBoundary({ filename }: { filename: string }): { hmr: boolean } {
|
|
44
|
+
return { hmr: devHotComponent(filename) }
|
|
45
|
+
}
|
|
46
|
+
|
|
23
47
|
export function svelteBunPlugin(generate: "dom" | "ssr"): BunPlugin {
|
|
24
48
|
// Compiled scoped CSS per `.svelte` file (client build only) - read back by the virtual-module loader.
|
|
25
49
|
const cssByPath = new Map<string, string>()
|
|
@@ -31,10 +55,35 @@ export function svelteBunPlugin(generate: "dom" | "ssr"): BunPlugin {
|
|
|
31
55
|
build.onLoad({ filter: /\.svelte(\?|$)/ }, async (args) => {
|
|
32
56
|
const path = args.path.split("?")[0] ?? args.path
|
|
33
57
|
const source = await Bun.file(path).text()
|
|
58
|
+
// Per compile, not per registration: the CLI registers the SSR plugin before the dev server
|
|
59
|
+
// exists, and the flag is set by the dev server.
|
|
60
|
+
//
|
|
61
|
+
// Svelte's HMR is a compiler feature (`$.hmr` + a self-accepting `import.meta.hot.accept`), and
|
|
62
|
+
// it is only available under `dev`. `dev` is set on BOTH halves so the server and client builds
|
|
63
|
+
// stay the same Svelte, while `hmr` stays client-only, there being no `import.meta.hot` on the
|
|
64
|
+
// server. `nifra build` sets neither, so nothing here reaches a production bundle.
|
|
65
|
+
//
|
|
66
|
+
// The server half additionally needs Svelte's own runtime in its dev shape - `dev` output calls
|
|
67
|
+
// `push_element`, which reads state the runtime only records when ITS `DEV` is true. That flag
|
|
68
|
+
// resolves through `esm-env`, which falls back to `NODE_ENV` off the export conditions; the dev
|
|
69
|
+
// server sets it for exactly this reason. Getting that half wrong throws on the first element:
|
|
70
|
+
// `undefined is not an object (evaluating 'context.function[FILENAME]')`.
|
|
71
|
+
//
|
|
72
|
+
// `hmr` narrows further, to the app's own non-route components ({@link devHotComponent}).
|
|
73
|
+
// Svelte's wrapper replaces the component with a block that resolves it through a signal, and
|
|
74
|
+
// that survives hydration only where the component is a plain child in a template. A chain
|
|
75
|
+
// member is not: the adapter's `Chain` enters layouts and pages as dynamic components and
|
|
76
|
+
// through snippets, and a wrapper on either side of that leaves the hydration cursor pointing
|
|
77
|
+
// at markup the wrapped component never claims - the whole tree is then thrown away and
|
|
78
|
+
// re-rendered on the client, silently, before any edit. A view component is the boundary that
|
|
79
|
+
// works, and it is also the one worth patching.
|
|
80
|
+
const dev = devServerCompile()
|
|
34
81
|
const { js, css } = compile(source, {
|
|
35
82
|
generate: generate === "ssr" ? "server" : "client",
|
|
36
83
|
filename: path,
|
|
37
84
|
css: "external", // emit the scoped stylesheet separately (not injected into the JS)
|
|
85
|
+
dev,
|
|
86
|
+
hmr: dev && generate === "dom" && devHotComponent(path),
|
|
38
87
|
})
|
|
39
88
|
if (generate === "dom" && css?.code) {
|
|
40
89
|
cssByPath.set(path, css.code)
|
|
@@ -44,7 +93,7 @@ export function svelteBunPlugin(generate: "dom" | "ssr"): BunPlugin {
|
|
|
44
93
|
loader: "js",
|
|
45
94
|
}
|
|
46
95
|
}
|
|
47
|
-
return { contents: js.code, loader: "js" }
|
|
96
|
+
return { contents: rewriteSsrImports(js.code, path, generate), loader: "js" }
|
|
48
97
|
})
|
|
49
98
|
// Virtual CSS module: `<file>.svelte?svelte-css` → the compiled scoped stylesheet (css loader).
|
|
50
99
|
build.onResolve({ filter: /\?svelte-css$/ }, (args) => ({
|
package/src/query.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type QueryState,
|
|
6
|
-
} from "@nifrajs/web"
|
|
1
|
+
import type { QueryClient, QueryHandle, QueryState } from "@nifrajs/web"
|
|
2
|
+
// `/client`, not the root - these are DOM values, and the root's graph carries the
|
|
3
|
+
// server, which Vite's dev server evaluates rather than tree-shakes.
|
|
4
|
+
import { createQueryClient } from "@nifrajs/web/client"
|
|
7
5
|
/**
|
|
8
6
|
* `@nifrajs/web-svelte/query` - Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
|
|
9
7
|
* `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` - read it
|
package/src/router.ts
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server"
|
|
2
|
+
// `import type` + a local re-export, NOT `export type { … } from "@nifrajs/web"`: that form leaves a
|
|
3
|
+
// bare `import "@nifrajs/web"` in the output, which pulls the server graph into the browser under
|
|
4
|
+
// Vite's dev server. Sourced from the ROOT so the generated `RouteSearch` augmentation applies.
|
|
5
|
+
import type {
|
|
6
|
+
Blocker,
|
|
7
|
+
BlockerFunction,
|
|
8
|
+
BlockerState,
|
|
9
|
+
NavigateFunction,
|
|
10
|
+
NavigateOptions,
|
|
11
|
+
NavigateTargetInput,
|
|
12
|
+
} from "@nifrajs/web"
|
|
13
|
+
// `/client`, not the root - these are DOM values, and the root's graph carries the
|
|
14
|
+
// server, which Vite's dev server evaluates rather than tree-shakes.
|
|
2
15
|
import {
|
|
3
|
-
type Blocker,
|
|
4
|
-
type BlockerFunction,
|
|
5
16
|
getBrowserNavigate,
|
|
6
17
|
IDLE_BLOCKER,
|
|
7
|
-
type NavigateFunction,
|
|
8
|
-
type NavigateOptions,
|
|
9
|
-
type NavigateTargetInput,
|
|
10
18
|
registerBlocker,
|
|
11
19
|
resolveNavigate,
|
|
12
|
-
} from "@nifrajs/web"
|
|
20
|
+
} from "@nifrajs/web/client"
|
|
13
21
|
import { getContext } from "svelte"
|
|
14
22
|
/**
|
|
15
23
|
* `@nifrajs/web-svelte/router` - Svelte routing bindings over the agnostic `@nifrajs/web` history layer,
|
|
@@ -21,7 +29,7 @@ import { getContext } from "svelte"
|
|
|
21
29
|
*/
|
|
22
30
|
import { type Readable, readable } from "svelte/store"
|
|
23
31
|
|
|
24
|
-
export type { Blocker, BlockerFunction, BlockerState, NavigateFunction }
|
|
32
|
+
export type { Blocker, BlockerFunction, BlockerState, NavigateFunction }
|
|
25
33
|
|
|
26
34
|
// Must match the string key `Chain.svelte` passes to `setContext` (a string avoids a `.svelte` → `.ts`
|
|
27
35
|
// import that wouldn't resolve once the .svelte is copied to dist).
|