nodality 1.0.144 → 1.0.146
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/layout/animator.js +1 -1
- package/layout/audio.js +1 -1
- package/layout/audionew.js +1 -1
- package/layout/base-2.js +1 -1
- package/layout/base.js +1 -1
- package/layout/beta-desktop-bar.js +1 -1
- package/layout/beta-mobile-bar.js +1 -1
- package/layout/box.js +1 -1
- package/layout/button.js +1 -1
- package/layout/cards.js +1 -1
- package/layout/center.js +1 -1
- package/layout/checkbox.js +1 -1
- package/layout/circle.js +1 -1
- package/layout/clean-row.js +1 -1
- package/layout/code.js +1 -1
- package/layout/container.js +1 -1
- package/layout/custom.js +1 -1
- package/layout/div-image.js +1 -1
- package/layout/dropdown-2025.js +1 -1
- package/layout/dropdown.js +1 -1
- package/layout/empty-element.js +1 -1
- package/layout/external-stylesheet.js +1 -1
- package/layout/flex-card.js +1 -1
- package/layout/flex-grid.js +1 -1
- package/layout/flex-row.js +1 -1
- package/layout/footer.js +1 -1
- package/layout/form-components/custom.js +1 -1
- package/layout/form-components/data-list.js +1 -1
- package/layout/form-components/floating-input.js +1 -1
- package/layout/form-components/form-all.js +1 -1
- package/layout/form-components/form.js +1 -1
- package/layout/form-components/image-picker.js +1 -1
- package/layout/form-components/picker.js +1 -1
- package/layout/form-components/radio.js +1 -1
- package/layout/form-components/radiogroup.js +1 -1
- package/layout/form-components/range.js +1 -1
- package/layout/free.js +1 -1
- package/layout/grid-new.js +1 -1
- package/layout/grid-switcher.js +1 -1
- package/layout/grid.js +1 -1
- package/layout/group.js +1 -1
- package/layout/header.js +1 -1
- package/layout/horizontal-scroller.js +1 -1
- package/layout/image-old.js +1 -1
- package/layout/image.js +1 -1
- package/layout/index.js +1 -1
- package/layout/label.js +1 -1
- package/layout/link.js +1 -1
- package/layout/list-OLD.js +1 -1
- package/layout/list.js +1 -1
- package/layout/meta-adder.js +1 -1
- package/layout/modal-2025.js +1 -1
- package/layout/modernwrap.js +1 -1
- package/layout/multiswitcher.js +1 -1
- package/layout/multiswitcherBeta.js +1 -1
- package/layout/nav-bar.js +1 -1
- package/layout/nav-factor/custom-div.js +1 -1
- package/layout/navBar-OLD.js +1 -1
- package/layout/new-flat-adder.js +1 -1
- package/layout/new-nav-bar.js +1 -1
- package/layout/offset-container.js +1 -1
- package/layout/polygon.js +1 -1
- package/layout/prerender.js +365 -0
- package/layout/progress.js +1 -1
- package/layout/row.js +1 -1
- package/layout/saved-new-nav-bar.js +1 -1
- package/layout/scroll-video.js +1 -1
- package/layout/side-bar.js +1 -1
- package/layout/side-nav-bar.js +1 -1
- package/layout/simple-bar.js +1 -1
- package/layout/slider-2025.js +1 -1
- package/layout/spacer.js +1 -1
- package/layout/stack.js +1 -1
- package/layout/styler.js +1 -1
- package/layout/svg.js +1 -1
- package/layout/switcher.js +1 -1
- package/layout/table.js +1 -1
- package/layout/text-field.js +1 -1
- package/layout/text.js +1 -1
- package/layout/ulist.js +1 -1
- package/layout/video.js +1 -1
- package/layout/without-new.js +1 -1
- package/layout/wrap.js +1 -1
- package/layout/zoom-card.js +1 -1
- package/lib/card-getter.js +1 -1
- package/lib/designer.js +1 -1
- package/lib/element-mapper.js +1 -1
- package/lib/keyframe-animation.js +1 -1
- package/lib/link-getter.js +1 -1
- package/lib/scroll-video.js +1 -1
- package/lib/stacker.js +1 -1
- package/lib/theme.js +1 -1
- package/lib/transform-anim.js +1 -1
- package/package.json +3 -2
package/layout/animator.js
CHANGED
package/layout/audio.js
CHANGED
package/layout/audionew.js
CHANGED
package/layout/base-2.js
CHANGED
package/layout/base.js
CHANGED
package/layout/box.js
CHANGED
package/layout/button.js
CHANGED
package/layout/cards.js
CHANGED
package/layout/center.js
CHANGED
package/layout/checkbox.js
CHANGED
package/layout/circle.js
CHANGED
package/layout/clean-row.js
CHANGED
package/layout/code.js
CHANGED
package/layout/container.js
CHANGED
package/layout/custom.js
CHANGED
package/layout/div-image.js
CHANGED
package/layout/dropdown-2025.js
CHANGED
package/layout/dropdown.js
CHANGED
package/layout/empty-element.js
CHANGED
package/layout/flex-card.js
CHANGED
package/layout/flex-grid.js
CHANGED
package/layout/flex-row.js
CHANGED
package/layout/footer.js
CHANGED
package/layout/free.js
CHANGED
package/layout/grid-new.js
CHANGED
package/layout/grid-switcher.js
CHANGED
package/layout/grid.js
CHANGED
package/layout/group.js
CHANGED
package/layout/header.js
CHANGED
package/layout/image-old.js
CHANGED
package/layout/image.js
CHANGED
package/layout/index.js
CHANGED
package/layout/label.js
CHANGED
package/layout/link.js
CHANGED
package/layout/list-OLD.js
CHANGED
package/layout/list.js
CHANGED
package/layout/meta-adder.js
CHANGED
package/layout/modal-2025.js
CHANGED
package/layout/modernwrap.js
CHANGED
package/layout/multiswitcher.js
CHANGED
package/layout/nav-bar.js
CHANGED
package/layout/navBar-OLD.js
CHANGED
package/layout/new-flat-adder.js
CHANGED
package/layout/new-nav-bar.js
CHANGED
package/layout/polygon.js
CHANGED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* nodality v1.0.146
|
|
3
|
+
* (c) 2026 Filip Vabrousek
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ════════════════════════════════════════════════════════════════════
|
|
8
|
+
// Nodality — static-site prerender helper
|
|
9
|
+
// ════════════════════════════════════════════════════════════════════
|
|
10
|
+
//
|
|
11
|
+
// Adds the ability to capture the HTML produced by Nodality primitives
|
|
12
|
+
// and write it to a file at BUILD TIME, without changing any other
|
|
13
|
+
// library file or the runtime mount path. The caller's existing
|
|
14
|
+
// `app.js` (or per-page script) keeps mounting dynamically in the
|
|
15
|
+
// browser — this module just runs the same builder once in Node,
|
|
16
|
+
// inside a jsdom-simulated DOM, and saves the resulting HTML so
|
|
17
|
+
// crawlers / AI bots / social-unfurl bots see the rendered page
|
|
18
|
+
// before any JavaScript executes.
|
|
19
|
+
//
|
|
20
|
+
// ─── Why this exists ────────────────────────────────────────────────
|
|
21
|
+
// Nodality apps construct the DOM imperatively in the browser
|
|
22
|
+
// (`new Text().set({...}).render("#mount")`). That works for users
|
|
23
|
+
// but leaves the raw HTML body essentially empty — `<div id="mount">
|
|
24
|
+
// </div>` plus a script tag. Most AI crawlers (GPTBot, ClaudeBot,
|
|
25
|
+
// PerplexityBot, CCBot) and every social-unfurl bot (Slack, LinkedIn,
|
|
26
|
+
// WhatsApp, iMessage, X) don't execute JavaScript, so they see nothing.
|
|
27
|
+
// Pre-rendering the same builder in Node gives those crawlers the
|
|
28
|
+
// real content. Users still get the JS-hydrated experience on top.
|
|
29
|
+
//
|
|
30
|
+
// ─── Design constraint ──────────────────────────────────────────────
|
|
31
|
+
// This file is the ONLY new module in the library; index.js,
|
|
32
|
+
// package.json, webpack.config.js, and every existing primitive are
|
|
33
|
+
// untouched. The caller is expected to import from this file path
|
|
34
|
+
// directly, e.g.:
|
|
35
|
+
//
|
|
36
|
+
// import { prerender } from "nodality/layout/prerender.js";
|
|
37
|
+
//
|
|
38
|
+
// If the caller wants to add a top-level export later, that's a
|
|
39
|
+
// one-line addition to `index.js` they can make on their own.
|
|
40
|
+
//
|
|
41
|
+
// ─── Usage ──────────────────────────────────────────────────────────
|
|
42
|
+
//
|
|
43
|
+
// import { prerender } from "nodality/layout/prerender.js";
|
|
44
|
+
//
|
|
45
|
+
// await prerender({
|
|
46
|
+
// template: "./page-template.html", // HTML shell (head + empty body)
|
|
47
|
+
// mount: "#mount", // selector inside the template
|
|
48
|
+
// locale: "de", // optional: pre-seeded into localStorage
|
|
49
|
+
// url: "https://h7active.com/", // jsdom base URL (used for relative paths)
|
|
50
|
+
// build: async (window) => {
|
|
51
|
+
// // Runs inside the jsdom-simulated browser. `window.document`,
|
|
52
|
+
// // `window.localStorage`, etc. are available globally for the
|
|
53
|
+
// // duration of the call. Import or invoke your page builder
|
|
54
|
+
// // here; it can use Nodality primitives exactly as in the
|
|
55
|
+
// // browser.
|
|
56
|
+
// await import("./app.js");
|
|
57
|
+
// },
|
|
58
|
+
// output: "./upload/de/index.html", // file to write
|
|
59
|
+
// });
|
|
60
|
+
//
|
|
61
|
+
// The function spins up jsdom, runs your builder, captures the full
|
|
62
|
+
// `<!DOCTYPE html>…</html>` document (preserving the template's
|
|
63
|
+
// <head>, the <script> tags pointing at runtime JS, AND the now-
|
|
64
|
+
// populated body), and writes it to `output`. When the live browser
|
|
65
|
+
// later loads that file, the runtime `app.js` still executes and
|
|
66
|
+
// remounts — the navigation, dropdowns, language switcher, scroll
|
|
67
|
+
// handlers, etc. all continue to work dynamically.
|
|
68
|
+
//
|
|
69
|
+
// ─── Peer dependency ────────────────────────────────────────────────
|
|
70
|
+
// `jsdom` is imported only by this file. It's listed as an optional
|
|
71
|
+
// peer dependency so the browser bundle stays small. Callers using
|
|
72
|
+
// prerender must `npm install jsdom` in their build environment.
|
|
73
|
+
|
|
74
|
+
import { promises as fs } from "node:fs";
|
|
75
|
+
import path from "node:path";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Render a Nodality-built page to a static HTML file.
|
|
79
|
+
*
|
|
80
|
+
* @param {object} opts
|
|
81
|
+
* @param {string} opts.template — Path to the HTML shell file. Must contain
|
|
82
|
+
* `<head>` and an empty mount container (e.g. `<div id="mount"></div>`).
|
|
83
|
+
* @param {string} [opts.mount="#mount"] — CSS selector for the mount point
|
|
84
|
+
* inside the template. The function verifies it exists before running
|
|
85
|
+
* the builder; missing selector throws (catches typos early).
|
|
86
|
+
* @param {string} [opts.locale] — If set, written to `window.localStorage`
|
|
87
|
+
* under the key `h7lang` before the builder runs. Lets locale-aware
|
|
88
|
+
* builders (e.g. those calling `detectLang()`) produce the correct
|
|
89
|
+
* translation without further plumbing.
|
|
90
|
+
* @param {string} [opts.localStorageKey="h7lang"] — Override the
|
|
91
|
+
* localStorage key used for the locale handshake if your app uses a
|
|
92
|
+
* different name.
|
|
93
|
+
* @param {string} [opts.url="http://localhost/"] — Base URL for the jsdom
|
|
94
|
+
* window. Affects `window.location`, relative-URL resolution, and the
|
|
95
|
+
* `Document.URL` your code may read.
|
|
96
|
+
* @param {(window: Window) => (void | Promise<void>)} opts.build —
|
|
97
|
+
* Async function that runs inside the simulated browser. Receives the
|
|
98
|
+
* jsdom `window` object. Should construct the page via Nodality
|
|
99
|
+
* primitives and call `.render(mount)`. Awaited so dynamic imports
|
|
100
|
+
* and async setup complete before serialization.
|
|
101
|
+
* @param {string} opts.output — Path where the rendered HTML is written.
|
|
102
|
+
* Parent directories are created automatically.
|
|
103
|
+
*
|
|
104
|
+
* @returns {Promise<{ bytes: number, output: string }>} — Resolves once
|
|
105
|
+
* the file is on disk. `bytes` is the rendered document size.
|
|
106
|
+
*/
|
|
107
|
+
export async function prerender({
|
|
108
|
+
template,
|
|
109
|
+
mount = "#mount",
|
|
110
|
+
locale,
|
|
111
|
+
localStorageKey = "h7lang",
|
|
112
|
+
url = "http://localhost/",
|
|
113
|
+
build,
|
|
114
|
+
output,
|
|
115
|
+
}) {
|
|
116
|
+
if (!template) throw new Error("prerender: `template` is required");
|
|
117
|
+
if (!output) throw new Error("prerender: `output` is required");
|
|
118
|
+
if (typeof build !== "function") {
|
|
119
|
+
throw new Error("prerender: `build` must be an async function");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Lazy import jsdom so this file can be imported in environments that
|
|
123
|
+
// don't have it installed (it's a peer dep). The error message is
|
|
124
|
+
// explicit because the default "Cannot find package 'jsdom'" stack
|
|
125
|
+
// trace is opaque to users who haven't seen it before.
|
|
126
|
+
let JSDOM;
|
|
127
|
+
try {
|
|
128
|
+
({ JSDOM } = await import("jsdom"));
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"prerender: jsdom is required at build time. " +
|
|
132
|
+
"Install it in the consuming project: `npm install --save-dev jsdom`. " +
|
|
133
|
+
"Original error: " + err.message
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Read the HTML shell. Use absolute path resolution so relative
|
|
138
|
+
// `template` arguments (the common case from a build script) work
|
|
139
|
+
// regardless of process.cwd().
|
|
140
|
+
const templatePath = path.resolve(template);
|
|
141
|
+
const html = await fs.readFile(templatePath, "utf8");
|
|
142
|
+
|
|
143
|
+
// ─── jsdom setup ─────────────────────────────────────────────────
|
|
144
|
+
//
|
|
145
|
+
// `runScripts: "outside-only"` is the critical flag: it gives the
|
|
146
|
+
// window a working JS execution context (so `new Text()` etc. work
|
|
147
|
+
// inside the builder) BUT does NOT auto-execute `<script>` tags
|
|
148
|
+
// from the template. We deliberately don't want the template's
|
|
149
|
+
// `<script src="./app.js">` to run inside jsdom — paths there are
|
|
150
|
+
// production-relative and would 404; instead the caller's `build`
|
|
151
|
+
// function imports the same modules through Node's module system,
|
|
152
|
+
// which resolves them correctly.
|
|
153
|
+
//
|
|
154
|
+
// `pretendToBeVisual: true` enables requestAnimationFrame and a few
|
|
155
|
+
// related visual APIs. Cheap and prevents subtle "rAF is not a
|
|
156
|
+
// function" errors from libraries that schedule layout work.
|
|
157
|
+
const dom = new JSDOM(html, {
|
|
158
|
+
url,
|
|
159
|
+
runScripts: "outside-only",
|
|
160
|
+
pretendToBeVisual: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const { window } = dom;
|
|
164
|
+
|
|
165
|
+
// Optional locale seed — written before any global proxying so the
|
|
166
|
+
// builder sees it as soon as the proxy is in place.
|
|
167
|
+
if (locale) {
|
|
168
|
+
window.localStorage.setItem(localStorageKey, locale);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─── Browser API shims jsdom doesn't ship ───────────────────────
|
|
172
|
+
//
|
|
173
|
+
// These are no-ops; their job is to prevent ReferenceErrors when
|
|
174
|
+
// the builder constructs objects that reference observation APIs.
|
|
175
|
+
// Real observation isn't meaningful at build time (no scrolling,
|
|
176
|
+
// no resizing, no layout) and SSG doesn't need it.
|
|
177
|
+
if (!window.IntersectionObserver) {
|
|
178
|
+
window.IntersectionObserver = class {
|
|
179
|
+
constructor() {}
|
|
180
|
+
observe() {}
|
|
181
|
+
unobserve() {}
|
|
182
|
+
disconnect() {}
|
|
183
|
+
takeRecords() { return []; }
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (!window.ResizeObserver) {
|
|
187
|
+
window.ResizeObserver = class {
|
|
188
|
+
constructor() {}
|
|
189
|
+
observe() {}
|
|
190
|
+
unobserve() {}
|
|
191
|
+
disconnect() {}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (!window.matchMedia) {
|
|
195
|
+
window.matchMedia = () => ({
|
|
196
|
+
matches: false,
|
|
197
|
+
media: "",
|
|
198
|
+
onchange: null,
|
|
199
|
+
addListener() {},
|
|
200
|
+
removeListener() {},
|
|
201
|
+
addEventListener() {},
|
|
202
|
+
removeEventListener() {},
|
|
203
|
+
dispatchEvent() { return false; },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// Some libraries probe these for code-splitting / analytics. Stub
|
|
207
|
+
// them as harmless no-ops so the builder doesn't crash; the live
|
|
208
|
+
// browser still uses the real implementations.
|
|
209
|
+
if (!window.fetch) {
|
|
210
|
+
window.fetch = () =>
|
|
211
|
+
Promise.resolve({ ok: true, status: 200, json: async () => ({}), text: async () => "" });
|
|
212
|
+
}
|
|
213
|
+
if (!window.dataLayer) window.dataLayer = [];
|
|
214
|
+
if (!window.gtag) window.gtag = () => {};
|
|
215
|
+
|
|
216
|
+
// ─── Global proxying ────────────────────────────────────────────
|
|
217
|
+
//
|
|
218
|
+
// Nodality primitives reach for `document`, `window`, etc. as bare
|
|
219
|
+
// globals — they were written for the browser, where those are
|
|
220
|
+
// already present. To run them in Node we hoist the jsdom-provided
|
|
221
|
+
// versions onto `globalThis` for the duration of the build call.
|
|
222
|
+
//
|
|
223
|
+
// We snapshot any prior values and restore them in the `finally`
|
|
224
|
+
// block so the parent process isn't polluted between successive
|
|
225
|
+
// prerender() invocations (e.g. a build loop over all locales).
|
|
226
|
+
const PROXIED = [
|
|
227
|
+
"window", "document", "localStorage", "sessionStorage", "navigator",
|
|
228
|
+
"location", "HTMLElement", "Element", "Node", "DocumentFragment",
|
|
229
|
+
"Event", "CustomEvent", "MouseEvent", "KeyboardEvent", "PointerEvent",
|
|
230
|
+
"IntersectionObserver", "ResizeObserver", "matchMedia",
|
|
231
|
+
"requestAnimationFrame", "cancelAnimationFrame", "getComputedStyle",
|
|
232
|
+
"fetch", "dataLayer", "gtag",
|
|
233
|
+
];
|
|
234
|
+
// Some Node versions (22+) make certain globals — most notably
|
|
235
|
+
// `navigator` — getter-only on `globalThis`, which means a plain
|
|
236
|
+
// assignment (`globalThis.navigator = window.navigator`) throws
|
|
237
|
+
// "Cannot set property navigator of #<Object> which has only a
|
|
238
|
+
// getter". Inspect the property descriptor and skip read-only
|
|
239
|
+
// ones — the live browser's native object is fine to leave alone
|
|
240
|
+
// since jsdom provides its own copy on `window` that the builder
|
|
241
|
+
// can reach via `window.navigator`. Same defensive check for any
|
|
242
|
+
// other future getter-only globals Node may add.
|
|
243
|
+
const originalGlobals = {};
|
|
244
|
+
for (const key of PROXIED) {
|
|
245
|
+
if (key in globalThis) originalGlobals[key] = globalThis[key];
|
|
246
|
+
if (!(key in window)) continue;
|
|
247
|
+
const desc = Object.getOwnPropertyDescriptor(globalThis, key);
|
|
248
|
+
if (desc && desc.set === undefined && desc.writable === false) {
|
|
249
|
+
// Read-only built-in (e.g. Node 22+ `navigator`). Skip — the
|
|
250
|
+
// builder can still reach the jsdom copy via `window.<key>`.
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
globalThis[key] = window[key];
|
|
255
|
+
} catch {
|
|
256
|
+
// Belt-and-braces: if descriptor inspection missed something
|
|
257
|
+
// (host-defined exotic, frozen prototype chain), swallow the
|
|
258
|
+
// assignment failure rather than aborting the whole render.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
// Sanity-check the mount point exists. Catches the common typo of
|
|
264
|
+
// changing the mount selector in the template without updating it
|
|
265
|
+
// here (or vice versa). Without this guard the builder runs but
|
|
266
|
+
// silently writes nothing useful.
|
|
267
|
+
if (mount && !window.document.querySelector(mount)) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`prerender: mount selector "${mount}" not found in template "${templatePath}". ` +
|
|
270
|
+
`Check that the template's <body> contains <div id="${mount.replace(/^#/, "")}"></div>.`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Run the builder. Errors propagate so the caller knows the
|
|
275
|
+
// page didn't render — better than writing a half-built file.
|
|
276
|
+
await build(window);
|
|
277
|
+
|
|
278
|
+
// Drain the microtask queue so any queued promise.then handlers
|
|
279
|
+
// (e.g. dynamic imports inside the builder, fetch().then chains)
|
|
280
|
+
// finish before we serialize. jsdom's macrotask scheduling means
|
|
281
|
+
// setTimeout(0) callbacks may NOT fire — builders that rely on
|
|
282
|
+
// those won't have their output captured. Keep builders sync or
|
|
283
|
+
// promise-based for full coverage.
|
|
284
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
285
|
+
|
|
286
|
+
// Serialize the full document. `dom.serialize()` returns
|
|
287
|
+
// `<!DOCTYPE html><html>...</html>` including the template's
|
|
288
|
+
// head + the now-populated body. The runtime <script> tags from
|
|
289
|
+
// the template are preserved verbatim, so the live browser will
|
|
290
|
+
// re-execute app.js and re-mount on top of the static DOM.
|
|
291
|
+
const rendered = dom.serialize();
|
|
292
|
+
|
|
293
|
+
// Ensure the output directory exists. Writing to a path like
|
|
294
|
+
// `./upload/de/index.html` would otherwise fail if `de/` doesn't
|
|
295
|
+
// exist yet.
|
|
296
|
+
const outputPath = path.resolve(output);
|
|
297
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
298
|
+
await fs.writeFile(outputPath, rendered, "utf8");
|
|
299
|
+
|
|
300
|
+
return { bytes: rendered.length, output: outputPath };
|
|
301
|
+
} finally {
|
|
302
|
+
// Restore globalThis to its pre-call state. Critical when the
|
|
303
|
+
// caller runs prerender() in a loop — without this, the second
|
|
304
|
+
// invocation would inherit the FIRST jsdom's window, which has
|
|
305
|
+
// already been closed.
|
|
306
|
+
for (const key of PROXIED) {
|
|
307
|
+
if (key in originalGlobals) {
|
|
308
|
+
globalThis[key] = originalGlobals[key];
|
|
309
|
+
} else {
|
|
310
|
+
delete globalThis[key];
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Release jsdom's internal resources (timers, parser state).
|
|
314
|
+
// Without this the Node process can keep ticking long after
|
|
315
|
+
// the build script "finishes" because jsdom's setInterval
|
|
316
|
+
// callbacks are still alive.
|
|
317
|
+
window.close();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Convenience wrapper: prerender the same page once per locale and
|
|
323
|
+
* write each rendered file to a different output path.
|
|
324
|
+
*
|
|
325
|
+
* @param {object} opts — Same as `prerender()`, but `output` is a
|
|
326
|
+
* function `(locale) => string` and `locale` is a list.
|
|
327
|
+
* @param {string[]} opts.locales — Locale codes to render, e.g.
|
|
328
|
+
* `["cs", "en", "sk", "de", "fr"]`. Each value is passed to both
|
|
329
|
+
* the builder (via localStorage) and the `outputFor(locale)` fn.
|
|
330
|
+
* @param {(locale: string) => string} opts.outputFor — Returns the
|
|
331
|
+
* destination path for each locale.
|
|
332
|
+
*
|
|
333
|
+
* @returns {Promise<Array<{ locale: string, bytes: number, output: string }>>}
|
|
334
|
+
*/
|
|
335
|
+
export async function prerenderEachLocale({
|
|
336
|
+
template,
|
|
337
|
+
mount,
|
|
338
|
+
url,
|
|
339
|
+
locales,
|
|
340
|
+
outputFor,
|
|
341
|
+
build,
|
|
342
|
+
localStorageKey,
|
|
343
|
+
}) {
|
|
344
|
+
if (!Array.isArray(locales) || locales.length === 0) {
|
|
345
|
+
throw new Error("prerenderEachLocale: `locales` must be a non-empty array");
|
|
346
|
+
}
|
|
347
|
+
if (typeof outputFor !== "function") {
|
|
348
|
+
throw new Error("prerenderEachLocale: `outputFor` must be (locale) => string");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const results = [];
|
|
352
|
+
for (const locale of locales) {
|
|
353
|
+
const result = await prerender({
|
|
354
|
+
template,
|
|
355
|
+
mount,
|
|
356
|
+
url,
|
|
357
|
+
locale,
|
|
358
|
+
localStorageKey,
|
|
359
|
+
build,
|
|
360
|
+
output: outputFor(locale),
|
|
361
|
+
});
|
|
362
|
+
results.push({ locale, ...result });
|
|
363
|
+
}
|
|
364
|
+
return results;
|
|
365
|
+
}
|
package/layout/progress.js
CHANGED
package/layout/row.js
CHANGED
package/layout/scroll-video.js
CHANGED
package/layout/side-bar.js
CHANGED
package/layout/side-nav-bar.js
CHANGED
package/layout/simple-bar.js
CHANGED
package/layout/slider-2025.js
CHANGED
package/layout/spacer.js
CHANGED
package/layout/stack.js
CHANGED
package/layout/styler.js
CHANGED
package/layout/svg.js
CHANGED
package/layout/switcher.js
CHANGED
package/layout/table.js
CHANGED
package/layout/text-field.js
CHANGED
package/layout/text.js
CHANGED
package/layout/ulist.js
CHANGED
package/layout/video.js
CHANGED
package/layout/without-new.js
CHANGED
package/layout/wrap.js
CHANGED
package/layout/zoom-card.js
CHANGED
package/lib/card-getter.js
CHANGED
package/lib/designer.js
CHANGED
package/lib/element-mapper.js
CHANGED
package/lib/link-getter.js
CHANGED
package/lib/scroll-video.js
CHANGED
package/lib/stacker.js
CHANGED
package/lib/theme.js
CHANGED
package/lib/transform-anim.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodality",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.146",
|
|
4
4
|
"description": "A lightweight library for declarative UI elements.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"require": "./dist/index.js",
|
|
15
15
|
"import": "./dist/index.esm.js"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"./ssg": "./layout/prerender.js"
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "webpack --config webpack.config.js",
|