@praxisjs/ssg 0.1.2 → 0.1.4

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 (46) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +39 -15
  4. package/dist/index.js.map +1 -1
  5. package/dist/loader-hook.d.ts.map +1 -1
  6. package/dist/loader-hook.js +3 -13
  7. package/dist/loader-hook.js.map +1 -1
  8. package/dist/prerender.d.ts +10 -7
  9. package/dist/prerender.d.ts.map +1 -1
  10. package/dist/prerender.js +80 -63
  11. package/dist/prerender.js.map +1 -1
  12. package/package.json +16 -8
  13. package/src/index.ts +42 -16
  14. package/src/loader-hook.ts +3 -13
  15. package/src/prerender.ts +95 -80
  16. package/dist/__tests__/loader-hook.test.d.ts +0 -2
  17. package/dist/__tests__/loader-hook.test.d.ts.map +0 -1
  18. package/dist/__tests__/loader-hook.test.js +0 -66
  19. package/dist/__tests__/loader-hook.test.js.map +0 -1
  20. package/dist/__tests__/plugin.test.d.ts +0 -2
  21. package/dist/__tests__/plugin.test.d.ts.map +0 -1
  22. package/dist/__tests__/plugin.test.js +0 -128
  23. package/dist/__tests__/plugin.test.js.map +0 -1
  24. package/dist/__tests__/prerender.test.d.ts +0 -2
  25. package/dist/__tests__/prerender.test.d.ts.map +0 -1
  26. package/dist/__tests__/prerender.test.js +0 -211
  27. package/dist/__tests__/prerender.test.js.map +0 -1
  28. package/dist/__tests__/register-fallback.test.d.ts +0 -2
  29. package/dist/__tests__/register-fallback.test.d.ts.map +0 -1
  30. package/dist/__tests__/register-fallback.test.js +0 -20
  31. package/dist/__tests__/register-fallback.test.js.map +0 -1
  32. package/dist/__tests__/resolve-sync.test.d.ts +0 -2
  33. package/dist/__tests__/resolve-sync.test.d.ts.map +0 -1
  34. package/dist/__tests__/resolve-sync.test.js +0 -67
  35. package/dist/__tests__/resolve-sync.test.js.map +0 -1
  36. package/dist/__tests__/routes.test.d.ts +0 -2
  37. package/dist/__tests__/routes.test.d.ts.map +0 -1
  38. package/dist/__tests__/routes.test.js +0 -154
  39. package/dist/__tests__/routes.test.js.map +0 -1
  40. package/src/__tests__/loader-hook.test.ts +0 -76
  41. package/src/__tests__/plugin.test.ts +0 -161
  42. package/src/__tests__/prerender.test.ts +0 -181
  43. package/src/__tests__/register-fallback.test.ts +0 -23
  44. package/src/__tests__/resolve-sync.test.ts +0 -91
  45. package/src/__tests__/routes.test.ts +0 -175
  46. package/tsconfig.json +0 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @praxisjs/ssg
2
2
 
