@nifrajs/web 3.2.0 → 3.5.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/build-plan.d.ts +17 -0
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js +7 -1
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts +14 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +29 -4
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +57 -15
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +115 -0
- package/dist/client.js.map +1 -1
- package/dist/css-contract.d.ts +21 -0
- package/dist/css-contract.d.ts.map +1 -0
- package/dist/css-contract.js +28 -0
- package/dist/css-contract.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +3 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +11 -6
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/page-execution.d.ts +57 -0
- package/dist/internal/page-execution.d.ts.map +1 -0
- package/dist/internal/page-execution.js +555 -0
- package/dist/internal/page-execution.js.map +1 -0
- package/dist/internal/render-document.d.ts +12 -2
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +28 -5
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/request-url.d.ts +3 -0
- package/dist/internal/request-url.d.ts.map +1 -0
- package/dist/internal/request-url.js +14 -0
- package/dist/internal/request-url.js.map +1 -0
- package/dist/internal/web-app.d.ts +20 -1
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +37 -734
- package/dist/internal/web-app.js.map +1 -1
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +39 -4
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts +3 -0
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +6 -3
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +69 -44
- package/dist/public-dir.js.map +1 -1
- package/dist/trusted-html.d.ts +34 -0
- package/dist/trusted-html.d.ts.map +1 -0
- package/dist/trusted-html.js +35 -0
- package/dist/trusted-html.js.map +1 -0
- package/package.json +3 -3
- package/src/build-plan.ts +23 -1
- package/src/build-vite.ts +47 -4
- package/src/build.ts +69 -6
- package/src/client.ts +140 -0
- package/src/css-contract.ts +44 -0
- package/src/index.ts +15 -0
- package/src/internal/codegen.ts +20 -6
- package/src/internal/page-execution.ts +761 -0
- package/src/internal/render-document.ts +39 -6
- package/src/internal/request-url.ts +14 -0
- package/src/internal/web-app.ts +68 -835
- package/src/isr.ts +42 -3
- package/src/llms-txt.ts +16 -4
- package/src/manifest.ts +1 -1
- package/src/public-dir.ts +70 -45
- package/src/trusted-html.ts +44 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How framework-owned stylesheets are made active during the first document load.
|
|
3
|
+
*
|
|
4
|
+
* `blocking` is the backwards-compatible default. `deferred` is intended for an aggregate
|
|
5
|
+
* stylesheet: the document fetches it with `media="print"`, then the generated client promotes it
|
|
6
|
+
* to `media="all"` after the resource settles and before mounting the app.
|
|
7
|
+
*/
|
|
8
|
+
export type CssLoadingMode = "blocking" | "deferred"
|
|
9
|
+
|
|
10
|
+
/** The default remains the ordinary render-blocking stylesheet link. */
|
|
11
|
+
export const DEFAULT_CSS_LOADING: CssLoadingMode = "blocking"
|
|
12
|
+
|
|
13
|
+
/** Runtime validation for the Vite CSS output switch. `undefined` means Vite's default (`true`). */
|
|
14
|
+
export function normalizeCssCodeSplit(value: unknown): boolean {
|
|
15
|
+
if (value === undefined) return true
|
|
16
|
+
if (typeof value === "boolean") return value
|
|
17
|
+
throw new TypeError(
|
|
18
|
+
`[nifra/web] cssCodeSplit must be a boolean when provided, received ${JSON.stringify(value)}`,
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Runtime validation for JavaScript callers, generated manifests, and hand-authored integrations. */
|
|
23
|
+
export function normalizeCssLoading(value: unknown): CssLoadingMode {
|
|
24
|
+
if (value === "blocking" || value === "deferred") return value
|
|
25
|
+
throw new TypeError(
|
|
26
|
+
`[nifra/web] cssLoading must be "blocking" or "deferred", received ${JSON.stringify(value)}`,
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Guard the only Vite combination that would claim to solve the stylesheet-insertion regression while
|
|
32
|
+
* leaving the cause in place. A deferred split build can still attach CSS when a prefetched lazy chunk
|
|
33
|
+
* evaluates, so it must fail closed instead of producing a misleadingly healthy deployment.
|
|
34
|
+
*/
|
|
35
|
+
export function assertCssLoadingCompatible(
|
|
36
|
+
cssCodeSplit: boolean,
|
|
37
|
+
cssLoading: CssLoadingMode,
|
|
38
|
+
): void {
|
|
39
|
+
if (cssLoading !== "deferred" || !cssCodeSplit) return
|
|
40
|
+
throw new Error(
|
|
41
|
+
'[nifra/web] cssLoading: "deferred" requires cssCodeSplit: false. Vite can otherwise inject ' +
|
|
42
|
+
"stylesheets when prefetched lazy chunks evaluate, reintroducing page-wide style/layout work.",
|
|
43
|
+
)
|
|
44
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -41,6 +41,12 @@ export {
|
|
|
41
41
|
RenderAdapterConformanceError,
|
|
42
42
|
type RenderAdapterConformanceFixture,
|
|
43
43
|
} from "./conformance.ts"
|
|
44
|
+
export {
|
|
45
|
+
assertCssLoadingCompatible,
|
|
46
|
+
type CssLoadingMode,
|
|
47
|
+
DEFAULT_CSS_LOADING,
|
|
48
|
+
normalizeCssLoading,
|
|
49
|
+
} from "./css-contract.ts"
|
|
44
50
|
// Deferred loader data (`defer()` + the `Deferred<T>` type) - consumed by the adapter's `<Await>`.
|
|
45
51
|
export { type Deferred, defer } from "./deferred.ts"
|
|
46
52
|
export {
|
|
@@ -114,6 +120,7 @@ export { SERVER_FN_MODULE, SERVER_ONLY_MODULE } from "./internal/server-boundary
|
|
|
114
120
|
export {
|
|
115
121
|
type CreateWebAppOptions,
|
|
116
122
|
createWebApp,
|
|
123
|
+
type NonceResolver,
|
|
117
124
|
} from "./internal/web-app.ts"
|
|
118
125
|
// ISR (incremental static regeneration): a pluggable cache store + the `withISR` stale-while-revalidate
|
|
119
126
|
// wrapper for rendered SSR responses.
|
|
@@ -258,3 +265,11 @@ export {
|
|
|
258
265
|
// ctx + `renderPage`) and a client adapter mount call this with the same URL + schema, so the two sides
|
|
259
266
|
// produce the identical value by construction. An adapter's `useSearch` binding reads its result.
|
|
260
267
|
export { type SearchOf, searchOf, searchOfChain, serializeSearch } from "./search.ts"
|
|
268
|
+
export {
|
|
269
|
+
type HtmlSanitizer,
|
|
270
|
+
type SanitizedHtml,
|
|
271
|
+
sanitizedHtml,
|
|
272
|
+
sanitizeHtml,
|
|
273
|
+
type TrustedHtml,
|
|
274
|
+
trustHtml,
|
|
275
|
+
} from "./trusted-html.ts"
|
package/src/internal/codegen.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CssLoadingMode, DEFAULT_CSS_LOADING, normalizeCssLoading } from "../css-contract.ts"
|
|
1
2
|
import { MAP_DEFERRED_SOURCE } from "../deferred.ts"
|
|
2
3
|
import type { LayoutEntry, Manifest } from "../manifest.ts"
|
|
3
4
|
import {
|
|
@@ -85,7 +86,7 @@ export function generateClientEntry(
|
|
|
85
86
|
// `/client`, never the root: the root's graph carries the server (renderPage, the static-file
|
|
86
87
|
// server), and Vite's dev server evaluates what it is given instead of tree-shaking it.
|
|
87
88
|
'import { createClientRouter, createMatcher, mergeHeads, resolveMeta } from "@nifrajs/web/client"',
|
|
88
|
-
'import { applyHead, installForms, installHistory, signalHydrated } from "@nifrajs/web/client"',
|
|
89
|
+
'import { applyHead, installForms, installHistory, signalHydrated, waitForStyles } from "@nifrajs/web/client"',
|
|
89
90
|
// Namespace import: `errorBoundary` is optional (an adapter may not export it). A namespace member
|
|
90
91
|
// access yields `undefined` if absent - unlike a named import, which would be a link error.
|
|
91
92
|
`import * as __adapter from ${JSON.stringify(clientModule)}`,
|
|
@@ -219,9 +220,9 @@ export function generateClientEntry(
|
|
|
219
220
|
"is set, `#root` otherwise - and this document has neither. If you emit the document yourself, " +
|
|
220
221
|
'keep `<div id="root">` around the SSR markup.',
|
|
221
222
|
)})`,
|
|
222
|
-
//
|
|
223
|
-
// is
|
|
224
|
-
"loadModule(initial.routeId).then(() => {",
|
|
223
|
+
// Wait for framework-owned deferred CSS before loading/mounting the initial route. The coordinator
|
|
224
|
+
// is a no-op for the default blocking links and always has a bounded fail-open terminal state.
|
|
225
|
+
"waitForStyles().then(() => loadModule(initial.routeId)).then(() => {",
|
|
225
226
|
" mountRouter({ router, routes: chains, searchSchemas, container: root })",
|
|
226
227
|
// Run the optional client loader only after the adapter has mounted the SSR tree. The initial
|
|
227
228
|
// server data is already in `window.__NIFRA_DATA__`, so `serverLoader()` reuses it and cannot
|
|
@@ -240,7 +241,7 @@ export function generateClientEntry(
|
|
|
240
241
|
" applyHead(mergeHeads((metas[s.routeId] ?? [undefined]).map((m) => resolveMeta(m, args))))",
|
|
241
242
|
" }",
|
|
242
243
|
" })",
|
|
243
|
-
"})",
|
|
244
|
+
"}).catch((error) => console.error('[nifra/web] hydration failed:', error))",
|
|
244
245
|
].join("\n")}\n`
|
|
245
246
|
}
|
|
246
247
|
|
|
@@ -305,6 +306,8 @@ export interface GenerateServerManifestOptions {
|
|
|
305
306
|
/** Per-route stylesheet URLs (`buildClient`'s `BuildManifest.routeStyles`) - baked as
|
|
306
307
|
* `export const routeStyles` so a page links only the CSS its route chain uses. */
|
|
307
308
|
readonly routeStyles?: Readonly<Record<string, readonly string[]>> | undefined
|
|
309
|
+
/** How framework-owned stylesheet links should activate before the generated client mounts. */
|
|
310
|
+
readonly cssLoading?: CssLoadingMode
|
|
308
311
|
/** Emit **lazy** per-route loaders (`() => import("./routes/x")`, a static specifier) instead of
|
|
309
312
|
* eager `import * as`, so a bundler with code-splitting emits one chunk per route - loaded on the
|
|
310
313
|
* first request to it, not all at boot (smaller cold-start parse). Default `false` (eager). Both
|
|
@@ -339,7 +342,15 @@ export function generateServerManifest(
|
|
|
339
342
|
manifest: Manifest,
|
|
340
343
|
options: GenerateServerManifestOptions,
|
|
341
344
|
): string {
|
|
342
|
-
const {
|
|
345
|
+
const {
|
|
346
|
+
resolve,
|
|
347
|
+
clientEntry,
|
|
348
|
+
styles,
|
|
349
|
+
routeStyles,
|
|
350
|
+
cssLoading: requestedCssLoading,
|
|
351
|
+
lazy = false,
|
|
352
|
+
} = options
|
|
353
|
+
const cssLoading = normalizeCssLoading(requestedCssLoading ?? DEFAULT_CSS_LOADING)
|
|
343
354
|
// Every unique source file in the manifest (routes + layouts + error/status pages), sorted for stable output.
|
|
344
355
|
const files = [
|
|
345
356
|
...new Set([
|
|
@@ -361,6 +372,7 @@ export function generateServerManifest(
|
|
|
361
372
|
// Always emitted (default empty) so consumers can `import { styles, routeStyles }` unconditionally.
|
|
362
373
|
const stylesLine = `export const styles = ${JSON.stringify(styles ?? [])}`
|
|
363
374
|
const routeStylesLine = `export const routeStyles = ${JSON.stringify(routeStyles ?? {})}`
|
|
375
|
+
const cssLoadingLine = `export const cssLoading = ${JSON.stringify(cssLoading)}`
|
|
364
376
|
if (lazy) {
|
|
365
377
|
// Lazy: `() => import("./routes/x")` per route (static specifier → one chunk per route under a
|
|
366
378
|
// code-splitting bundler, loaded on first request). The map keys are the route-relative paths
|
|
@@ -377,6 +389,7 @@ export function generateServerManifest(
|
|
|
377
389
|
clientEntryLine,
|
|
378
390
|
stylesLine,
|
|
379
391
|
routeStylesLine,
|
|
392
|
+
cssLoadingLine,
|
|
380
393
|
"export const manifest = buildManifest(Object.keys(loaders), (file) => () => loaders[file]())",
|
|
381
394
|
].join("\n")}\n`
|
|
382
395
|
}
|
|
@@ -395,6 +408,7 @@ export function generateServerManifest(
|
|
|
395
408
|
clientEntryLine,
|
|
396
409
|
stylesLine,
|
|
397
410
|
routeStylesLine,
|
|
411
|
+
cssLoadingLine,
|
|
398
412
|
"export const manifest = buildManifest(Object.keys(modules), (file) => () => Promise.resolve(modules[file]))",
|
|
399
413
|
].join("\n")}\n`
|
|
400
414
|
}
|