@nifrajs/web 2.1.0 → 2.3.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/README.md +3 -3
- package/dist/build-vite.d.ts +68 -0
- package/dist/build-vite.d.ts.map +1 -0
- package/dist/build-vite.js +433 -0
- package/dist/build-vite.js.map +1 -0
- package/dist/build.d.ts +174 -65
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +376 -135
- package/dist/build.js.map +1 -1
- package/dist/bun-dev-entry.d.ts +83 -0
- package/dist/bun-dev-entry.d.ts.map +1 -0
- package/dist/bun-dev-entry.js +136 -0
- package/dist/bun-dev-entry.js.map +1 -0
- package/dist/chunk-names.js +1 -1
- package/dist/client.d.ts +3 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +209 -27
- package/dist/client.js.map +1 -1
- package/dist/conformance.d.ts +2 -2
- package/dist/conformance.js +3 -3
- package/dist/deferred.d.ts +11 -11
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +16 -16
- package/dist/dev-error.d.ts +2 -2
- package/dist/dev-error.js +3 -3
- package/dist/dev-port.d.ts +36 -0
- package/dist/dev-port.d.ts.map +1 -0
- package/dist/dev-port.js +48 -0
- package/dist/dev-port.js.map +1 -0
- package/dist/dev.d.ts +62 -5
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +419 -60
- package/dist/dev.js.map +1 -1
- package/dist/draft.d.ts +44 -7
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +92 -9
- package/dist/draft.js.map +1 -1
- package/dist/fn-state.d.ts +56 -0
- package/dist/fn-state.d.ts.map +1 -0
- package/dist/fn-state.js +77 -0
- package/dist/fn-state.js.map +1 -0
- package/dist/fn.d.ts +111 -0
- package/dist/fn.d.ts.map +1 -0
- package/dist/fn.js +125 -0
- package/dist/fn.js.map +1 -0
- package/dist/fonts-google.d.ts +9 -9
- package/dist/fonts-google.js +10 -10
- package/dist/fonts.d.ts +7 -7
- package/dist/fonts.js +1 -1
- package/dist/forms.d.ts +8 -8
- package/dist/fs.d.ts +15 -2
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +8 -2
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +266 -74
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +869 -179
- package/dist/index.js.map +1 -1
- package/dist/internal/head-attributes.d.ts +15 -0
- package/dist/internal/head-attributes.d.ts.map +1 -0
- package/dist/internal/head-attributes.js +99 -0
- package/dist/internal/head-attributes.js.map +1 -0
- package/dist/internal/script-types.d.ts +14 -0
- package/dist/internal/script-types.d.ts.map +1 -0
- package/dist/internal/script-types.js +17 -0
- package/dist/internal/script-types.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +60 -0
- package/dist/internal/server-boundary.d.ts.map +1 -0
- package/dist/internal/server-boundary.js +174 -0
- package/dist/internal/server-boundary.js.map +1 -0
- package/dist/internal/timing-safe-equal.d.ts +16 -0
- package/dist/internal/timing-safe-equal.d.ts.map +1 -0
- package/dist/internal/timing-safe-equal.js +26 -0
- package/dist/internal/timing-safe-equal.js.map +1 -0
- package/dist/internal/vite-import.d.ts +63 -0
- package/dist/internal/vite-import.d.ts.map +1 -0
- package/dist/internal/vite-import.js +94 -0
- package/dist/internal/vite-import.js.map +1 -0
- package/dist/islands.d.ts +5 -5
- package/dist/islands.js +5 -5
- package/dist/isr.d.ts +15 -15
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +8 -21
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +12 -6
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +121 -40
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +136 -26
- package/dist/manifest.js.map +1 -1
- package/dist/module-graph.d.ts +91 -0
- package/dist/module-graph.d.ts.map +1 -0
- package/dist/module-graph.js +92 -0
- package/dist/module-graph.js.map +1 -0
- package/dist/navigation.d.ts +120 -5
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +37 -5
- package/dist/navigation.js.map +1 -1
- package/dist/plugins/css-modules.d.ts +3 -3
- package/dist/plugins/css-modules.d.ts.map +1 -1
- package/dist/plugins/css-modules.js +0 -0
- package/dist/plugins/css-modules.js.map +1 -1
- package/dist/plugins/kit.d.ts +8 -8
- package/dist/plugins/kit.js +9 -9
- package/dist/plugins/scss.js +3 -3
- package/dist/plugins/vite-leak-guard.d.ts +45 -0
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -0
- package/dist/plugins/vite-leak-guard.js +67 -0
- package/dist/plugins/vite-leak-guard.js.map +1 -0
- package/dist/plugins/vite-server-fn.d.ts +23 -0
- package/dist/plugins/vite-server-fn.d.ts.map +1 -0
- package/dist/plugins/vite-server-fn.js +51 -0
- package/dist/plugins/vite-server-fn.js.map +1 -0
- package/dist/plugins/vite-server-only.d.ts +49 -0
- package/dist/plugins/vite-server-only.d.ts.map +1 -0
- package/dist/plugins/vite-server-only.js +57 -0
- package/dist/plugins/vite-server-only.js.map +1 -0
- package/dist/prerender.d.ts +5 -5
- package/dist/prerender.js +7 -7
- package/dist/public-dir.d.ts +36 -0
- package/dist/public-dir.d.ts.map +1 -0
- package/dist/public-dir.js +89 -0
- package/dist/public-dir.js.map +1 -0
- package/dist/query.d.ts +10 -10
- package/dist/query.js +15 -15
- package/dist/route-manifest.d.ts +91 -0
- package/dist/route-manifest.d.ts.map +1 -0
- package/dist/route-manifest.js +128 -0
- package/dist/route-manifest.js.map +1 -0
- package/dist/router.d.ts +94 -22
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +170 -58
- package/dist/router.js.map +1 -1
- package/dist/search.d.ts +94 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +232 -0
- package/dist/search.js.map +1 -0
- package/dist/server-only.d.ts +6 -6
- package/dist/server-only.js +7 -7
- package/dist/service-worker.d.ts +56 -0
- package/dist/service-worker.d.ts.map +1 -0
- package/dist/service-worker.js +127 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/vite.d.ts +25 -17
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +75 -33
- package/dist/vite.js.map +1 -1
- package/package.json +48 -4
- package/server-manifest.d.ts +31 -0
- package/src/build-vite.ts +553 -0
- package/src/build.ts +563 -172
- package/src/bun-dev-entry.ts +184 -0
- package/src/chunk-names.ts +1 -1
- package/src/client.ts +221 -30
- package/src/conformance.ts +3 -3
- package/src/deferred.ts +17 -17
- package/src/dev-error.ts +3 -3
- package/src/dev-port.ts +63 -0
- package/src/dev.ts +468 -66
- package/src/draft.ts +124 -12
- package/src/fn-state.ts +107 -0
- package/src/fn.ts +199 -0
- package/src/fonts-google.ts +13 -13
- package/src/fonts.ts +7 -7
- package/src/forms.ts +8 -8
- package/src/fs.ts +24 -6
- package/src/index.ts +1174 -225
- package/src/internal/head-attributes.ts +109 -0
- package/src/internal/script-types.ts +18 -0
- package/src/internal/server-boundary.ts +187 -0
- package/src/internal/timing-safe-equal.ts +24 -0
- package/src/internal/vite-import.ts +92 -0
- package/src/islands.ts +7 -7
- package/src/isr.ts +18 -30
- package/src/llms-txt.ts +13 -5
- package/src/manifest.ts +264 -48
- package/src/module-graph.ts +146 -0
- package/src/navigation.ts +151 -5
- package/src/plugins/css-modules.ts +0 -0
- package/src/plugins/kit.ts +11 -11
- package/src/plugins/scss.ts +3 -3
- package/src/plugins/vite-leak-guard.ts +110 -0
- package/src/plugins/vite-server-fn.ts +67 -0
- package/src/plugins/vite-server-only.ts +75 -0
- package/src/prerender.ts +10 -10
- package/src/public-dir.ts +108 -0
- package/src/query.ts +22 -22
- package/src/route-manifest.ts +240 -0
- package/src/router.ts +288 -74
- package/src/search.ts +297 -0
- package/src/server-only.ts +7 -7
- package/src/service-worker.ts +165 -0
- package/src/vite.ts +103 -49
package/dist/plugins/kit.js
CHANGED
|
@@ -7,7 +7,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
* `@nifrajs/web/plugins` shared kit
|
|
10
|
+
* `@nifrajs/web/plugins` shared kit - the small infrastructure every CSS-bearing asset plugin reuses, so
|
|
11
11
|
* P1 (CSS Modules), P2 (SCSS), and any future P4 (LESS/Stylus/PostCSS) don't each re-implement the
|
|
12
12
|
* CSS→client-bundle wiring, the deterministic hash, or the optional-peer guard. Internal to
|
|
13
13
|
* `@nifrajs/web` (imported by the sibling plugin modules); not a public subpath.
|
|
@@ -27,7 +27,7 @@ function packageRootOf(startDir) {
|
|
|
27
27
|
break;
|
|
28
28
|
const parent = dirname(dir);
|
|
29
29
|
if (parent === dir) {
|
|
30
|
-
dir = startDir; // reached the filesystem root with no package.json
|
|
30
|
+
dir = startDir; // reached the filesystem root with no package.json - anchor on the file's own dir
|
|
31
31
|
break;
|
|
32
32
|
}
|
|
33
33
|
dir = parent;
|
|
@@ -36,11 +36,11 @@ function packageRootOf(startDir) {
|
|
|
36
36
|
return dir;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* A **package-root-relative**, forward-slashed form of an absolute path
|
|
39
|
+
* A **package-root-relative**, forward-slashed form of an absolute path - the input to {@link hash8} for
|
|
40
40
|
* any build-stable identifier (e.g. CSS-module scoped names). Anchoring on the file's nearest
|
|
41
41
|
* `package.json` (not the absolute path, not `process.cwd()`) makes the result independent of BOTH the
|
|
42
|
-
* machine's directory layout AND the working directory: the dom build and the ssr runtime
|
|
43
|
-
* different machines or from different cwds
|
|
42
|
+
* machine's directory layout AND the working directory: the dom build and the ssr runtime - even on
|
|
43
|
+
* different machines or from different cwds - derive the SAME relative path for a given file, so their
|
|
44
44
|
* scoped class maps always agree. (Hashing the absolute path would differ across CI/host; a
|
|
45
45
|
* cwd-relative path would silently desync if the SSR server started from a non-project-root cwd.)
|
|
46
46
|
*/
|
|
@@ -49,7 +49,7 @@ export function reproduciblePath(absolutePath) {
|
|
|
49
49
|
return relative(root, absolutePath).replaceAll("\\", "/");
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* Deterministic 8-hex hash (djb2/xor). Stable across builds
|
|
52
|
+
* Deterministic 8-hex hash (djb2/xor). Stable across builds - no `Date.now`/`Math.random` - so build
|
|
53
53
|
* output is reproducible. The single hash implementation behind CSS-module scoped names (and a drop-in
|
|
54
54
|
* for any SFC scope id).
|
|
55
55
|
*/
|
|
@@ -62,7 +62,7 @@ export function hash8(input) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Wire the virtual-CSS-module handlers onto `build` for `namespace`, returning an {@link StylesheetEmitter}.
|
|
64
64
|
* The `namespace` must be a plain identifier (letters/`-`); it's used verbatim as the import suffix and
|
|
65
|
-
* the Bun namespace. Only the `"dom"` build should emit CSS
|
|
65
|
+
* the Bun namespace. Only the `"dom"` build should emit CSS - the `"ssr"` build ships no stylesheet.
|
|
66
66
|
*/
|
|
67
67
|
export function createStylesheetEmitter(build, namespace) {
|
|
68
68
|
const suffix = `?${namespace}`;
|
|
@@ -85,7 +85,7 @@ export function createStylesheetEmitter(build, namespace) {
|
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Load an optional peer compiler at build time, throwing a consistent, actionable install-hint error if
|
|
88
|
-
* it's absent
|
|
88
|
+
* it's absent - the `@vue/compiler-sfc` peer pattern, centralized. Build-time only, so the dynamic
|
|
89
89
|
* `import` (which keeps the peer out of the package's hard dependencies) is correct here.
|
|
90
90
|
*/
|
|
91
91
|
export async function requirePeer(specifier, hint) {
|
|
@@ -93,7 +93,7 @@ export async function requirePeer(specifier, hint) {
|
|
|
93
93
|
return (await import(__rewriteRelativeImportExtension(specifier)));
|
|
94
94
|
}
|
|
95
95
|
catch (err) {
|
|
96
|
-
// Only a genuine resolution failure means "not installed"
|
|
96
|
+
// Only a genuine resolution failure means "not installed" - surface anything else (a corrupt
|
|
97
97
|
// install, a native-binding error, a throw at module top-level) as the real error rather than
|
|
98
98
|
// masking it behind a misleading "please install it" hint.
|
|
99
99
|
if (err.code === "ERR_MODULE_NOT_FOUND") {
|
package/dist/plugins/scss.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nifrajs/web/plugins/scss`
|
|
2
|
+
* `@nifrajs/web/plugins/scss` - a SASS/SCSS (`*.scss`, `*.sass`) Bun plugin, in its OWN module so the SSR
|
|
3
3
|
* preload registers it BEFORE any Sass file loads. Mirrors the `@nifrajs/web-vue/plugin` seam: pass
|
|
4
4
|
* `"dom"` for the client bundle (`buildClient({ plugins: [...] })`) and preload `"ssr"` for the server
|
|
5
5
|
* (`bun --preload`).
|
|
6
6
|
*
|
|
7
|
-
* The Dart Sass compiler is an **optional peer** (`sass`, or the faster `sass-embedded`
|
|
7
|
+
* The Dart Sass compiler is an **optional peer** (`sass`, or the faster `sass-embedded` - same API), not
|
|
8
8
|
* a hard dependency: it's loaded on first Sass file and fails loud with an install hint if absent. Pass
|
|
9
9
|
* your own `compiler` to override (e.g. `sass-embedded`, or a stub in tests).
|
|
10
10
|
*
|
|
11
11
|
* Composes with CSS Modules: a `*.module.scss` / `*.module.sass` file is compiled to CSS and then run
|
|
12
12
|
* through the same scoped-class transform as `@nifrajs/web/plugins/css-modules`, so its `import styles`
|
|
13
13
|
* yields the `{ original: scoped }` map (SSR/dom class-map parity included). A plain `*.scss` is a
|
|
14
|
-
* side-effect import
|
|
14
|
+
* side-effect import - its CSS is bundled (dom) and it resolves to an empty module (ssr).
|
|
15
15
|
*/
|
|
16
16
|
import { dirname } from "node:path";
|
|
17
17
|
import { pathToFileURL } from "node:url";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type RollupBundleLike } from "../module-graph.js";
|
|
2
|
+
/**
|
|
3
|
+
* The Rollup plugin-context slice this uses: `getModuleInfo` for a module's resolved imports, and `error`
|
|
4
|
+
* to fail the build. Typed structurally so the file needs no `rollup`/`vite` type dependency.
|
|
5
|
+
*/
|
|
6
|
+
interface RollupPluginContext {
|
|
7
|
+
getModuleInfo(id: string): {
|
|
8
|
+
readonly importedIds?: readonly string[];
|
|
9
|
+
readonly dynamicallyImportedIds?: readonly string[];
|
|
10
|
+
} | null;
|
|
11
|
+
/**
|
|
12
|
+
* Takes an `Error`, never a string.
|
|
13
|
+
*
|
|
14
|
+
* Handed a string, rolldown synthesizes its own error type and calls `Error.captureStackTrace` on a
|
|
15
|
+
* plain object - which Bun rejects with "First argument must be an Error object", and THAT becomes the
|
|
16
|
+
* build failure. The guard still fires, but its message is replaced by an internal one naming nothing,
|
|
17
|
+
* so a real client leak reports as a stack-trace complaint. Passing an Error skips that construction
|
|
18
|
+
* entirely and the message survives.
|
|
19
|
+
*/
|
|
20
|
+
error(error: Error): never;
|
|
21
|
+
}
|
|
22
|
+
/** The minimal Rollup plugin shape this returns - `generateBundle` bound to the plugin context. */
|
|
23
|
+
export interface LeakGuardPlugin {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/**
|
|
26
|
+
* The finding that failed the build, or `undefined` when the bundle was clean.
|
|
27
|
+
*
|
|
28
|
+
* Recorded as well as thrown because the throw does not reliably survive. A plugin error is handed to
|
|
29
|
+
* the bundler, which builds its own error to report it, and on some Bun + rolldown combinations that
|
|
30
|
+
* construction itself fails (`Error.captureStackTrace` rejecting a non-Error) - so what surfaces is an
|
|
31
|
+
* internal complaint naming neither the leaked module nor its import chain. A caller that owns the
|
|
32
|
+
* build can read this afterwards and raise the real message from its own code, where nothing can
|
|
33
|
+
* rewrite it. A leak is the one failure that must stay legible: the whole point is naming what
|
|
34
|
+
* reached the browser.
|
|
35
|
+
*/
|
|
36
|
+
leak?: string;
|
|
37
|
+
generateBundle(this: RollupPluginContext, options: unknown, bundle: RollupBundleLike): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A Vite/Rollup plugin that fails the build when server-only code or a `node:` builtin reaches the client
|
|
41
|
+
* bundle - the same two guards, and the same error messages, as nifra's Bun production build.
|
|
42
|
+
*/
|
|
43
|
+
export declare function viteLeakGuard(): LeakGuardPlugin;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=vite-leak-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-leak-guard.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-leak-guard.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE5E;;;GAGG;AACH,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG;QACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QACxC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KACpD,GAAG,IAAI,CAAA;IACR;;;;;;;;OAQG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAA;CAC3B;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAA;CAC5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,eAAe,CAyB/C"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/web/plugins/vite-leak-guard` - nifra's two client-leak guards, for a Vite/Rollup production
|
|
3
|
+
* build.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* nifra's production default is Bun, and that is not changing: a Vite/Rollup production build inherits
|
|
8
|
+
* Rollup's build profile and gives up the Bun-native advantage nifra competes on. But some apps depend on
|
|
9
|
+
* a Vite-only transform that has no Bun equivalent, and for those a Vite production build is the escape
|
|
10
|
+
* hatch. The moment that hatch exists, the two client-leak guards have to come with it - they are security
|
|
11
|
+
* guards, not lints: one stops secrets and database access from shipping to a browser. A second production
|
|
12
|
+
* pipeline arriving WITHOUT them, or with a hastily re-implemented "mostly ported" copy, is the failure the
|
|
13
|
+
* neutral module-graph seam was built to prevent.
|
|
14
|
+
*
|
|
15
|
+
* So this is not a second implementation. The detection logic (`detectNodeBuiltinsInClient`,
|
|
16
|
+
* `detectServerOnlyInClient`) and the failure MESSAGES (`formatNodeBuiltinLeak`, `formatServerOnlyLeak`)
|
|
17
|
+
* are the exact same functions the Bun build calls. This plugin only adapts Rollup's bundle shape into the
|
|
18
|
+
* neutral `ClientModuleGraph` (`fromRollupBundle`) and runs them. A leak reads identically whichever
|
|
19
|
+
* bundler produced it, and there is one place to change if a guard changes.
|
|
20
|
+
*
|
|
21
|
+
* ## How
|
|
22
|
+
*
|
|
23
|
+
* In `generateBundle` the whole client graph is available: each output chunk lists its `moduleIds`, and
|
|
24
|
+
* `this.getModuleInfo(id)` gives each module's resolved import edges. That is exactly what
|
|
25
|
+
* `fromRollupBundle` needs. Findings fail the build through Rollup's `this.error`, so the message lands in
|
|
26
|
+
* the build output the same way a Bun-build throw does.
|
|
27
|
+
*
|
|
28
|
+
* Add it to the `plugins` of a Vite production build (the LAST plugin, so it sees the final graph):
|
|
29
|
+
*
|
|
30
|
+
* // vite.config.ts (production client build)
|
|
31
|
+
* import { viteLeakGuard } from "@nifrajs/web/plugins/vite-leak-guard"
|
|
32
|
+
* export default { build: { rollupOptions: { plugins: [viteLeakGuard()] } } }
|
|
33
|
+
*/
|
|
34
|
+
import { detectNodeBuiltinsInClient, detectServerOnlyInClient, formatNodeBuiltinLeak, formatServerOnlyLeak, } from "../build.js";
|
|
35
|
+
import { fromRollupBundle } from "../module-graph.js";
|
|
36
|
+
/**
|
|
37
|
+
* A Vite/Rollup plugin that fails the build when server-only code or a `node:` builtin reaches the client
|
|
38
|
+
* bundle - the same two guards, and the same error messages, as nifra's Bun production build.
|
|
39
|
+
*/
|
|
40
|
+
export function viteLeakGuard() {
|
|
41
|
+
const plugin = {
|
|
42
|
+
name: "nifra:leak-guard",
|
|
43
|
+
generateBundle(_options, bundle) {
|
|
44
|
+
// Resolved import edges per module, straight from Rollup's graph. Dynamic imports count too: a
|
|
45
|
+
// `node:`/server-only module reached only via `import()` still ships to the browser.
|
|
46
|
+
const importsOf = (id) => {
|
|
47
|
+
const info = this.getModuleInfo(id);
|
|
48
|
+
if (info === null)
|
|
49
|
+
return [];
|
|
50
|
+
return [...(info.importedIds ?? []), ...(info.dynamicallyImportedIds ?? [])];
|
|
51
|
+
};
|
|
52
|
+
const graph = fromRollupBundle(bundle, importsOf);
|
|
53
|
+
// Node-builtin guard first, then server-only - the same order as the Bun build, so the first error a
|
|
54
|
+
// dev sees is the same across pipelines when a module trips both.
|
|
55
|
+
const leak = formatNodeBuiltinLeak(detectNodeBuiltinsInClient(graph)) ??
|
|
56
|
+
formatServerOnlyLeak(detectServerOnlyInClient(graph));
|
|
57
|
+
if (leak === undefined)
|
|
58
|
+
return;
|
|
59
|
+
// Record before throwing: the throw is what fails the build for a standalone user of this plugin,
|
|
60
|
+
// and the record is what lets a caller that owns the build re-raise the real message (see `leak`).
|
|
61
|
+
plugin.leak = leak;
|
|
62
|
+
this.error(new Error(leak));
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
return plugin;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=vite-leak-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-leak-guard.js","sourceRoot":"","sources":["../../src/plugins/vite-leak-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAA;AAyC5E;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAoB;QAC9B,IAAI,EAAE,kBAAkB;QACxB,cAAc,CAAC,QAAQ,EAAE,MAAM;YAC7B,+FAA+F;YAC/F,qFAAqF;YACrF,MAAM,SAAS,GAAG,CAAC,EAAU,EAAqB,EAAE;gBAClD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;gBACnC,IAAI,IAAI,KAAK,IAAI;oBAAE,OAAO,EAAE,CAAA;gBAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,CAAA;YAC9E,CAAC,CAAA;YACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;YACjD,qGAAqG;YACrG,kEAAkE;YAClE,MAAM,IAAI,GACR,qBAAqB,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;gBACxD,oBAAoB,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAA;YACvD,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAM;YAC9B,kGAAkG;YAClG,mGAAmG;YACnG,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;YAClB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QAC7B,CAAC;KACF,CAAA;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The slice of a Vite/Rollup plugin this returns. Structural, so `vite` stays an optional peer. */
|
|
2
|
+
export interface ServerFnStubPlugin {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly enforce: "pre";
|
|
5
|
+
/** Applied only to the CLIENT build; the server build keeps the real module. */
|
|
6
|
+
readonly applyToEnvironment?: (environment: {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
}) => boolean;
|
|
9
|
+
transform(code: string, id: string, options?: {
|
|
10
|
+
readonly ssr?: boolean;
|
|
11
|
+
}): {
|
|
12
|
+
code: string;
|
|
13
|
+
map: null;
|
|
14
|
+
} | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Replace every `*.fn` module with its client stubs.
|
|
18
|
+
*
|
|
19
|
+
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
20
|
+
* SSR build, because the server is what actually runs these functions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function viteServerFnStub(): ServerFnStubPlugin;
|
|
23
|
+
//# sourceMappingURL=vite-server-fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-server-fn.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-server-fn.ts"],"names":[],"mappings":"AA2BA,oGAAoG;AACpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,gFAAgF;IAChF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjF,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtC;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAiBrD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/web/plugins/vite-server-fn` - the Vite half of the server-function client transform.
|
|
3
|
+
*
|
|
4
|
+
* A `*.fn` module must never reach a browser: it holds the function bodies and everything they import.
|
|
5
|
+
* The Bun build replaces each one with client stubs, and this does the same for Vite, in both the dev
|
|
6
|
+
* server and the production build.
|
|
7
|
+
*
|
|
8
|
+
* ## Why the generation is not in here
|
|
9
|
+
*
|
|
10
|
+
* The stub text comes from `internal/server-boundary.ts`, shared verbatim with the Bun plugin. Two
|
|
11
|
+
* implementations of "what a stub looks like" would drift, and the drift would be invisible until a
|
|
12
|
+
* client that worked in dev 404'd in production - the exact shape of failure this codebase has already
|
|
13
|
+
* paid for once. A parity test asserts both pipelines emit the same bytes.
|
|
14
|
+
*
|
|
15
|
+
* ## Why `transform` and not `load`
|
|
16
|
+
*
|
|
17
|
+
* Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
|
|
18
|
+
* `transform` receives the source Vite already read, and returning replacement code discards the
|
|
19
|
+
* original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts this
|
|
20
|
+
* ahead of framework transforms, which have no business seeing server code either.
|
|
21
|
+
*/
|
|
22
|
+
import { generateServerFnStub, SERVER_FN_MODULE, serverFnNamespace, } from "../internal/server-boundary.js";
|
|
23
|
+
/** Strips a Vite id's `?query` and `#hash` so the suffix test sees a plain path. */
|
|
24
|
+
const bare = (id) => id.split("?")[0]?.split("#")[0] ?? id;
|
|
25
|
+
/**
|
|
26
|
+
* Replace every `*.fn` module with its client stubs.
|
|
27
|
+
*
|
|
28
|
+
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
29
|
+
* SSR build, because the server is what actually runs these functions.
|
|
30
|
+
*/
|
|
31
|
+
export function viteServerFnStub() {
|
|
32
|
+
return {
|
|
33
|
+
name: "nifra:server-fn-stub",
|
|
34
|
+
enforce: "pre",
|
|
35
|
+
// Vite 6+ environments: the SSR environment must keep the real module. Older Vite ignores this and
|
|
36
|
+
// relies on nifra only registering the plugin on the client side.
|
|
37
|
+
applyToEnvironment: (environment) => environment.name === "client",
|
|
38
|
+
transform(code, id, options) {
|
|
39
|
+
// Vite 5 ignores `applyToEnvironment` but supplies this transform context for SSR.
|
|
40
|
+
if (options?.ssr === true)
|
|
41
|
+
return null;
|
|
42
|
+
const path = bare(id);
|
|
43
|
+
if (!SERVER_FN_MODULE.test(path))
|
|
44
|
+
return null;
|
|
45
|
+
// `map: null` is honest rather than lazy: the stub shares no lines with the source, so any
|
|
46
|
+
// mapping would point a debugger at unrelated code.
|
|
47
|
+
return { code: generateServerFnStub(code, serverFnNamespace(path)), map: null };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=vite-server-fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-server-fn.js","sourceRoot":"","sources":["../../src/plugins/vite-server-fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gCAAgC,CAAA;AAevC,oFAAoF;AACpF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,KAAK;QACd,mGAAmG;QACnG,kEAAkE;QAClE,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;QAClE,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO;YACzB,mFAAmF;YACnF,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC7C,2FAA2F;YAC3F,oDAAoD;YACpD,OAAO,EAAE,IAAI,EAAE,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACjF,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
|
|
3
|
+
*
|
|
4
|
+
* A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
|
|
5
|
+
* subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
|
|
6
|
+
* for Vite, in both the dev server and the production build.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this exists separately
|
|
9
|
+
*
|
|
10
|
+
* The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
|
|
11
|
+
* the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
|
|
12
|
+
* and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
|
|
13
|
+
* worse than none, because the file name reads as protection everywhere.
|
|
14
|
+
*
|
|
15
|
+
* `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
|
|
16
|
+
* cannot transform. `.server` now matches it.
|
|
17
|
+
*
|
|
18
|
+
* ## Why `transform` and not `load`
|
|
19
|
+
*
|
|
20
|
+
* Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
|
|
21
|
+
* `transform` receives the source Vite already read, and returning replacement code discards the
|
|
22
|
+
* original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
|
|
23
|
+
* this ahead of framework transforms, which have no business seeing server code either.
|
|
24
|
+
*/
|
|
25
|
+
import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement } from "../internal/server-boundary.js";
|
|
26
|
+
export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
|
|
27
|
+
/** The slice of a Vite/Rollup plugin this returns. Structural, so `vite` stays an optional peer. */
|
|
28
|
+
export interface ServerOnlyEmptyPlugin {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly enforce: "pre";
|
|
31
|
+
/** Applied only to the CLIENT build; the server build keeps the real module. */
|
|
32
|
+
readonly applyToEnvironment?: (environment: {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
}) => boolean;
|
|
35
|
+
transform(code: string, id: string, options?: {
|
|
36
|
+
readonly ssr?: boolean;
|
|
37
|
+
}): {
|
|
38
|
+
code: string;
|
|
39
|
+
map: null;
|
|
40
|
+
} | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Empty every `*.server` module in the client build.
|
|
44
|
+
*
|
|
45
|
+
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
46
|
+
* SSR build, where the real module is what runs.
|
|
47
|
+
*/
|
|
48
|
+
export declare function viteServerOnlyEmpty(): ServerOnlyEmptyPlugin;
|
|
49
|
+
//# sourceMappingURL=vite-server-only.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-server-only.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,gCAAgC,CAAA;AAIvC,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAEjF,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,gFAAgF;IAChF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjF,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtC;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,qBAAqB,CAiB3D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
|
|
3
|
+
*
|
|
4
|
+
* A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
|
|
5
|
+
* subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
|
|
6
|
+
* for Vite, in both the dev server and the production build.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this exists separately
|
|
9
|
+
*
|
|
10
|
+
* The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
|
|
11
|
+
* the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
|
|
12
|
+
* and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
|
|
13
|
+
* worse than none, because the file name reads as protection everywhere.
|
|
14
|
+
*
|
|
15
|
+
* `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
|
|
16
|
+
* cannot transform. `.server` now matches it.
|
|
17
|
+
*
|
|
18
|
+
* ## Why `transform` and not `load`
|
|
19
|
+
*
|
|
20
|
+
* Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
|
|
21
|
+
* `transform` receives the source Vite already read, and returning replacement code discards the
|
|
22
|
+
* original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
|
|
23
|
+
* this ahead of framework transforms, which have no business seeing server code either.
|
|
24
|
+
*/
|
|
25
|
+
import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement, } from "../internal/server-boundary.js";
|
|
26
|
+
// Re-exported so a caller wiring its own bundler gets the matcher from the same owner the pipelines
|
|
27
|
+
// use, rather than writing a fifth copy of the regex.
|
|
28
|
+
export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
|
|
29
|
+
/** Strips a Vite id's `?query` and `#hash` so the suffix test sees a plain path. */
|
|
30
|
+
const bare = (id) => id.split("?")[0]?.split("#")[0] ?? id;
|
|
31
|
+
/**
|
|
32
|
+
* Empty every `*.server` module in the client build.
|
|
33
|
+
*
|
|
34
|
+
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
35
|
+
* SSR build, where the real module is what runs.
|
|
36
|
+
*/
|
|
37
|
+
export function viteServerOnlyEmpty() {
|
|
38
|
+
return {
|
|
39
|
+
name: "nifra:server-only-empty",
|
|
40
|
+
enforce: "pre",
|
|
41
|
+
// Vite 6+ environments: the SSR environment must keep the real module. Older Vite ignores this and
|
|
42
|
+
// relies on nifra only registering the plugin on the client side.
|
|
43
|
+
applyToEnvironment: (environment) => environment.name === "client",
|
|
44
|
+
transform(code, id, options) {
|
|
45
|
+
// Vite 5 ignores `applyToEnvironment` but supplies this transform context for SSR.
|
|
46
|
+
if (options?.ssr === true)
|
|
47
|
+
return null;
|
|
48
|
+
const path = bare(id);
|
|
49
|
+
if (!SERVER_ONLY_MODULE.test(path))
|
|
50
|
+
return null;
|
|
51
|
+
// `map: null` is honest rather than lazy: the replacement shares no lines with the source, so any
|
|
52
|
+
// mapping would point a debugger at unrelated code.
|
|
53
|
+
return { code: viteServerOnlyReplacement(code), map: null };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=vite-server-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-server-only.js","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAA;AAEvC,oGAAoG;AACpG,sDAAsD;AACtD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAejF,oFAAoF;AACpF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,KAAK;QACd,mGAAmG;QACnG,kEAAkE;QAClE,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;QAClE,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO;YACzB,mFAAmF;YACnF,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC/C,kGAAkG;YAClG,oDAAoD;YACpD,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QAC7D,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/prerender.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RouteEntry } from "./manifest.js";
|
|
2
|
-
/** Minimal app surface the driver needs
|
|
2
|
+
/** Minimal app surface the driver needs - just a fetch handler (a built `createWebApp`). */
|
|
3
3
|
export interface PrerenderApp {
|
|
4
4
|
fetch(req: Request): Response | Promise<Response>;
|
|
5
5
|
}
|
|
@@ -33,7 +33,7 @@ export interface PrerenderResult {
|
|
|
33
33
|
readonly path: string;
|
|
34
34
|
readonly reason: string;
|
|
35
35
|
}[];
|
|
36
|
-
/** Per dynamic route pattern, its `getStaticPaths` `fallback` (`"ssr"` default)
|
|
36
|
+
/** Per dynamic route pattern, its `getStaticPaths` `fallback` (`"ssr"` default) - so the deploy
|
|
37
37
|
* layer knows whether unlisted paths should hit the worker (`"ssr"`) or 404 (`"404"`). */
|
|
38
38
|
readonly fallbacks: Readonly<Record<string, "ssr" | "404">>;
|
|
39
39
|
}
|
|
@@ -44,7 +44,7 @@ export declare function htmlFileFor(pattern: string): string;
|
|
|
44
44
|
export declare function dataFileFor(pattern: string): string;
|
|
45
45
|
/**
|
|
46
46
|
* Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
|
|
47
|
-
* (so the app references the hashed client entry). Returns a report of what was emitted vs skipped
|
|
47
|
+
* (so the app references the hashed client entry). Returns a report of what was emitted vs skipped -
|
|
48
48
|
* the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
|
|
49
49
|
* worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
|
|
50
50
|
* non-opted/dynamic route is recorded in `skipped` and the build continues.
|
|
@@ -59,7 +59,7 @@ export interface CloudflarePagesRoutes {
|
|
|
59
59
|
}
|
|
60
60
|
export interface CloudflarePagesRoutesOptions {
|
|
61
61
|
/** Prerendered request paths (e.g. `prerenderRoutes(...).prerendered.map(p => p.path)`, or the
|
|
62
|
-
* build's `prerendered.json`). Each is excluded from the worker
|
|
62
|
+
* build's `prerendered.json`). Each is excluded from the worker - plus its static `_data.json`. */
|
|
63
63
|
readonly prerendered: readonly string[];
|
|
64
64
|
/** Extra globs to keep OFF the worker (CDN-served). Default `["/assets/*"]` (the hashed bundle). */
|
|
65
65
|
readonly staticGlobs?: readonly string[];
|
|
@@ -71,7 +71,7 @@ export interface CloudflarePagesRoutesOptions {
|
|
|
71
71
|
*
|
|
72
72
|
* ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
|
|
73
73
|
* costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
|
|
74
|
-
* `/blog/*`) instead of listing every path
|
|
74
|
+
* `/blog/*`) instead of listing every path - pass those via `staticGlobs` and a smaller `prerendered`.
|
|
75
75
|
*/
|
|
76
76
|
export declare function cloudflarePagesRoutes(options: CloudflarePagesRoutesOptions): CloudflarePagesRoutes;
|
|
77
77
|
//# sourceMappingURL=prerender.d.ts.map
|
package/dist/prerender.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Build-time SSG driver. Prerendering is framework-agnostic by construction: a nifra SSR page is
|
|
3
3
|
* "just `app.fetch(GET)` returning a streamed `Response`", so prerendering = drive that same handler
|
|
4
4
|
* at build with a synthetic request, drain the body to bytes, and write the HTML to disk. No adapter
|
|
5
|
-
* (React/Solid/Vue/Preact/Svelte) is touched
|
|
5
|
+
* (React/Solid/Vue/Preact/Svelte) is touched - this sits entirely above the render seam.
|
|
6
6
|
*
|
|
7
7
|
* Handles **static** routes (opt in via `export const prerender = true`) and **dynamic** `:param`
|
|
8
8
|
* routes (enumerate concrete params via `export const getStaticPaths`). Catch-all/wildcard (`*`)
|
|
@@ -28,7 +28,7 @@ const outputPath = (root, file) => {
|
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
30
|
* Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
|
|
31
|
-
* (so the app references the hashed client entry). Returns a report of what was emitted vs skipped
|
|
31
|
+
* (so the app references the hashed client entry). Returns a report of what was emitted vs skipped -
|
|
32
32
|
* the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
|
|
33
33
|
* worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
|
|
34
34
|
* non-opted/dynamic route is recorded in `skipped` and the build continues.
|
|
@@ -44,7 +44,7 @@ export async function prerenderRoutes(options) {
|
|
|
44
44
|
const renderPath = async (path) => {
|
|
45
45
|
const res = await options.app.fetch(new Request(`${origin}${path}`));
|
|
46
46
|
if (!res.ok) {
|
|
47
|
-
// A failed render is a per-route skip, not a build failure
|
|
47
|
+
// A failed render is a per-route skip, not a build failure - surfaced for the caller to log.
|
|
48
48
|
skipped.push({ path, reason: `render returned HTTP ${res.status}` });
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
@@ -59,7 +59,7 @@ export async function prerenderRoutes(options) {
|
|
|
59
59
|
writeFileSync(abs, html);
|
|
60
60
|
// Also emit the loader data as a static `_data.json` next to it, so a client soft-nav into this
|
|
61
61
|
// route fetches a file instead of hitting the worker. Only when the data-mode response is plain
|
|
62
|
-
// JSON
|
|
62
|
+
// JSON - a deferred loader answers with NDJSON, which the static fast-path doesn't cover (the
|
|
63
63
|
// client falls back to the worker for those).
|
|
64
64
|
let dataFile;
|
|
65
65
|
const dataRes = await options.app.fetch(new Request(`${origin}${path}`, { headers: { [DATA_HEADER]: "1" } }));
|
|
@@ -76,13 +76,13 @@ export async function prerenderRoutes(options) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
79
|
-
await dataRes.body?.cancel(); // not emitting (non-OK or NDJSON)
|
|
79
|
+
await dataRes.body?.cancel(); // not emitting (non-OK or NDJSON) - release the stream
|
|
80
80
|
}
|
|
81
81
|
prerendered.push({ path, file, bytes: html.length, ...(dataFile ? { dataFile } : {}) });
|
|
82
82
|
};
|
|
83
83
|
for (const route of options.routes) {
|
|
84
84
|
if (route.pattern.includes("*")) {
|
|
85
|
-
skipped.push({ path: route.pattern, reason: "catch-all/wildcard route
|
|
85
|
+
skipped.push({ path: route.pattern, reason: "catch-all/wildcard route - not supported" });
|
|
86
86
|
continue;
|
|
87
87
|
}
|
|
88
88
|
const mod = await route.load();
|
|
@@ -126,7 +126,7 @@ export async function prerenderRoutes(options) {
|
|
|
126
126
|
*
|
|
127
127
|
* ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
|
|
128
128
|
* costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
|
|
129
|
-
* `/blog/*`) instead of listing every path
|
|
129
|
+
* `/blog/*`) instead of listing every path - pass those via `staticGlobs` and a smaller `prerendered`.
|
|
130
130
|
*/
|
|
131
131
|
export function cloudflarePagesRoutes(options) {
|
|
132
132
|
const exclude = [...(options.staticGlobs ?? ["/assets/*"])];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** How long each subtree may be cached. Content-hashed bundle output can be immutable; a
|
|
2
|
+
* user-authored file keeps its name across deploys, so it gets a day and a revalidation. */
|
|
3
|
+
export interface PublicDirCache {
|
|
4
|
+
/** `cache-control` for content-hashed assets (default immutable, one year). */
|
|
5
|
+
readonly hashed?: string;
|
|
6
|
+
/** `cache-control` for everything else under `public/` (default one day). */
|
|
7
|
+
readonly assets?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ServePublicDirOptions {
|
|
10
|
+
/** Absolute path of the directory to serve. */
|
|
11
|
+
readonly dir: string;
|
|
12
|
+
/** URL prefix whose files are content-hashed and may be cached immutably (default `"/assets/"`). */
|
|
13
|
+
readonly hashedPrefix?: string;
|
|
14
|
+
readonly cache?: PublicDirCache;
|
|
15
|
+
/** Optional encoded URL-path allowlist. When present, route misses avoid a filesystem probe. */
|
|
16
|
+
readonly files?: ReadonlySet<string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a URL pathname to an absolute path **confined** to `root`, or `undefined` if it escapes.
|
|
20
|
+
*
|
|
21
|
+
* This is a user-path-to-filesystem sink, which makes it the one part of this feature with a security
|
|
22
|
+
* consequence. Decode first (`%2e%2e%2f` is `../`), then normalize, then verify the result is still
|
|
23
|
+
* under `root` by prefix - checking the *resolved* path rather than scanning the input for `..`,
|
|
24
|
+
* because a blocklist over encodings is exactly the kind of check that gets bypassed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolvePublicPath(root: string, pathname: string): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Build a static-file handler for `dir`.
|
|
29
|
+
*
|
|
30
|
+
* Returns `undefined` on any miss so the caller **falls through to routing** - a static probe must
|
|
31
|
+
* never shadow a route. That ordering is also why a `routes/robots.txt.tsx` beats a
|
|
32
|
+
* `public/robots.txt` only if the caller checks routes first; the documented precedence is that the
|
|
33
|
+
* static probe runs first, so `public/` wins, and an app wanting the route should not ship both.
|
|
34
|
+
*/
|
|
35
|
+
export declare function servePublicDir(options: ServePublicDirOptions): (request: Request) => Promise<Response | undefined>;
|
|
36
|
+
//# sourceMappingURL=public-dir.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-dir.d.ts","sourceRoot":"","sources":["../src/public-dir.ts"],"names":[],"mappings":"AAiBA;4FAC4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,cAAc,CAAA;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAapF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAmCrD"}
|