3
+ ## 0.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [d634a29]
8
+ - @praxisjs/runtime@0.8.1
9
+ - @praxisjs/router@2.3.1
10
+
11
+ ## 0.1.3
12
+
13
+ ### Patch Changes
14
+
15
+ - 75115a9: Tooling cleanup and fixes.
16
+ - `@praxisjs/ssg`: `prerender()` reuses one JSDOM window for the whole run and resets it before each route (fresh copy of the template, the route's URL, empty storage, no window globals left by the previous route) instead of parsing a new one per route: 300 routes went from about 900 ms to about 170 ms. The window is closed at the end, so timers a page leaves running no longer outlive the build. `localStorage`/`sessionStorage` are now the page's own (they were the host's, so `@Persisted` values leaked between routes). A page is unmounted even when serializing it fails. Pages are written in parallel, and an absolute `build.outDir` is no longer nested under the project root.
17
+ - `@praxisjs/mcp`: `praxisjs_get_page` accepts the `/docs/…` links `praxisjs_overview` lists (they used to 404), full praxisjs.org URLs, trailing slashes and `.md`/`.mdx`. Docs responses are cached in memory for 10 minutes, concurrent requests share one fetch and requests time out after 15 seconds. `praxisjs_get_install_command` checks package names strictly and drops duplicates. The server reports its real version.
18
+ - `@praxisjs/storybook`: re-rendering a story (new args, HMR) now disposes the previous render, which used to keep its effects alive. Each canvas tracks its own story, and `renderToCanvas` returns a teardown for Storybook. The preset registers a new `@praxisjs/storybook/preview` entry with `renderToCanvas` and a default `render` (the meta's `component` mounted with the args), so `.storybook/preview.ts` needs no setup. Preset paths work in folders with spaces and on Windows; story source is read asynchronously, and `.story.*` files get the source panel too.
19
+ - `praxisjs`: `doctor` flags `@praxisjs/*` versions that don't work together (`vite-plugin` 4.1+ or `jsx` 0.8+ with `runtime` older than 0.8, `decorators` 1.8+ with `core` older than 2.3). `upgrade` looks packages up in parallel, honors `npm_config_registry`, never moves a dependency to an older version, detects `bun.lock`, and reports a malformed `package.json` instead of crashing. The AI skills cover `For`/`Index`, `@Selector`, `createTransition()`, compiled templates, the upload composables and the devtools debugging options (`@Debug({ log, break })`, `@Trace({ slow })`, the profiler).
20
+ - `create-praxisjs`: `npm run lint` failed in every freshly scaffolded project, because the type-aware parser options also applied to `eslint.config.mjs`, which no tsconfig includes. They now apply to `.ts`/`.tsx` files only. The blog template renders its post list with `For`.
21
+
22
+ - 75115a9: Published packages no longer contain their tests: package builds compile `src` without `src/**/__tests__` (through a `tsconfig.build.json`), and each package's `files` leaves test sources and stale compiled tests out of the tarball. `pnpm typecheck` still type-checks the tests.
23
+ - 75115a9: Smaller startup: `render()` hydrates only after the new `enableHydration()` (called by `ssgPlugin()`), so apps that don't prerender leave the hydration code out. **Breaking:** an app that hydrates server-rendered HTML without `ssgPlugin()` must call `enableHydration()` before `render()`. Also, compiled templates whose tree the HTML parser reads back unchanged are emitted as markup and parsed once (`_$html`); decorated fields, methods and classes call shorter lowering helpers; the list view's internal members are `#private`.
24
+ - Updated dependencies [75115a9]
25
+ - Updated dependencies [75115a9]
26
+ - Updated dependencies [75115a9]
27
+ - Updated dependencies [75115a9]
28
+ - Updated dependencies [75115a9]
29
+ - Updated dependencies [75115a9]
30
+ - Updated dependencies [75115a9]
31
+ - Updated dependencies [75115a9]
32
+ - Updated dependencies [75115a9]
33
+ - Updated dependencies [75115a9]
34
+ - Updated dependencies [75115a9]
35
+ - Updated dependencies [75115a9]
36
+ - Updated dependencies [75115a9]
37
+ - Updated dependencies [75115a9]
38
+ - Updated dependencies [75115a9]
39
+ - @praxisjs/router@2.3.0
40
+ - @praxisjs/runtime@0.8.0
41
+ - @praxisjs/core@2.3.0
42
+ - @praxisjs/head@0.3.2
43
+ - @praxisjs/shared@0.3.2
44
+
3
45
  ## 0.1.2
4
46
 
5
47
  ### Patch Changes
@@ -1 +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"}
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;AAoFD,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAqDnD"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFile, writeFile, mkdir } from "node:fs/promises";
2
2
  import * as nodeModule from "node:module";
3
- import { dirname, join } from "node:path";
3
+ import { dirname, join, resolve } from "node:path";
4
4
  import { createServer } from "vite";
5
5
  // ── extensionless-relative-import retry hook ────────────────────────────────
6
6
  //
@@ -87,11 +87,8 @@ async function loadEntry(resolvedConfig, root, withDomGlobals) {
87
87
  optimizeDeps: { noDiscovery: true, include: [] },
88
88
  });
89
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()).
90
+ // @Router([...]) calls createRouter() at module-evaluation time, so importing the root module
91
+ // needs a window even though nothing is rendered yet. prerender() installs its own.
95
92
  const mod = await withDomGlobals("<!doctype html><html><head></head><body></body></html>", "http://localhost/", async () => {
96
93
  return (await server.ssrLoadModule(root));
97
94
  });
@@ -113,6 +110,22 @@ async function loadEntry(resolvedConfig, root, withDomGlobals) {
113
110
  throw err;
114
111
  }
115
112
  }
