@praxisjs/ssg 0.1.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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/dist/__tests__/loader-hook.test.d.ts +2 -0
- package/dist/__tests__/loader-hook.test.d.ts.map +1 -0
- package/dist/__tests__/loader-hook.test.js +66 -0
- package/dist/__tests__/loader-hook.test.js.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +2 -0
- package/dist/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/__tests__/plugin.test.js +128 -0
- package/dist/__tests__/plugin.test.js.map +1 -0
- package/dist/__tests__/prerender.test.d.ts +2 -0
- package/dist/__tests__/prerender.test.d.ts.map +1 -0
- package/dist/__tests__/prerender.test.js +211 -0
- package/dist/__tests__/prerender.test.js.map +1 -0
- package/dist/__tests__/register-fallback.test.d.ts +2 -0
- package/dist/__tests__/register-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/register-fallback.test.js +20 -0
- package/dist/__tests__/register-fallback.test.js.map +1 -0
- package/dist/__tests__/resolve-sync.test.d.ts +2 -0
- package/dist/__tests__/resolve-sync.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-sync.test.js +67 -0
- package/dist/__tests__/resolve-sync.test.js.map +1 -0
- package/dist/__tests__/routes.test.d.ts +2 -0
- package/dist/__tests__/routes.test.d.ts.map +1 -0
- package/dist/__tests__/routes.test.js +154 -0
- package/dist/__tests__/routes.test.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +156 -0
- package/dist/index.js.map +1 -0
- package/dist/loader-hook.d.ts +5 -0
- package/dist/loader-hook.d.ts.map +1 -0
- package/dist/loader-hook.js +34 -0
- package/dist/loader-hook.js.map +1 -0
- package/dist/prerender.d.ts +38 -0
- package/dist/prerender.d.ts.map +1 -0
- package/dist/prerender.js +116 -0
- package/dist/prerender.js.map +1 -0
- package/dist/routes.d.ts +32 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +59 -0
- package/dist/routes.js.map +1 -0
- package/package.json +38 -0
- package/src/__tests__/loader-hook.test.ts +76 -0
- package/src/__tests__/plugin.test.ts +161 -0
- package/src/__tests__/prerender.test.ts +181 -0
- package/src/__tests__/register-fallback.test.ts +23 -0
- package/src/__tests__/resolve-sync.test.ts +91 -0
- package/src/__tests__/routes.test.ts +175 -0
- package/src/index.ts +233 -0
- package/src/loader-hook.ts +43 -0
- package/src/prerender.ts +168 -0
- package/src/routes.ts +83 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-sync.test.js","sourceRoot":"","sources":["../../src/__tests__/resolve-sync.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/routes.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { flattenRoutes, isStaticPath, pathToOutputFile, resolvePaths } from "../routes";
|
|
3
|
+
const Noop = class {
|
|
4
|
+
};
|
|
5
|
+
describe("flattenRoutes", () => {
|
|
6
|
+
it("flattens a flat route list unchanged", () => {
|
|
7
|
+
const routes = [
|
|
8
|
+
{ path: "/", component: Noop },
|
|
9
|
+
{ path: "/about", component: Noop },
|
|
10
|
+
];
|
|
11
|
+
expect(flattenRoutes(routes).map((r) => r.fullPath)).toEqual(["/", "/about"]);
|
|
12
|
+
});
|
|
13
|
+
it("prefixes nested children with the parent path", () => {
|
|
14
|
+
const routes = [
|
|
15
|
+
{
|
|
16
|
+
path: "/blog",
|
|
17
|
+
component: Noop,
|
|
18
|
+
children: [
|
|
19
|
+
{ path: "/", component: Noop },
|
|
20
|
+
{ path: "/:slug", component: Noop },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
expect(flattenRoutes(routes).map((r) => r.fullPath)).toEqual(["/blog", "/blog/", "/blog/:slug"]);
|
|
25
|
+
});
|
|
26
|
+
it("treats a root parent's prefix as empty, matching RouterInstance.addRoute", () => {
|
|
27
|
+
const routes = [
|
|
28
|
+
{
|
|
29
|
+
path: "/",
|
|
30
|
+
component: Noop,
|
|
31
|
+
children: [{ path: "/dashboard", component: Noop }],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
expect(flattenRoutes(routes).map((r) => r.fullPath)).toEqual(["/", "/dashboard"]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("isStaticPath", () => {
|
|
38
|
+
it("treats plain paths as static", () => {
|
|
39
|
+
expect(isStaticPath("/about")).toBe(true);
|
|
40
|
+
expect(isStaticPath("/")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it("treats :param segments as dynamic", () => {
|
|
43
|
+
expect(isStaticPath("/blog/:slug")).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
it("treats optional :param? segments as dynamic", () => {
|
|
46
|
+
expect(isStaticPath("/users/:id?")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it("treats ** catch-alls as dynamic", () => {
|
|
49
|
+
expect(isStaticPath("/docs/**")).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("resolvePaths", () => {
|
|
53
|
+
it("returns static paths as-is", async () => {
|
|
54
|
+
const routes = [
|
|
55
|
+
{ path: "/", component: Noop },
|
|
56
|
+
{ path: "/about", component: Noop },
|
|
57
|
+
];
|
|
58
|
+
expect(await resolvePaths({ routes })).toEqual(["/", "/about"]);
|
|
59
|
+
});
|
|
60
|
+
it("expands a dynamic route via getStaticPaths", async () => {
|
|
61
|
+
const routes = [{ path: "/blog/:slug", component: Noop }];
|
|
62
|
+
const paths = await resolvePaths({
|
|
63
|
+
routes,
|
|
64
|
+
getStaticPaths: async () => ["/blog/hello", "/blog/world"],
|
|
65
|
+
});
|
|
66
|
+
expect(paths).toEqual(["/blog/hello", "/blog/world"]);
|
|
67
|
+
});
|
|
68
|
+
it("skips dynamic routes with no getStaticPaths", async () => {
|
|
69
|
+
const routes = [
|
|
70
|
+
{ path: "/", component: Noop },
|
|
71
|
+
{ path: "/blog/:slug", component: Noop },
|
|
72
|
+
];
|
|
73
|
+
expect(await resolvePaths({ routes })).toEqual(["/"]);
|
|
74
|
+
});
|
|
75
|
+
it("passes the route and its full path pattern to getStaticPaths", async () => {
|
|
76
|
+
const target = { path: "/blog/:slug", component: Noop };
|
|
77
|
+
const routes = [target];
|
|
78
|
+
let seen;
|
|
79
|
+
await resolvePaths({
|
|
80
|
+
routes,
|
|
81
|
+
getStaticPaths: async (route, fullPath) => {
|
|
82
|
+
seen = [route, fullPath];
|
|
83
|
+
return [];
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
expect(seen).toEqual([target, "/blog/:slug"]);
|
|
87
|
+
});
|
|
88
|
+
it("de-duplicates paths across routes", async () => {
|
|
89
|
+
const routes = [
|
|
90
|
+
{ path: "/blog/:slug", component: Noop },
|
|
91
|
+
{ path: "/other/:slug", component: Noop },
|
|
92
|
+
];
|
|
93
|
+
const paths = await resolvePaths({
|
|
94
|
+
routes,
|
|
95
|
+
getStaticPaths: async () => ["/shared"],
|
|
96
|
+
});
|
|
97
|
+
expect(paths).toEqual(["/shared"]);
|
|
98
|
+
});
|
|
99
|
+
it("expands a dynamic route via its own colocated getStaticPaths, with no global fallback needed", async () => {
|
|
100
|
+
const routes = [
|
|
101
|
+
{ path: "/blog/:slug", component: Noop, getStaticPaths: async () => ["/blog/hello", "/blog/world"] },
|
|
102
|
+
];
|
|
103
|
+
expect(await resolvePaths({ routes })).toEqual(["/blog/hello", "/blog/world"]);
|
|
104
|
+
});
|
|
105
|
+
it("passes the route's own full path to its colocated getStaticPaths", async () => {
|
|
106
|
+
let seen;
|
|
107
|
+
const routes = [
|
|
108
|
+
{
|
|
109
|
+
path: "/blog/:slug",
|
|
110
|
+
component: Noop,
|
|
111
|
+
getStaticPaths: async (fullPath) => {
|
|
112
|
+
seen = fullPath;
|
|
113
|
+
return [];
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
await resolvePaths({ routes });
|
|
118
|
+
expect(seen).toBe("/blog/:slug");
|
|
119
|
+
});
|
|
120
|
+
it("prefers a route's own getStaticPaths over the global fallback", async () => {
|
|
121
|
+
const routes = [
|
|
122
|
+
{ path: "/blog/:slug", component: Noop, getStaticPaths: async () => ["/blog/own"] },
|
|
123
|
+
];
|
|
124
|
+
const paths = await resolvePaths({
|
|
125
|
+
routes,
|
|
126
|
+
getStaticPaths: async () => ["/blog/global"],
|
|
127
|
+
});
|
|
128
|
+
expect(paths).toEqual(["/blog/own"]);
|
|
129
|
+
});
|
|
130
|
+
it("falls back to the global getStaticPaths for routes with no colocated one, alongside routes that have their own", async () => {
|
|
131
|
+
const routes = [
|
|
132
|
+
{ path: "/blog/:slug", component: Noop, getStaticPaths: async () => ["/blog/own"] },
|
|
133
|
+
{ path: "/other/:slug", component: Noop },
|
|
134
|
+
];
|
|
135
|
+
const paths = await resolvePaths({
|
|
136
|
+
routes,
|
|
137
|
+
getStaticPaths: async (_route, fullPath) => (fullPath === "/other/:slug" ? ["/other/global"] : []),
|
|
138
|
+
});
|
|
139
|
+
expect(paths).toEqual(["/blog/own", "/other/global"]);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe("pathToOutputFile", () => {
|
|
143
|
+
it("maps the root path to index.html", () => {
|
|
144
|
+
expect(pathToOutputFile("/")).toBe("index.html");
|
|
145
|
+
});
|
|
146
|
+
it("maps a nested path to <path>/index.html", () => {
|
|
147
|
+
expect(pathToOutputFile("/about")).toBe("about/index.html");
|
|
148
|
+
expect(pathToOutputFile("/blog/hello-world")).toBe("blog/hello-world/index.html");
|
|
149
|
+
});
|
|
150
|
+
it("normalizes surrounding slashes", () => {
|
|
151
|
+
expect(pathToOutputFile("/about/")).toBe("about/index.html");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=routes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.test.js","sourceRoot":"","sources":["../../src/__tests__/routes.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
import type { RouterOptions } from "@praxisjs/router";
|
|
3
|
+
export type { GetStaticPaths, RouteEntry, RouteStaticPaths } from "./routes";
|
|
4
|
+
export interface SSGConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Module specifier exporting the root component as its default export and
|
|
7
|
+
* the route table (same one passed to `@Router([...])`) as a named
|
|
8
|
+
* `routes` export, e.g. `"./src/app.tsx"`. Loaded through Vite's own SSR
|
|
9
|
+
* module graph — not imported directly by `vite.config.ts` — so the config
|
|
10
|
+
* file itself never needs to resolve JSX/component source.
|
|
11
|
+
*
|
|
12
|
+
* A dynamic route can expand itself into concrete paths by declaring its
|
|
13
|
+
* own `getStaticPaths` directly on the route object (see `RouteEntry`) —
|
|
14
|
+
* the recommended default, since it's colocated with the route and needs
|
|
15
|
+
* no path matching. `root` may *also* export a named `getStaticPaths` as a
|
|
16
|
+
* fallback for routes that don't declare their own; a route's own always
|
|
17
|
+
* takes precedence.
|
|
18
|
+
*
|
|
19
|
+
* Either way, this can only be reached through `root`, never passed as a
|
|
20
|
+
* plain config value here: Vite's own config loader bundles
|
|
21
|
+
* `vite.config.ts` with a minimal bundler that inlines relative imports
|
|
22
|
+
* (only bare package specifiers get externalized) but never lowers TC39
|
|
23
|
+
* decorators. A `getStaticPaths` written inline in `vite.config.ts` that
|
|
24
|
+
* reaches a decorated class through a relative import — e.g. one that
|
|
25
|
+
* loads a `@Collection`-decorated schema to enumerate content slugs —
|
|
26
|
+
* would bundle that raw decorator syntax straight into the config bundle,
|
|
27
|
+
* which Node cannot then execute. Exporting it from the same module as
|
|
28
|
+
* `root` (or from a route reachable from it) avoids this: it only ever
|
|
29
|
+
* loads through `ssrLoadModule`, which runs the app's real Vite pipeline
|
|
30
|
+
* (decorator lowering included).
|
|
31
|
+
*/
|
|
32
|
+
root: string;
|
|
33
|
+
routerOptions?: RouterOptions;
|
|
34
|
+
/** CSS selector for the mount container inside `index.html`. Default: `#app`. */
|
|
35
|
+
containerSelector?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Enables client-side hydration (reconciling the prerendered DOM instead of
|
|
38
|
+
* discarding it). Default: `true`. Set to `false` to fall back to a plain
|
|
39
|
+
* client remount over the static HTML — no reconciliation runs at all,
|
|
40
|
+
* client or server, and `render()` in `@praxisjs/runtime` never sees the
|
|
41
|
+
* hydration marker.
|
|
42
|
+
*/
|
|
43
|
+
hydrate?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Installs the retry hook above for the rest of this process. Idempotent.
|
|
47
|
+
* `ssgPlugin()` calls this itself before dynamically importing
|
|
48
|
+
* `@praxisjs/ssg/prerender` — only needed directly if you're calling
|
|
49
|
+
* `prerender()` from your own Node script instead of going through
|
|
50
|
+
* `ssgPlugin()`, and must run before that import.
|
|
51
|
+
*
|
|
52
|
+
* Prefers `module.registerHooks()` (in-thread, sync, Node 22.15+) and falls
|
|
53
|
+
* back to the older `module.register()` (off-thread, needs its own module
|
|
54
|
+
* file — see loader-hook.ts, the asynchronous twin of `resolveSync` above)
|
|
55
|
+
* on earlier Node versions.
|
|
56
|
+
*/
|
|
57
|
+
export declare function registerLoaderHook(): void;
|
|
58
|
+
export declare function ssgPlugin(config: SSGConfig): Plugin;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,MAAM,EAAuB,MAAM,MAAM,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtD,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE7E,MAAM,WAAW,SAAS;IACxB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6CD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CASzC;AAoED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CA2CnD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import * as nodeModule from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { createServer } from "vite";
|
|
5
|
+
// ── extensionless-relative-import retry hook ────────────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// Every other @praxisjs/* package is built with tsc, which emits relative
|
|
8
|
+
// specifiers exactly as written in source ("./router", not "./router.js") —
|
|
9
|
+
// harmless for every consumer so far, since they're all resolved through a
|
|
10
|
+
// bundler (Vite/esbuild/rollup), which resolves extensionless specifiers
|
|
11
|
+
// fine. Node's own ESM resolver does not: it's spec-strict and requires an
|
|
12
|
+
// explicit extension on relative specifiers, and @praxisjs/ssg is the first
|
|
13
|
+
// package here that needs to load that dependency chain (router, core, head,
|
|
14
|
+
// runtime) directly through Node, when `closeBundle` below dynamically
|
|
15
|
+
// imports ./prerender.
|
|
16
|
+
//
|
|
17
|
+
// This lives inline in the package's main entry, with zero relative imports
|
|
18
|
+
// of its own, rather than in a separate module: `registerLoaderHook()` must
|
|
19
|
+
// run *before* anything reachable from this file needs the workaround it
|
|
20
|
+
// installs, and a relative import here would hit the exact problem it's
|
|
21
|
+
// meant to fix before it ever gets the chance to run.
|
|
22
|
+
const RETRYABLE_CODES = new Set(["ERR_MODULE_NOT_FOUND", "ERR_UNSUPPORTED_DIR_IMPORT"]);
|
|
23
|
+
function resolveSync(specifier, context, nextResolve) {
|
|
24
|
+
try {
|
|
25
|
+
return nextResolve(specifier, context);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const code = err.code;
|
|
29
|
+
if ((specifier.startsWith("./") || specifier.startsWith("../")) && code !== undefined && RETRYABLE_CODES.has(code)) {
|
|
30
|
+
for (const candidate of [`${specifier}.js`, `${specifier}/index.js`]) {
|
|
31
|
+
try {
|
|
32
|
+
return nextResolve(candidate, context);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// try the next candidate
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let hookRegistered = false;
|
|
43
|
+
/**
|
|
44
|
+
* Installs the retry hook above for the rest of this process. Idempotent.
|
|
45
|
+
* `ssgPlugin()` calls this itself before dynamically importing
|
|
46
|
+
* `@praxisjs/ssg/prerender` — only needed directly if you're calling
|
|
47
|
+
* `prerender()` from your own Node script instead of going through
|
|
48
|
+
* `ssgPlugin()`, and must run before that import.
|
|
49
|
+
*
|
|
50
|
+
* Prefers `module.registerHooks()` (in-thread, sync, Node 22.15+) and falls
|
|
51
|
+
* back to the older `module.register()` (off-thread, needs its own module
|
|
52
|
+
* file — see loader-hook.ts, the asynchronous twin of `resolveSync` above)
|
|
53
|
+
* on earlier Node versions.
|
|
54
|
+
*/
|
|
55
|
+
export function registerLoaderHook() {
|
|
56
|
+
if (hookRegistered)
|
|
57
|
+
return;
|
|
58
|
+
hookRegistered = true;
|
|
59
|
+
if (typeof nodeModule.registerHooks === "function") {
|
|
60
|
+
nodeModule.registerHooks({ resolve: resolveSync });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- intentional fallback for Node < 22.15, which has no registerHooks
|
|
64
|
+
nodeModule.register(new URL("./loader-hook.js", import.meta.url));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function loadEntry(resolvedConfig, root, withDomGlobals) {
|
|
68
|
+
const server = await createServer({
|
|
69
|
+
configFile: resolvedConfig.configFile,
|
|
70
|
+
root: resolvedConfig.root,
|
|
71
|
+
server: { middlewareMode: true, watch: null },
|
|
72
|
+
appType: "custom",
|
|
73
|
+
// Every @praxisjs/* package (and anything else in node_modules) must
|
|
74
|
+
// resolve through plain Node `import()` here — the same path `prerender()`
|
|
75
|
+
// itself uses to load @praxisjs/core/runtime/router/head — instead of
|
|
76
|
+
// being re-processed through Vite's SSR transform. Two different copies
|
|
77
|
+
// of @praxisjs/core would mean two separate component-instance registries:
|
|
78
|
+
// mountComponent() (from the plain-Node copy prerender.ts imports) would
|
|
79
|
+
// construct instances that @Router([...])'s decorator (baked into the
|
|
80
|
+
// Vite-SSR-transformed copy the root module was loaded with) doesn't
|
|
81
|
+
// recognize as valid components.
|
|
82
|
+
ssr: { external: true },
|
|
83
|
+
// No client ever connects to this server and nothing here benefits from
|
|
84
|
+
// pre-bundling npm deps for browser delivery — and a background
|
|
85
|
+
// (re-)optimization pass restarting mid-request looked like a plausible
|
|
86
|
+
// cause of "transport was disconnected" errors on lazy-loaded routes.
|
|
87
|
+
optimizeDeps: { noDiscovery: true, include: [] },
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
// @Router([...])'s class decorator calls createRouter() at
|
|
91
|
+
// module-evaluation time — ssrLoadModule() below runs that as a side
|
|
92
|
+
// effect of importing the root module, so it needs *a* window in place
|
|
93
|
+
// already, even though nothing is actually being rendered yet (the real,
|
|
94
|
+
// per-route window gets installed later, in prerender()).
|
|
95
|
+
const mod = await withDomGlobals("<!doctype html><html><head></head><body></body></html>", "http://localhost/", async () => {
|
|
96
|
+
return (await server.ssrLoadModule(root));
|
|
97
|
+
});
|
|
98
|
+
if (!mod.default) {
|
|
99
|
+
throw new Error(`[@praxisjs/ssg] "${root}" has no default export — expected the root component.`);
|
|
100
|
+
}
|
|
101
|
+
if (!mod.routes) {
|
|
102
|
+
throw new Error(`[@praxisjs/ssg] "${root}" has no named "routes" export — expected the same route table passed to @Router([...]).`);
|
|
103
|
+
}
|
|
104
|
+
// Left open, closed by the caller after prerender() finishes: routes
|
|
105
|
+
// wrapped in Lazy(() => import(...)) keep their loader closures bound to
|
|
106
|
+
// *this* server's module runner for as long as they might still be
|
|
107
|
+
// invoked — closing it here would break lazy routes on every page after
|
|
108
|
+
// the first that needs them.
|
|
109
|
+
return { root: mod.default, routes: mod.routes, getStaticPaths: mod.getStaticPaths, close: () => server.close() };
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
await server.close();
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export function ssgPlugin(config) {
|
|
117
|
+
let resolvedConfig;
|
|
118
|
+
// Before anything below ever dynamically imports ./prerender — see the
|
|
119
|
+
// retry-hook section above for why this needs to happen here.
|
|
120
|
+
registerLoaderHook();
|
|
121
|
+
return {
|
|
122
|
+
name: "praxisjs:ssg",
|
|
123
|
+
apply: "build",
|
|
124
|
+
configResolved(c) {
|
|
125
|
+
resolvedConfig = c;
|
|
126
|
+
},
|
|
127
|
+
async closeBundle() {
|
|
128
|
+
const { prerender, withDomGlobals } = (await import("./prerender"));
|
|
129
|
+
const outDir = resolvedConfig.build.outDir;
|
|
130
|
+
const templatePath = join(resolvedConfig.root, outDir, "index.html");
|
|
131
|
+
const template = await readFile(templatePath, "utf-8");
|
|
132
|
+
const { root, routes, getStaticPaths, close } = await loadEntry(resolvedConfig, config.root, withDomGlobals);
|
|
133
|
+
let pages;
|
|
134
|
+
try {
|
|
135
|
+
pages = await prerender({
|
|
136
|
+
root,
|
|
137
|
+
routes,
|
|
138
|
+
routerOptions: config.routerOptions,
|
|
139
|
+
getStaticPaths,
|
|
140
|
+
template,
|
|
141
|
+
containerSelector: config.containerSelector,
|
|
142
|
+
hydrate: config.hydrate,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
await close();
|
|
147
|
+
}
|
|
148
|
+
for (const page of pages) {
|
|
149
|
+
const outFile = join(resolvedConfig.root, outDir, page.file);
|
|
150
|
+
await mkdir(dirname(outFile), { recursive: true });
|
|
151
|
+
await writeFile(outFile, page.html, "utf-8");
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ResolveFnOutput, ResolveHookContext } from "node:module";
|
|
2
|
+
type NextResolve = (specifier: string, context?: Partial<ResolveHookContext>) => ResolveFnOutput | Promise<ResolveFnOutput>;
|
|
3
|
+
export declare function resolve(specifier: string, context: ResolveHookContext, nextResolve: NextResolve): Promise<ResolveFnOutput>;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=loader-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader-hook.d.ts","sourceRoot":"","sources":["../src/loader-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAiBvE,KAAK,WAAW,GAAG,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAClC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,EAC3B,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,eAAe,CAAC,CAgB1B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Loaded as a standalone hook module via module.register() (see register.ts)
|
|
2
|
+
// — only used as a fallback on Node versions without the newer, in-thread
|
|
3
|
+
// module.registerHooks() API, so it must have zero relative imports of its
|
|
4
|
+
// own: this file needs to work *before* the extensionless-import problem it
|
|
5
|
+
// exists to fix has been worked around. See register.ts for the full story.
|
|
6
|
+
//
|
|
7
|
+
// Every other @praxisjs/* package is built with tsc, which emits relative
|
|
8
|
+
// specifiers exactly as written in source ("./router", not "./router.js") —
|
|
9
|
+
// harmless for every consumer so far, since they're all resolved through a
|
|
10
|
+
// bundler (Vite/esbuild/rollup), which resolves extensionless specifiers
|
|
11
|
+
// fine. Node's own ESM resolver does not: it's spec-strict and requires an
|
|
12
|
+
// explicit extension on relative specifiers. This hook retries with ".js" /
|
|
13
|
+
// "/index.js" on the two failure modes that produces.
|
|
14
|
+
const RETRYABLE_CODES = new Set(["ERR_MODULE_NOT_FOUND", "ERR_UNSUPPORTED_DIR_IMPORT"]);
|
|
15
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
16
|
+
try {
|
|
17
|
+
return await nextResolve(specifier, context);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const code = err.code;
|
|
21
|
+
if ((specifier.startsWith("./") || specifier.startsWith("../")) && code !== undefined && RETRYABLE_CODES.has(code)) {
|
|
22
|
+
for (const candidate of [`${specifier}.js`, `${specifier}/index.js`]) {
|
|
23
|
+
try {
|
|
24
|
+
return await nextResolve(candidate, context);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// try the next candidate
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=loader-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader-hook.js","sourceRoot":"","sources":["../src/loader-hook.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,sDAAsD;AACtD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,sBAAsB,EAAE,4BAA4B,CAAC,CAAC,CAAC;AAOxF,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,OAA2B,EAC3B,WAAwB;IAExB,IAAI,CAAC;QACH,OAAO,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACnH,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,SAAS,KAAK,EAAE,GAAG,SAAS,WAAW,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC;oBACH,OAAO,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAC/C,CAAC;gBAAC,MAAM,CAAC;oBACP,yBAAyB;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JSDOM } from "jsdom";
|
|
2
|
+
import type { RouterOptions } from "@praxisjs/router";
|
|
3
|
+
import type { ComponentConstructor } from "@praxisjs/shared/internal";
|
|
4
|
+
import { type GetStaticPaths, type RouteEntry } from "./routes";
|
|
5
|
+
export { flattenRoutes, isStaticPath, normalizeRoutes, pathToOutputFile, resolvePaths, type FlattenedRoute, type ResolvePathsConfig } from "./routes";
|
|
6
|
+
export interface PrerenderConfig {
|
|
7
|
+
/** Root component class — same one passed to `@Router([...])`/mounted by `main.tsx`. */
|
|
8
|
+
root: ComponentConstructor;
|
|
9
|
+
/** Same route table passed to `@Router([...])` — a mix of RouteDefinitions and bare @Route-decorated classes is fine, same as the decorator itself accepts. */
|
|
10
|
+
routes: RouteEntry[];
|
|
11
|
+
routerOptions?: RouterOptions;
|
|
12
|
+
/** Global fallback for expanding a dynamic route (`:slug`, `**`) into concrete paths — a route with its own `getStaticPaths` uses that instead. Dynamic routes with neither are skipped. */
|
|
13
|
+
getStaticPaths?: GetStaticPaths;
|
|
14
|
+
/** The built `index.html` content — reused as the template for every page. */
|
|
15
|
+
template: string;
|
|
16
|
+
/** CSS selector for the mount container inside `template`. Default: `#app`. */
|
|
17
|
+
containerSelector?: string;
|
|
18
|
+
/** Whether to mark output HTML for client-side hydration. Default: `true`. When `false`, the client falls back to a full remount instead of reconciling. */
|
|
19
|
+
hydrate?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface PrerenderedPage {
|
|
22
|
+
path: string;
|
|
23
|
+
/** Output file path relative to the SSG out dir, e.g. `about/index.html`. */
|
|
24
|
+
file: string;
|
|
25
|
+
html: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function prerender(config: PrerenderConfig): Promise<PrerenderedPage[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Runs `fn` with a fresh JSDOM's `window`/`document`/etc installed as globals
|
|
30
|
+
* for its duration, then restores whatever was there before. Exported so
|
|
31
|
+
* `index.ts`'s `loadEntry()` can use it too: `ssrLoadModule()`-ing the app's
|
|
32
|
+
* root module runs `@Router([...])`'s class decorator, which calls
|
|
33
|
+
* `createRouter()` immediately (at module-evaluation time) — that needs a
|
|
34
|
+
* `window` in place just as much as the real per-route render below does,
|
|
35
|
+
* even though it's not rendering anything itself yet.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withDomGlobals<T>(html: string, url: string, fn: (dom: JSDOM) => Promise<T>): Promise<T>;
|
|
38
|
+
//# sourceMappingURL=prerender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAQ9B,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,EAAmD,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAMjH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAItJ,MAAM,WAAW,eAAe;IAC9B,wFAAwF;IACxF,IAAI,EAAE,oBAAoB,CAAC;IAC3B,+JAA+J;IAC/J,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,4LAA4L;IAC5L,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4JAA4J;IAC5J,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgBnF;AAoDD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQ7G"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { JSDOM } from "jsdom";
|
|
2
|
+
import { flushPendingResources, isServerRenderPass, setServerRenderPass, } from "@praxisjs/core/internal";
|
|
3
|
+
import { resetHeadState } from "@praxisjs/head/internal";
|
|
4
|
+
import { createRouter } from "@praxisjs/router/internal";
|
|
5
|
+
import { getCurrentScope, mountComponent, render } from "@praxisjs/runtime";
|
|
6
|
+
import { normalizeRoutes, pathToOutputFile, resolvePaths } from "./routes";
|
|
7
|
+
// Re-exported here (this whole file, plus these, only resolves once the
|
|
8
|
+
// loader hook is registered — see register.ts) rather than from the main
|
|
9
|
+
// package entry, so importing `ssgPlugin` alone never needs router/core/head
|
|
10
|
+
// resolved statically. See index.ts.
|
|
11
|
+
export { flattenRoutes, isStaticPath, normalizeRoutes, pathToOutputFile, resolvePaths } from "./routes";
|
|
12
|
+
const SSG_MARKER = "data-praxis-ssg";
|
|
13
|
+
export async function prerender(config) {
|
|
14
|
+
const { getStaticPaths } = config;
|
|
15
|
+
const routes = normalizeRoutes(config.routes);
|
|
16
|
+
const paths = await resolvePaths({ routes, getStaticPaths });
|
|
17
|
+
const wasServerPass = isServerRenderPass();
|
|
18
|
+
setServerRenderPass(true);
|
|
19
|
+
try {
|
|
20
|
+
const pages = [];
|
|
21
|
+
for (const path of paths) {
|
|
22
|
+
pages.push(await renderPage(path, { ...config, routes }));
|
|
23
|
+
}
|
|
24
|
+
return pages;
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setServerRenderPass(wasServerPass);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const PATCHED_GLOBALS = ["window", "document", "navigator", "history", "location", "Node", "Comment", "Text", "Element", "HTMLElement"];
|
|
31
|
+
// Modern Node already defines some of these itself (globalThis.navigator is a
|
|
32
|
+
// getter-only built-in since Node 21) — plain assignment throws on those, so
|
|
33
|
+
// every property is installed *and* restored via defineProperty, capturing
|
|
34
|
+
// the original descriptor (not just its value) to put back exactly what was
|
|
35
|
+
// there, getter and all, rather than flattening it into a plain value.
|
|
36
|
+
function installDomGlobals(dom) {
|
|
37
|
+
const g = globalThis;
|
|
38
|
+
const snapshot = {};
|
|
39
|
+
for (const key of PATCHED_GLOBALS) {
|
|
40
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, key);
|
|
41
|
+
if (descriptor)
|
|
42
|
+
snapshot[key] = descriptor;
|
|
43
|
+
}
|
|
44
|
+
const { window } = dom;
|
|
45
|
+
const values = {
|
|
46
|
+
window,
|
|
47
|
+
document: window.document,
|
|
48
|
+
navigator: window.navigator,
|
|
49
|
+
history: window.history,
|
|
50
|
+
location: window.location,
|
|
51
|
+
Node: window.Node,
|
|
52
|
+
Comment: window.Comment,
|
|
53
|
+
Text: window.Text,
|
|
54
|
+
Element: window.Element,
|
|
55
|
+
HTMLElement: window.HTMLElement,
|
|
56
|
+
};
|
|
57
|
+
for (const key of PATCHED_GLOBALS) {
|
|
58
|
+
Object.defineProperty(g, key, { value: values[key], writable: true, configurable: true, enumerable: true });
|
|
59
|
+
}
|
|
60
|
+
return snapshot;
|
|
61
|
+
}
|
|
62
|
+
function restoreGlobals(snapshot) {
|
|
63
|
+
const g = globalThis;
|
|
64
|
+
for (const key of PATCHED_GLOBALS) {
|
|
65
|
+
const descriptor = snapshot[key];
|
|
66
|
+
if (descriptor) {
|
|
67
|
+
Object.defineProperty(g, key, descriptor);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- removing a global that genuinely didn't exist before installDomGlobals() added it
|
|
71
|
+
delete g[key];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Runs `fn` with a fresh JSDOM's `window`/`document`/etc installed as globals
|
|
77
|
+
* for its duration, then restores whatever was there before. Exported so
|
|
78
|
+
* `index.ts`'s `loadEntry()` can use it too: `ssrLoadModule()`-ing the app's
|
|
79
|
+
* root module runs `@Router([...])`'s class decorator, which calls
|
|
80
|
+
* `createRouter()` immediately (at module-evaluation time) — that needs a
|
|
81
|
+
* `window` in place just as much as the real per-route render below does,
|
|
82
|
+
* even though it's not rendering anything itself yet.
|
|
83
|
+
*/
|
|
84
|
+
export async function withDomGlobals(html, url, fn) {
|
|
85
|
+
const dom = new JSDOM(html, { url });
|
|
86
|
+
const snapshot = installDomGlobals(dom);
|
|
87
|
+
try {
|
|
88
|
+
return await fn(dom);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
restoreGlobals(snapshot);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function renderPage(path, config) {
|
|
95
|
+
const { root, routes, routerOptions, template, hydrate = true } = config;
|
|
96
|
+
const containerSelector = config.containerSelector ?? "#app";
|
|
97
|
+
return withDomGlobals(template, `http://localhost${path}`, async (dom) => {
|
|
98
|
+
resetHeadState();
|
|
99
|
+
const container = dom.window.document.querySelector(containerSelector);
|
|
100
|
+
if (!container) {
|
|
101
|
+
throw new Error(`[@praxisjs/ssg] No element matching "${containerSelector}" found in the HTML template.`);
|
|
102
|
+
}
|
|
103
|
+
createRouter(routes, routerOptions);
|
|
104
|
+
const dispose = render(() => mountComponent(root, {}, getCurrentScope()), container);
|
|
105
|
+
await flushPendingResources();
|
|
106
|
+
await Promise.resolve();
|
|
107
|
+
// Marked *after* rendering — the server always mounts fresh (create-mode);
|
|
108
|
+
// the marker only tells the client to hydrate instead of remounting.
|
|
109
|
+
if (hydrate)
|
|
110
|
+
container.setAttribute(SSG_MARKER, "1");
|
|
111
|
+
const html = dom.serialize();
|
|
112
|
+
dispose();
|
|
113
|
+
return { path, file: pathToOutputFile(path), html };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=prerender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"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"}
|