@nifrajs/web 0.1.0-alpha.1

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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/css-modules.d.ts +34 -0
  4. package/dist/build.d.ts +110 -0
  5. package/dist/build.d.ts.map +1 -0
  6. package/dist/build.js +238 -0
  7. package/dist/build.js.map +1 -0
  8. package/dist/client.d.ts +32 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +204 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/deferred.d.ts +90 -0
  13. package/dist/deferred.d.ts.map +1 -0
  14. package/dist/deferred.js +310 -0
  15. package/dist/deferred.js.map +1 -0
  16. package/dist/dev-error.d.ts +17 -0
  17. package/dist/dev-error.d.ts.map +1 -0
  18. package/dist/dev-error.js +59 -0
  19. package/dist/dev-error.js.map +1 -0
  20. package/dist/dev.d.ts +25 -0
  21. package/dist/dev.d.ts.map +1 -0
  22. package/dist/dev.js +83 -0
  23. package/dist/dev.js.map +1 -0
  24. package/dist/draft.d.ts +58 -0
  25. package/dist/draft.d.ts.map +1 -0
  26. package/dist/draft.js +55 -0
  27. package/dist/draft.js.map +1 -0
  28. package/dist/fonts-google.d.ts +109 -0
  29. package/dist/fonts-google.d.ts.map +1 -0
  30. package/dist/fonts-google.js +291 -0
  31. package/dist/fonts-google.js.map +1 -0
  32. package/dist/fonts.d.ts +67 -0
  33. package/dist/fonts.d.ts.map +1 -0
  34. package/dist/fonts.js +103 -0
  35. package/dist/fonts.js.map +1 -0
  36. package/dist/fs.d.ts +10 -0
  37. package/dist/fs.d.ts.map +1 -0
  38. package/dist/fs.js +24 -0
  39. package/dist/fs.js.map +1 -0
  40. package/dist/index.d.ts +280 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +784 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/islands.d.ts +37 -0
  45. package/dist/islands.d.ts.map +1 -0
  46. package/dist/islands.js +107 -0
  47. package/dist/islands.js.map +1 -0
  48. package/dist/isr.d.ts +154 -0
  49. package/dist/isr.d.ts.map +1 -0
  50. package/dist/isr.js +331 -0
  51. package/dist/isr.js.map +1 -0
  52. package/dist/manifest.d.ts +178 -0
  53. package/dist/manifest.d.ts.map +1 -0
  54. package/dist/manifest.js +213 -0
  55. package/dist/manifest.js.map +1 -0
  56. package/dist/plugins/css-modules.d.ts +40 -0
  57. package/dist/plugins/css-modules.d.ts.map +1 -0
  58. package/dist/plugins/css-modules.js +0 -0
  59. package/dist/plugins/css-modules.js.map +1 -0
  60. package/dist/plugins/kit.d.ts +44 -0
  61. package/dist/plugins/kit.d.ts.map +1 -0
  62. package/dist/plugins/kit.js +105 -0
  63. package/dist/plugins/kit.js.map +1 -0
  64. package/dist/plugins/scss.d.ts +27 -0
  65. package/dist/plugins/scss.d.ts.map +1 -0
  66. package/dist/plugins/scss.js +78 -0
  67. package/dist/plugins/scss.js.map +1 -0
  68. package/dist/prerender.d.ts +77 -0
  69. package/dist/prerender.d.ts.map +1 -0
  70. package/dist/prerender.js +139 -0
  71. package/dist/prerender.js.map +1 -0
  72. package/dist/query.d.ts +62 -0
  73. package/dist/query.d.ts.map +1 -0
  74. package/dist/query.js +184 -0
  75. package/dist/query.js.map +1 -0
  76. package/dist/router.d.ts +169 -0
  77. package/dist/router.d.ts.map +1 -0
  78. package/dist/router.js +531 -0
  79. package/dist/router.js.map +1 -0
  80. package/dist/vite.d.ts +49 -0
  81. package/dist/vite.d.ts.map +1 -0
  82. package/dist/vite.js +144 -0
  83. package/dist/vite.js.map +1 -0
  84. package/package.json +127 -0
  85. package/src/build.ts +375 -0
  86. package/src/client.ts +218 -0
  87. package/src/deferred.ts +339 -0
  88. package/src/dev-error.ts +62 -0
  89. package/src/dev.ts +108 -0
  90. package/src/draft.ts +82 -0
  91. package/src/fonts-google.ts +397 -0
  92. package/src/fonts.ts +147 -0
  93. package/src/fs.ts +26 -0
  94. package/src/index.ts +1202 -0
  95. package/src/islands.ts +121 -0
  96. package/src/isr.ts +461 -0
  97. package/src/manifest.ts +366 -0
  98. package/src/plugins/css-modules.ts +0 -0
  99. package/src/plugins/kit.ts +123 -0
  100. package/src/plugins/scss.ts +108 -0
  101. package/src/prerender.ts +209 -0
  102. package/src/query.ts +250 -0
  103. package/src/router.ts +676 -0
  104. package/src/vite.ts +212 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nifra Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @nifrajs/web