113
+ // Enough to keep the disk busy on large sites without running into the open-file limit.
114
+ const WRITE_CONCURRENCY = 16;
115
+ async function writePages(outDir, pages) {
116
+ let next = 0;
117
+ const worker = async () => {
118
+ while (next < pages.length) {
119
+ const page = pages[next++];
120
+ const outFile = join(outDir, page.file);
121
+ await mkdir(dirname(outFile), { recursive: true });
122
+ await writeFile(outFile, page.html, "utf-8");
123
+ }
124
+ };
125
+ await Promise.all(Array.from({ length: Math.min(WRITE_CONCURRENCY, pages.length) }, worker));
126
+ }
127
+ /** The script ssgPlugin() puts in front of the app's entry when hydration is on. */
128
+ const HYDRATION_ENTRY = 'import { enableHydration } from "@praxisjs/runtime";\nenableHydration();';
116
129
  export function ssgPlugin(config) {
117
130
  let resolvedConfig;
118
131
  // Before anything below ever dynamically imports ./prerender — see the
@@ -124,11 +137,26 @@ export function ssgPlugin(config) {
124
137
  configResolved(c) {
125
138
  resolvedConfig = c;
126
139
  },
140
+ // render() hydrates only once enableHydration() has run, which keeps the hydration code out of
141
+ // apps that never prerender. A module script in the head runs before the app's own entry, and
142
+ // "pre" lets Vite bundle it with that entry instead of leaving a bare import in the HTML.
143
+ transformIndexHtml: {
144
+ order: "pre",
145
+ handler() {
146
+ if (config.hydrate === false)
147
+ return [];
148
+ return [{
149
+ tag: "script",
150
+ attrs: { type: "module" },
151
+ children: HYDRATION_ENTRY,
152
+ injectTo: "head-prepend",
153
+ }];
154
+ },
155
+ },
127
156
  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");
157
+ const { prerender, withDomGlobals } = await import("./prerender");
158
+ const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
159
+ const template = await readFile(join(outDir, "index.html"), "utf-8");
132
160
  const { root, routes, getStaticPaths, close } = await loadEntry(resolvedConfig, config.root, withDomGlobals);
133
161
  let pages;
134
162
  try {
@@ -145,11 +173,7 @@ export function ssgPlugin(config) {
145
173
  finally {
146
174
  await close();
147
175
  }
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
- }
176
+ await writePages(outDir, pages);
153
177
  },
154
178
  };
155
179
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
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;AAOvE,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"}
@@ -1,16 +1,6 @@
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.
1
+ // Asynchronous twin of `resolveSync` in index.ts, loaded through module.register() on Node versions
2
+ // without module.registerHooks(). It must have no relative imports of its own: it has to work before
3
+ // the extensionless-import problem it fixes is worked around. See index.ts for the full story.
14
4
  const RETRYABLE_CODES = new Set(["ERR_MODULE_NOT_FOUND", "ERR_UNSUPPORTED_DIR_IMPORT"]);
15
5
  export async function resolve(specifier, context, nextResolve) {
16
6
  try {
@@ -1 +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"}
1
+ {"version":3,"file":"loader-hook.js","sourceRoot":"","sources":["../src/loader-hook.ts"],"names":[],"mappings":"AAEA,oGAAoG;AACpG,qGAAqG;AACrG,+FAA+F;AAC/F,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"}
@@ -24,15 +24,18 @@ export interface PrerenderedPage {
24
24
  file: string;
25
25
  html: string;
26
26
  }
27
+ /**
28
+ * Renders every route into a full HTML page. One JSDOM window serves the whole run (parsing a new
29
+ * one per route cost more than most renders) and is reset before each route: a fresh copy of the
30
+ * template's document, the route's URL, empty storage and none of the window globals the previous
31
+ * route added. The window is closed at the end, which also stops any timer a page left running.
32
+ */
27
33
  export declare function prerender(config: PrerenderConfig): Promise<PrerenderedPage[]>;
28
34
  /**
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.
35
+ * Runs `fn` with a fresh JSDOM's `window`/`document`/etc installed as globals, then restores what
36
+ * was there before and closes the window. Also used by index.ts's `loadEntry()`: loading the root
37
+ * module runs `@Router([...])`, which calls `createRouter()` at module-evaluation time and so needs
38
+ * a `window` even though nothing is rendered yet.
36
39
  */
37
40
  export declare function withDomGlobals<T>(html: string, url: string, fn: (dom: JSDOM) => Promise<T>): Promise<T>;
38
41
  //# sourceMappingURL=prerender.d.ts.map
@@ -1 +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"}
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;AAKjH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAKtJ,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;AAUD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA6BnF;AA8ED;;;;;GAKG;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,CAS7G"}
package/dist/prerender.js CHANGED
@@ -4,58 +4,99 @@ import { resetHeadState } from "@praxisjs/head/internal";
4
4
  import { createRouter } from "@praxisjs/router/internal";
5
5
  import { getCurrentScope, mountComponent, render } from "@praxisjs/runtime";
6
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.
7
+ // Re-exported here rather than from the main entry so importing `ssgPlugin` alone never needs
8
+ // router/core/head resolved statically: this module only loads once the loader hook in index.ts is
9
+ // registered.
11
10
  export { flattenRoutes, isStaticPath, normalizeRoutes, pathToOutputFile, resolvePaths } from "./routes";
12
11
  const SSG_MARKER = "data-praxis-ssg";
12
+ const ORIGIN = "http://localhost";
13
+ /**
14
+ * Renders every route into a full HTML page. One JSDOM window serves the whole run (parsing a new
15
+ * one per route cost more than most renders) and is reset before each route: a fresh copy of the
16
+ * template's document, the route's URL, empty storage and none of the window globals the previous
17
+ * route added. The window is closed at the end, which also stops any timer a page left running.
18
+ */
13
19
  export async function prerender(config) {
14
- const { getStaticPaths } = config;
15
20
  const routes = normalizeRoutes(config.routes);
16
- const paths = await resolvePaths({ routes, getStaticPaths });
21
+ const paths = await resolvePaths({ routes, getStaticPaths: config.getStaticPaths });
22
+ const options = {
23
+ root: config.root,
24
+ routes,
25
+ routerOptions: config.routerOptions,
26
+ containerSelector: config.containerSelector ?? "#app",
27
+ hydrate: config.hydrate ?? true,
28
+ };
17
29
  const wasServerPass = isServerRenderPass();
18
30
  setServerRenderPass(true);
19
31
  try {
20
- const pages = [];
21
- for (const path of paths) {
22
- pages.push(await renderPage(path, { ...config, routes }));
23
- }
24
- return pages;
32
+ return await withDomGlobals(config.template, `${ORIGIN}/`, async (dom) => {
33
+ const { window } = dom;
34
+ const pristine = window.document.documentElement.cloneNode(true);
35
+ const baseline = new Set(Object.getOwnPropertyNames(window));
36
+ const pages = [];
37
+ for (const path of paths) {
38
+ resetWindow(dom, pristine, baseline, path);
39
+ pages.push(await renderPage(dom, path, options));
40
+ }
41
+ return pages;
42
+ });
25
43
  }
26
44
  finally {
27
45
  setServerRenderPass(wasServerPass);
28
46
  }
29
47
  }
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.
48
+ function resetWindow(dom, pristine, baseline, path) {
49
+ const { window } = dom;
50
+ const { document } = window;
51
+ document.replaceChild(pristine.cloneNode(true), document.documentElement);
52
+ window.history.replaceState(null, "", `${ORIGIN}${path}`);
53
+ window.localStorage.clear();
54
+ window.sessionStorage.clear();
55
+ const globals = window;
56
+ for (const key of Object.getOwnPropertyNames(window)) {
57
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- removing globals the previous route's code added
58
+ if (!baseline.has(key))
59
+ delete globals[key];
60
+ }
61
+ }
62
+ async function renderPage(dom, path, options) {
63
+ const { root, routes, routerOptions, containerSelector, hydrate } = options;
64
+ resetHeadState();
65
+ const container = dom.window.document.querySelector(containerSelector);
66
+ if (!container) {
67
+ throw new Error(`[@praxisjs/ssg] No element matching "${containerSelector}" found in the HTML template.`);
68
+ }
69
+ createRouter(routes, routerOptions);
70
+ const dispose = render(() => mountComponent(root, {}, getCurrentScope()), container);
71
+ try {
72
+ await flushPendingResources();
73
+ await Promise.resolve();
74
+ // The server always mounts fresh; the marker only tells the client to hydrate instead of remounting.
75
+ if (hydrate)
76
+ container.setAttribute(SSG_MARKER, "1");
77
+ return { path, file: pathToOutputFile(path), html: dom.serialize() };
78
+ }
79
+ finally {
80
+ dispose();
81
+ }
82
+ }
83
+ const PATCHED_GLOBALS = [
84
+ "window", "document", "navigator", "history", "location", "localStorage", "sessionStorage",
85
+ "Node", "Comment", "Text", "Element", "HTMLElement",
86
+ ];
87
+ // Modern Node defines some of these itself (globalThis.navigator is a getter-only built-in since
88
+ // Node 21), where plain assignment throws. Every property is installed and restored through
89
+ // defineProperty, keeping the original descriptor so a getter comes back as a getter.
36
90
  function installDomGlobals(dom) {
37
- const g = globalThis;
38
91
  const snapshot = {};
92
+ const { window } = dom;
93
+ const source = window;
39
94
  for (const key of PATCHED_GLOBALS) {
40
95
  const descriptor = Object.getOwnPropertyDescriptor(globalThis, key);
41
96
  if (descriptor)
42
97
  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 });
98
+ const value = key === "window" ? window : source[key];
99
+ Object.defineProperty(globalThis, key, { value, writable: true, configurable: true, enumerable: true });
59
100
  }
60
101
  return snapshot;
61
102
  }
@@ -73,13 +114,10 @@ function restoreGlobals(snapshot) {
73
114
  }
74
115
  }
75
116
  /**
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.
117
+ * Runs `fn` with a fresh JSDOM's `window`/`document`/etc installed as globals, then restores what
118
+ * was there before and closes the window. Also used by index.ts's `loadEntry()`: loading the root
119
+ * module runs `@Router([...])`, which calls `createRouter()` at module-evaluation time and so needs
120
+ * a `window` even though nothing is rendered yet.
83
121
  */
84
122
  export async function withDomGlobals(html, url, fn) {
85
123
  const dom = new JSDOM(html, { url });
@@ -89,28 +127,7 @@ export async function withDomGlobals(html, url, fn) {
89
127
  }
90
128
  finally {
91
129
  restoreGlobals(snapshot);
130
+ dom.window.close();
92
131
  }
93
132
  }
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
133
  //# sourceMappingURL=prerender.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisjs/ssg",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,13 +14,20 @@
14
14
  "types": "./dist/prerender.d.ts"
15
15
  }
16
16
  },
17
+ "files": [
18
+ "dist",
19
+ "src",
20
+ "!dist/**/__tests__",
21
+ "!src/**/__tests__",
22
+ "CHANGELOG.md"
23
+ ],
17
24
  "dependencies": {
18
25
  "jsdom": "^29.0.1",
19
- "@praxisjs/core": "2.2.0",
20
- "@praxisjs/head": "0.3.1",
21
- "@praxisjs/router": "2.2.2",
22
- "@praxisjs/runtime": "0.7.1",
23
- "@praxisjs/shared": "0.3.1"
26
+ "@praxisjs/core": "2.3.0",
27
+ "@praxisjs/head": "0.3.2",
28
+ "@praxisjs/router": "2.3.1",
29
+ "@praxisjs/runtime": "0.8.1",
30
+ "@praxisjs/shared": "0.3.2"
24
31
  },