2
+
3
+ Framework-agnostic SSR core for nifra — the render seam + HTML document orchestration.
4
+
5
+ Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework — one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ bun add @nifrajs/web
11
+ ```
12
+
13
+ ## Docs
14
+
15
+ - Reference: <https://nifra.dev/docs>
16
+ - AI-readable: <https://nifra.dev/llms.txt>
17
+
18
+ MIT
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Ambient types for CSS asset imports handled by the `@nifrajs/web/plugins/*` Bun plugins
3
+ * (`css-modules` + `scss`).
4
+ *
5
+ * Add to your app so the imports below are typed. Either reference it from a `.d.ts` in your project:
6
+ *
7
+ * ```ts
8
+ * /// <reference types="@nifrajs/web/css-modules" />
9
+ * ```
10
+ *
11
+ * or list it in `tsconfig.json` → `compilerOptions.types: ["@nifrajs/web/css-modules"]`.
12
+ */
13
+
14
+ /** CSS Modules (`*.module.css`) → the scoped `{ originalClassName: scopedClassName }` map. */
15
+ declare module "*.module.css" {
16
+ /** Keys are the source class names (asIs convention). */
17
+ const classes: Readonly<Record<string, string>>
18
+ export default classes
19
+ }
20
+
21
+ /** SCSS/SASS Modules (`*.module.scss` / `*.module.sass`) → the scoped class map (same as above). */
22
+ declare module "*.module.scss" {
23
+ const classes: Readonly<Record<string, string>>
24
+ export default classes
25
+ }
26
+ declare module "*.module.sass" {
27
+ const classes: Readonly<Record<string, string>>
28
+ export default classes
29
+ }
30
+
31
+ // Plain stylesheet imports are side-effect only (`import "./styles.scss"`); they bundle the CSS and
32
+ // expose no bindings. Declared so the import resolves under TypeScript.
33
+ declare module "*.scss" {}
34
+ declare module "*.sass" {}
@@ -0,0 +1,110 @@
1
+ import type { BunPlugin } from "bun";
2
+ export { type CloudflarePagesRoutes, type CloudflarePagesRoutesOptions, cloudflarePagesRoutes, dataFileFor, htmlFileFor, type PrerenderApp, type PrerenderEntry, type PrerenderOptions, type PrerenderResult, prerenderRoutes, } from "./prerender.js";
3
+ export interface BuildClientOptions {
4
+ /** The `routes/` directory to discover (absolute path). */
5
+ readonly routesDir: string;
6
+ /** Output directory for the bundle + `manifest.json` (absolute path). */
7
+ readonly outDir: string;
8
+ /** The adapter's client runtime (exports `mountRouter`), e.g. `"@nifrajs/web-solid/client"`. */
9
+ readonly clientModule: string;
10
+ /** Route/layout file → import specifier (default: `${routesDir}/${file}`). */
11
+ readonly resolve?: (file: string) => string;
12
+ /** Adapter build plugins (e.g. `solidBunPlugin("dom")`). */
13
+ readonly plugins?: readonly BunPlugin[];
14
+ /** `Bun.build` export conditions (e.g. `["bun", "solid", "browser"]`). */
15
+ readonly conditions?: readonly string[];
16
+ /** Compile-time replacements (e.g. `{ "process.env.NODE_ENV": '"production"' }`). */
17
+ readonly define?: Readonly<Record<string, string>>;
18
+ /** Minify the output (default `true`). */
19
+ readonly minify?: boolean;
20
+ /** URL prefix the assets are served under (default `"/assets/"`); also Bun's chunk `publicPath`. */
21
+ readonly publicPath?: string;
22
+ }
23
+ /** The built asset map — the server reads `entry` for the client script + serves `assets`. */
24
+ export interface BuildManifest {
25
+ /** URL of the client entry module (content-hashed). */
26
+ readonly entry: string;
27
+ /** URLs of every emitted asset (entry + chunks) — for serving + preloading. */
28
+ readonly assets: readonly string[];
29
+ /** `routeId → [layout chunk URLs…, own chunk URL]` — the chunks a route needs, for `createWebApp`'s
30
+ * `routePreload` (`<link rel="modulepreload">` the matched route alongside the entry). Each route +
31
+ * layout is also a build entrypoint, so it gets a named chunk the bootstrap's lazy import dedupes to. */
32
+ readonly routes: Readonly<Record<string, readonly string[]>>;
33
+ /** The app's bundled, content-hashed stylesheet(s) — the bootstrap's **aggregate** CSS (every
34
+ * `import './x.css'` reachable from the app). The complete stylesheet regardless of which file
35
+ * imported the CSS; the always-safe fallback `createWebApp` links when a route has no per-route entry
36
+ * in {@link routeStyles}. Omitted when the app imports no CSS. */
37
+ readonly css?: readonly string[];
38
+ /** `routeId → [chain CSS URLs]` — only the stylesheets the matched route's layout chain + own file
39
+ * actually use (Bun emits a per-entrypoint CSS bundle per route/layout, with shared-component CSS
40
+ * inlined into each consumer). `createWebApp` links these instead of the aggregate, so a page ships
41
+ * only its own CSS. A route is omitted (→ aggregate fallback) when its `[name]` collides with another
42
+ * route's basename (ambiguous CSS↔route) or the build emitted orphan shared-chunk CSS — correctness
43
+ * over minimality. Absent entirely when the app imports no CSS. */
44
+ readonly routeStyles?: Readonly<Record<string, readonly string[]>>;
45
+ }
46
+ /**
47
+ * Build the client bundle for a file-routed app. Writes the hashed assets + `manifest.json` to
48
+ * `outDir` and returns the manifest. Throws (with the bundler logs) on build failure — never
49
+ * silently ships a broken bundle.
50
+ */
51
+ export declare function buildClient(options: BuildClientOptions): Promise<BuildManifest>;
52
+ export interface BuildServerOptions {
53
+ /** The `routes/` directory to discover (absolute path). */
54
+ readonly routesDir: string;
55
+ /** The worker entry module (absolute path) — your `worker.ts`. It imports `{ manifest, clientEntry }`
56
+ * from the generated `./server-manifest`, builds `createWebApp`, and `export default toFetchHandler(app)`. */
57
+ readonly serverEntry: string;
58
+ /** Output directory for the bundled worker (absolute path). */
59
+ readonly outDir: string;
60
+ /** The content-hashed client entry URL (from `buildClient`'s manifest) — **baked** into the generated
61
+ * server manifest, since a disk-less worker can't read `manifest.json` at runtime. */
62
+ readonly clientEntry: string;
63
+ /** Route/layout file → import specifier in the generated manifest (default: a relative path from the
64
+ * manifest's location — written next to `serverEntry` — to `routesDir`). */
65
+ readonly resolve?: (file: string) => string;
66
+ /** Filename for the generated server-manifest module, written next to `serverEntry` (default
67
+ * `"server-manifest.ts"`); your `serverEntry` imports it as `./server-manifest`. */
68
+ readonly manifestFile?: string;
69
+ /** Adapter build plugins (e.g. `solidBunPlugin("ssr")` — Solid routes need their SSR transform at
70
+ * build time; React's JSX is Bun-native and needs none). */
71
+ readonly plugins?: readonly BunPlugin[];
72
+ /** `Bun.build` resolution conditions (default `["workerd", "edge-light", "browser"]`) — selects each
73
+ * dependency's edge build. */
74
+ readonly conditions?: readonly string[];
75
+ /** Compile-time replacements (default `{ "process.env.NODE_ENV": '"production"' }` → production
76
+ * React/Solid on the edge). Pass an explicit object to override (e.g. `{}` to opt out). */
77
+ readonly define?: Readonly<Record<string, string>>;
78
+ /** Minify the output (default `true`). */
79
+ readonly minify?: boolean;
80
+ /** `Bun.build` target (default `"browser"` — the right shape for edge runtimes: Cloudflare Workers,
81
+ * Vercel Edge, Deno, Deno Deploy). Use `"node"` for a `@nifrajs/node` server (so `node:*` built-ins
82
+ * stay external), or `"bun"` for a Bun server. The default `conditions` + the edge resolve shims
83
+ * only apply to the `"browser"` target; `"node"`/`"bun"` resolve their own renderer builds via the
84
+ * matching condition. */
85
+ readonly target?: "browser" | "node" | "bun";
86
+ /** **Lazy/code-split routes** (default `false`): emit `() => import(route)` loaders + bundle with
87
+ * `splitting`, so each route is its own chunk loaded on first request (smaller cold-start parse)
88
+ * instead of all parsed at boot. The output becomes the worker entry **+ chunk files** in `outDir`
89
+ * — on Cloudflare, ship them with wrangler's `no_bundle` + `find_additional_modules` + an ESModule
90
+ * `rule` (Node/Deno import the chunks natively). Eager (one self-contained file) stays the default. */
91
+ readonly lazy?: boolean;
92
+ }
93
+ /** The built worker bundle — point your `wrangler.toml`'s `main` at `worker`. */
94
+ export interface ServerBuild {
95
+ /** Path to the bundled, self-contained worker entry. */
96
+ readonly worker: string;
97
+ /** Paths of every emitted output (entry + any code-split chunks) — what to ship to the platform. */
98
+ readonly outputs: readonly string[];
99
+ }
100
+ /**
101
+ * Build a self-contained **worker bundle** for a file-routed app on a disk-less edge (Cloudflare
102
+ * Workers / workerd). Discovers routes (build-time fs), codegens the static-import server manifest
103
+ * (`generateServerManifest`, written next to `serverEntry`), then bundles `serverEntry` with
104
+ * `Bun.build` using **edge conditions** + the adapter's SSR plugins. The output imports no `node:fs`
105
+ * and does no dynamic-path import, so it runs on workerd: point `wrangler.toml`'s `main` at it and
106
+ * serve the client assets via Workers Assets. Throws (with the bundler logs) on failure — never
107
+ * silently ships a broken worker.
108
+ */
109
+ export declare function buildServer(options: BuildServerOptions): Promise<ServerBuild>;
110
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
package/dist/build.js ADDED
@@ -0,0 +1,238 @@
1
+ /**
2
+ * `@nifrajs/web/build` — the production build (Bun-only, build-time). `buildClient` codegens + bundles
3
+ * the client entry (content-hashed, code-split); `buildServer` codegens the static-import server
4
+ * manifest + bundles a self-contained **worker** for the disk-less edge (Cloudflare Workers). Both
5
+ * are Bun-specific and never on the request path (own subpath, like `@nifrajs/web/fs`); the *output*
6
+ * runs on any runtime.
7
+ */
8
+ import { mkdirSync, writeFileSync } from "node:fs";
9
+ import { dirname, relative, resolve as resolvePath } from "node:path";
10
+ import { discoverRoutes } from "./fs.js";
11
+ import { generateClientEntry, generateServerManifest } from "./index.js";
12
+ // Build-time SSG: prerender opted-in static + dynamic routes to `index.html` (+ static `_data.json`),
13
+ // run after `buildClient`.
14
+ export { cloudflarePagesRoutes, dataFileFor, htmlFileFor, prerenderRoutes, } from "./prerender.js";
15
+ const basename = (path) => path.slice(path.lastIndexOf("/") + 1);
16
+ /** `[name]` Bun derives for an entrypoint: basename without extension (`users/[id].tsx` → `[id]`). */
17
+ const entryName = (file) => {
18
+ const base = basename(file);
19
+ const dot = base.lastIndexOf(".");
20
+ return dot === -1 ? base : base.slice(0, dot);
21
+ };
22
+ /**
23
+ * Build the client bundle for a file-routed app. Writes the hashed assets + `manifest.json` to
24
+ * `outDir` and returns the manifest. Throws (with the bundler logs) on build failure — never
25
+ * silently ships a broken bundle.
26
+ */
27
+ export async function buildClient(options) {
28
+ const { routesDir, outDir, clientModule } = options;
29
+ const resolve = options.resolve ?? ((file) => `${routesDir}/${file}`);
30
+ const publicPath = options.publicPath ?? "/assets/";
31
+ mkdirSync(outDir, { recursive: true });
32
+ const routeManifest = discoverRoutes(routesDir);
33
+ // The bootstrap's filename is `_velo`-namespaced (not `entry.ts`) so its `[name]` can't collide with
34
+ // a user route named `entry.tsx` when the CSS mapping below excludes the bootstrap's aggregate CSS.
35
+ const entryFile = `${outDir}/_nifra-entry.ts`;
36
+ writeFileSync(entryFile, generateClientEntry(routeManifest, { clientModule, resolve }));
37
+ // Every unique route/layout/`_404` file (sorted, stable), as ADDITIONAL entrypoints — Bun emits a
38
+ // named chunk per file that the bootstrap's lazy `import()` dedupes to (verified), so the manifest
39
+ // can map each route to its chunk URLs for matched-route preload. `resolve(file)` is the same
40
+ // specifier the bootstrap imports, so the entrypoint + lazy import are the same module (dedup).
41
+ const routeFiles = [
42
+ ...new Set([
43
+ ...routeManifest.routes.map((r) => r.file),
44
+ ...Object.values(routeManifest.layouts).map((l) => l.file),
45
+ ...(routeManifest.notFound ? [routeManifest.notFound.file] : []),
46
+ ]),
47
+ ].sort();
48
+ // `metafile: true` asks Bun for the input/output graph — specifically `outputs[js].entryPoint`
49
+ // (the source file) + `outputs[js].cssBundle` (that entry's emitted stylesheet). It's the robust
50
+ // entry→CSS link for per-route splitting: keyed by the unique source path, so it survives
51
+ // same-basename collisions (`index.tsx` + `blog/index.tsx`) that a filename match can't. Not yet in
52
+ // `@types/bun`'s `BuildConfig`, so spread it in (spread props skip the excess-property check).
53
+ const buildExtras = { metafile: true };
54
+ const result = await Bun.build({
55
+ entrypoints: [entryFile, ...routeFiles.map(resolve)],
56
+ outdir: outDir,
57
+ target: "browser",
58
+ naming: "[name]-[hash].[ext]",
59
+ publicPath,
60
+ splitting: true, // one chunk per lazily-imported route; shared deps deduped into shared chunks
61
+ // `import "./x.css"` in a route/component → bundled, minified, content-hashed `.css` asset (Bun
62
+ // strips the import from the JS; CSS bundling is on by default since Bun 1.2). Mapped to routes
63
+ // below — both the aggregate and per-route — via the metafile, for `<link>` injection.
64
+ ...buildExtras,
65
+ minify: options.minify ?? true,
66
+ plugins: [...(options.plugins ?? [])],
67
+ ...(options.conditions ? { conditions: [...options.conditions] } : {}),
68
+ ...(options.define ? { define: { ...options.define } } : {}),
69
+ });
70
+ if (!result.success) {
71
+ throw new Error(`[nifra/web] client build failed:\n${result.logs.map((l) => String(l)).join("\n")}`);
72
+ }
73
+ const toUrl = (path) => `${publicPath}${basename(path)}`;
74
+ // Entry-point outputs come back in entrypoint order: [bootstrap, ...routeFiles]. Map each route file
75
+ // to its chunk URL by that order (guarded against drift), then a route's chunks = its layout chain +
76
+ // own file.
77
+ const entryPoints = result.outputs.filter((o) => o.kind === "entry-point");
78
+ const bootstrap = entryPoints[0];
79
+ if (bootstrap === undefined)
80
+ throw new Error("[nifra/web] build produced no entry-point output");
81
+ if (entryPoints.length !== routeFiles.length + 1) {
82
+ throw new Error(`[nifra/web] expected ${routeFiles.length + 1} entry-point outputs (bootstrap + ${routeFiles.length} routes), got ${entryPoints.length}`);
83
+ }
84
+ const fileToChunk = new Map();
85
+ routeFiles.forEach((file, i) => {
86
+ const out = entryPoints[i + 1]; // in range — length checked above
87
+ if (out !== undefined)
88
+ fileToChunk.set(file, toUrl(out.path));
89
+ });
90
+ const chunksFor = (chainFiles) => chainFiles.map((f) => fileToChunk.get(f)).filter((u) => u !== undefined);
91
+ const routes = {};
92
+ for (const route of routeManifest.routes) {
93
+ routes[route.id] = chunksFor([
94
+ ...route.layoutIds.map((id) => routeManifest.layouts[id]?.file ?? ""),
95
+ route.file,
96
+ ]);
97
+ }
98
+ if (routeManifest.notFound)
99
+ routes._404 = chunksFor([routeManifest.notFound.file]);
100
+ // CSS — aggregate: an `import "./x.css"` anywhere → a content-hashed `.css` asset (Bun strips the
101
+ // import from the JS). The bootstrap lazily imports every route, so its **aggregate** stylesheet is
102
+ // the whole app's CSS — the always-safe fallback `createWebApp` links when a route has no per-route
103
+ // entry below. Fallback to all CSS assets if Bun emitted no distinct aggregate.
104
+ const bootstrapName = entryName(entryFile); // `_nifra-entry`
105
+ const cssNameOf = (path) => {
106
+ const base = basename(path);
107
+ return base.slice(0, base.lastIndexOf("-")); // strip `-${hash}.css`
108
+ };
109
+ const cssAssets = result.outputs.filter((o) => o.kind === "asset" && o.path.endsWith(".css"));
110
+ const aggregate = cssAssets
111
+ .filter((o) => cssNameOf(o.path) === bootstrapName)
112
+ .map((o) => toUrl(o.path));
113
+ const css = aggregate.length > 0 ? aggregate : cssAssets.map((o) => toUrl(o.path));
114
+ // CSS — per-route: each route/layout file is its own entrypoint, so the build metafile records its
115
+ // `cssBundle` — exactly the CSS that file's subtree uses (shared-component CSS is inlined into each
116
+ // consumer; verified). Keyed by the metafile's unique source `entryPoint`, so it survives
117
+ // same-basename collisions (`index.tsx` + `blog/index.tsx`) that a filename match can't. A page then
118
+ // links only its layout chain + own CSS (deduped); an empty array means the page needs no CSS at all.
119
+ // Absent (→ aggregate fallback) only if Bun emits no metafile/cssBundle — never silently incomplete.
120
+ const meta = result.metafile;
121
+ const cwd = process.cwd();
122
+ const cssByEntry = new Map();
123
+ for (const out of Object.values(meta?.outputs ?? {})) {
124
+ if (out.entryPoint !== undefined && out.cssBundle !== undefined) {
125
+ cssByEntry.set(resolvePath(cwd, out.entryPoint), toUrl(out.cssBundle));
126
+ }
127
+ }
128
+ const stylesFor = (chainFiles) => {
129
+ const urls = chainFiles
130
+ .map((f) => (f ? cssByEntry.get(resolvePath(resolve(f))) : undefined))
131
+ .filter((u) => u !== undefined);
132
+ return [...new Set(urls)];
133
+ };
134
+ const routeStyles = {};
135
+ if (css.length > 0 && cssByEntry.size > 0) {
136
+ for (const route of routeManifest.routes) {
137
+ routeStyles[route.id] = stylesFor([
138
+ ...route.layoutIds.map((id) => routeManifest.layouts[id]?.file ?? ""),
139
+ route.file,
140
+ ]);
141
+ }
142
+ if (routeManifest.notFound)
143
+ routeStyles._404 = stylesFor([routeManifest.notFound.file]);
144
+ }
145
+ const manifest = {
146
+ entry: toUrl(bootstrap.path),
147
+ assets: result.outputs.map((o) => toUrl(o.path)),
148
+ routes,
149
+ ...(css.length > 0 ? { css } : {}),
150
+ ...(Object.keys(routeStyles).length > 0 ? { routeStyles } : {}),
151
+ };
152
+ writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest, null, 2));
153
+ return manifest;
154
+ }
155
+ /**
156
+ * react-dom's `exports["./server"]` maps the `bun` condition to a Bun-API server build that crashes
157
+ * on workerd, and `Bun.build` always applies the `bun` condition (it wins over `workerd`/`edge-light`),
158
+ * so conditions alone can't select the edge build. This shim pins `react-dom/server` to its edge build
159
+ * (`server.edge.js`, which exports `renderToReadableStream`). A no-op when nothing imports react-dom
160
+ * (e.g. a Solid worker) — the resolver only runs on a match.
161
+ */
162
+ const reactDomEdgePlugin = (from) => ({
163
+ name: "nifra-react-dom-edge",
164
+ setup(build) {
165
+ build.onResolve({ filter: /^react-dom\/server$/ }, () => ({
166
+ path: Bun.resolveSync("react-dom/server.edge", from),
167
+ }));
168
+ },
169
+ });
170
+ /**
171
+ * `solid-js/web` selects its **server** runtime (`renderToStream`) via the `worker` condition, but
172
+ * `Bun.build` 1.3.14 **segfaults** when the `worker` condition is active (https://bun.report). And
173
+ * without `worker`, `browser` (which precedes the other server conditions in solid's exports map)
174
+ * wins → the *dom* runtime, which can't SSR. So this shim pins `solid-js/web` straight to its server
175
+ * build, sidestepping the crashing condition. Lazy (resolved on match) + a no-op when nothing imports
176
+ * `solid-js/web` (e.g. a React worker). Drop it (and use the `worker` condition) once Bun is fixed.
177
+ */
178
+ const solidWebServerPlugin = (from) => ({
179
+ name: "nifra-solid-web-server",
180
+ setup(build) {
181
+ build.onResolve({ filter: /^solid-js\/web$/ }, () => {
182
+ // solid's `worker`/`node`/`deno` conditions all map to ./web/dist/server.js (its server build).
183
+ const pkg = Bun.resolveSync("solid-js/package.json", from);
184
+ return { path: pkg.replace(/package\.json$/, "web/dist/server.js") };
185
+ });
186
+ },
187
+ });
188
+ /**
189
+ * Build a self-contained **worker bundle** for a file-routed app on a disk-less edge (Cloudflare
190
+ * Workers / workerd). Discovers routes (build-time fs), codegens the static-import server manifest
191
+ * (`generateServerManifest`, written next to `serverEntry`), then bundles `serverEntry` with
192
+ * `Bun.build` using **edge conditions** + the adapter's SSR plugins. The output imports no `node:fs`
193
+ * and does no dynamic-path import, so it runs on workerd: point `wrangler.toml`'s `main` at it and
194
+ * serve the client assets via Workers Assets. Throws (with the bundler logs) on failure — never
195
+ * silently ships a broken worker.
196
+ */
197
+ export async function buildServer(options) {
198
+ const { routesDir, serverEntry, outDir, clientEntry } = options;
199
+ const entryDir = dirname(serverEntry);
200
+ const manifestFile = options.manifestFile ?? "server-manifest.ts";
201
+ // Default: import routes relative from the generated manifest (next to serverEntry) to routesDir.
202
+ const rel = relative(entryDir, routesDir).replaceAll("\\", "/");
203
+ const resolve = options.resolve ?? ((file) => `./${rel}/${file}`);
204
+ mkdirSync(outDir, { recursive: true });
205
+ const lazy = options.lazy ?? false;
206
+ const target = options.target ?? "browser";
207
+ // Edge (browser) target: Bun's `bun` condition contaminates react-dom's server build + the `worker`
208
+ // condition segfaults Bun.build on solid, so force the edge/server builds via shims. The `node`/`bun`
209
+ // targets resolve those correctly via their own condition, so the shims (and edge conditions) don't
210
+ // apply — defaults become `[target]` (e.g. react-dom → server.node.js under `node`).
211
+ const edge = target === "browser";
212
+ const conditions = options.conditions ?? (edge ? ["workerd", "edge-light", "browser"] : [target]);
213
+ const manifest = discoverRoutes(routesDir);
214
+ writeFileSync(`${entryDir}/${manifestFile}`, generateServerManifest(manifest, { resolve, clientEntry, lazy }));
215
+ const result = await Bun.build({
216
+ entrypoints: [serverEntry],
217
+ outdir: outDir,
218
+ target,
219
+ conditions: [...conditions],
220
+ define: { ...(options.define ?? { "process.env.NODE_ENV": '"production"' }) },
221
+ minify: options.minify ?? true,
222
+ // Lazy → one chunk per route (loaded on first request); eager → a single self-contained file.
223
+ splitting: lazy,
224
+ plugins: [
225
+ ...(edge ? [reactDomEdgePlugin(entryDir), solidWebServerPlugin(entryDir)] : []),
226
+ ...(options.plugins ?? []),
227
+ ],
228
+ });
229
+ if (!result.success) {
230
+ throw new Error(`[nifra/web] server build failed:\n${result.logs.map((l) => String(l)).join("\n")}`);
231
+ }
232
+ const entryOutput = result.outputs.find((o) => o.kind === "entry-point");
233
+ if (entryOutput === undefined) {
234
+ throw new Error("[nifra/web] server build produced no entry-point output");
235
+ }
236
+ return { worker: entryOutput.path, outputs: result.outputs.map((o) => o.path) };
237
+ }
238
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `@nifrajs/web/client` — the agnostic browser layer for client-side navigation. It wires the pure
3
+ * router store ({@link ClientRouter}) to the browser: `pushState` on navigate, `popstate` →
4
+ * navigate, and delegated interception of same-origin `<a>` clicks (client transition instead of
5
+ * a full page load). DOM-only — never imported on the server, so the store stays SSR-safe.
6
+ */
7
+ import type { Meta } from "./manifest.js";
8
+ import type { ClientRouter } from "./router.js";
9
+ export interface InstallHistoryOptions {
10
+ /** Full-page fallback when a client navigation can't proceed (default: `location.assign`). */
11
+ readonly fallback?: (path: string) => void;
12
+ }
13
+ /**
14
+ * Attach history + link interception to a router. Returns a teardown function that removes the
15
+ * listeners. A data-fetch failure during a client navigation falls back to a full-page load, so
16
+ * navigation degrades gracefully rather than leaving the user stuck.
17
+ */
18
+ export declare function installHistory(router: ClientRouter, options?: InstallHistoryOptions): () => void;
19
+ /**
20
+ * Intercept submissions of same-origin `<form method="post">` whose action targets an app route:
21
+ * submit via the router (no full reload — POST the action, then revalidate the active loader). On
22
+ * failure it falls back to a native submit, so the form still works. Returns a teardown function.
23
+ */
24
+ /**
25
+ * Sync the document head to a route's resolved {@link Meta} on client navigation. Sets the title
26
+ * (when provided) and replaces the **managed** (`data-nifra`) `<meta>`/`<link>` tags — static head
27
+ * content (charset, hand-written tags) is never touched. SSR injects the same `data-nifra` tags, so
28
+ * the first navigation cleanly takes over from the server-rendered head.
29
+ */
30
+ export declare function applyHead(head: Meta): void;
31
+ export declare function installForms(router: ClientRouter): () => void;
32
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,WAAW,qBAAqB;IACpC,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,qBAA0B,GAClC,MAAM,IAAI,CAsJZ;AAED;;;;GAIG;AACH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAW1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,IAAI,CAmB7D"}