25
32
  "peerDependencies": {
26
33
  "vite": ">=8.0.0"
@@ -32,7 +39,8 @@
32
39
  "vite": "^8.1.4"
33
40
  },
34
41
  "scripts": {
35
- "build": "tsc",
36
- "dev": "tsc --watch"
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "dev": "tsc -p tsconfig.build.json --watch",
44
+ "typecheck": "tsc --noEmit"
37
45
  }
38
46
  }
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFile, writeFile, mkdir } from "node:fs/promises";
2
2
  import * as nodeModule from "node:module";
3
- import { dirname, join } from "node:path";
3
+ import { dirname, join, resolve } from "node:path";
4
4
 
5
5
  import { createServer, type Plugin, type ResolvedConfig } from "vite";
6
6
 
@@ -161,11 +161,8 @@ async function loadEntry(
161
161
  optimizeDeps: { noDiscovery: true, include: [] },
162
162
  });
163
163
  try {
164
- // @Router([...])'s class decorator calls createRouter() at
165
- // module-evaluation time — ssrLoadModule() below runs that as a side
166
- // effect of importing the root module, so it needs *a* window in place
167
- // already, even though nothing is actually being rendered yet (the real,
168
- // per-route window gets installed later, in prerender()).
164
+ // @Router([...]) calls createRouter() at module-evaluation time, so importing the root module
165
+ // needs a window even though nothing is rendered yet. prerender() installs its own.
169
166
  const mod = await withDomGlobals("<!doctype html><html><head></head><body></body></html>", "http://localhost/", async () => {
170
167
  return (await server.ssrLoadModule(root)) as RootModule;
171
168
  });
@@ -187,6 +184,25 @@ async function loadEntry(
187
184
  }
188
185
  }
189
186
 
187
+ // Enough to keep the disk busy on large sites without running into the open-file limit.
188
+ const WRITE_CONCURRENCY = 16;
189
+
190
+ async function writePages(outDir: string, pages: PrerenderModule.PrerenderedPage[]): Promise<void> {
191
+ let next = 0;
192
+ const worker = async (): Promise<void> => {
193
+ while (next < pages.length) {
194
+ const page = pages[next++];
195
+ const outFile = join(outDir, page.file);
196
+ await mkdir(dirname(outFile), { recursive: true });
197
+ await writeFile(outFile, page.html, "utf-8");
198
+ }
199
+ };
200
+ await Promise.all(Array.from({ length: Math.min(WRITE_CONCURRENCY, pages.length) }, worker));
201
+ }
202
+
203
+ /** The script ssgPlugin() puts in front of the app's entry when hydration is on. */
204
+ const HYDRATION_ENTRY = 'import { enableHydration } from "@praxisjs/runtime";\nenableHydration();';
205
+
190
206
  export function ssgPlugin(config: SSGConfig): Plugin {
191
207
  let resolvedConfig: ResolvedConfig;
192
208
 
@@ -200,15 +216,29 @@ export function ssgPlugin(config: SSGConfig): Plugin {
200
216
  configResolved(c) {
201
217
  resolvedConfig = c;
202
218
  },
219
+ // render() hydrates only once enableHydration() has run, which keeps the hydration code out of
220
+ // apps that never prerender. A module script in the head runs before the app's own entry, and
221
+ // "pre" lets Vite bundle it with that entry instead of leaving a bare import in the HTML.
222
+ transformIndexHtml: {
223
+ order: "pre",
224
+ handler() {
225
+ if (config.hydrate === false) return [];
226
+ return [{
227
+ tag: "script",
228
+ attrs: { type: "module" },
229
+ children: HYDRATION_ENTRY,
230
+ injectTo: "head-prepend",
231
+ }];
232
+ },
233
+ },
203
234
  async closeBundle() {
204
- const { prerender, withDomGlobals } = (await import("./prerender"));
235
+ const { prerender, withDomGlobals } = await import("./prerender");
205
236
 
206
- const outDir = resolvedConfig.build.outDir;
207
- const templatePath = join(resolvedConfig.root, outDir, "index.html");
208
- const template = await readFile(templatePath, "utf-8");
237
+ const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
238
+ const template = await readFile(join(outDir, "index.html"), "utf-8");
209
239
  const { root, routes, getStaticPaths, close } = await loadEntry(resolvedConfig, config.root, withDomGlobals);
210
240
 
211
- let pages;
241
+ let pages: PrerenderModule.PrerenderedPage[];
212
242
  try {
213
243
  pages = await prerender({
214
244
  root,
@@ -223,11 +253,7 @@ export function ssgPlugin(config: SSGConfig): Plugin {
223
253
  await close();
224
254
  }
225
255
 
226
- for (const page of pages) {
227
- const outFile = join(resolvedConfig.root, outDir, page.file);
228
- await mkdir(dirname(outFile), { recursive: true });
229
- await writeFile(outFile, page.html, "utf-8");
230
- }
256
+ await writePages(outDir, pages);
231
257
  },
232
258
  };
233
259
  }
@@ -1,18 +1,8 @@
1
1
  import type { ResolveFnOutput, ResolveHookContext } from "node:module";
2
2
 
3
- // Loaded as a standalone hook module via module.register() (see register.ts)
4
- // — only used as a fallback on Node versions without the newer, in-thread
5
- // module.registerHooks() API, so it must have zero relative imports of its
6
- // own: this file needs to work *before* the extensionless-import problem it
7
- // exists to fix has been worked around. See register.ts for the full story.
8
- //
9
- // Every other @praxisjs/* package is built with tsc, which emits relative
10
- // specifiers exactly as written in source ("./router", not "./router.js") —
11
- // harmless for every consumer so far, since they're all resolved through a
12
- // bundler (Vite/esbuild/rollup), which resolves extensionless specifiers
13
- // fine. Node's own ESM resolver does not: it's spec-strict and requires an
14
- // explicit extension on relative specifiers. This hook retries with ".js" /
15
- // "/index.js" on the two failure modes that produces.
3
+ // Asynchronous twin of `resolveSync` in index.ts, loaded through module.register() on Node versions
4
+ // without module.registerHooks(). It must have no relative imports of its own: it has to work before
5
+ // the extensionless-import problem it fixes is worked around. See index.ts for the full story.
16
6
  const RETRYABLE_CODES = new Set(["ERR_MODULE_NOT_FOUND", "ERR_UNSUPPORTED_DIR_IMPORT"]);
17
7
 
18
8
  type NextResolve = (