nodality 1.0.164 → 1.0.165
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/bin/nodality.js +144 -99
- package/dist/bundle.umd.js +1 -1
- package/dist/designer.cjs.js +1 -1
- package/dist/designer.esm.js +1 -1
- package/dist/finalresult.esm.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- 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-site.js +1 -1
- package/layout/prerender.js +1 -1
- 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 +14 -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 +1 -1
package/bin/nodality.js
CHANGED
|
@@ -29,16 +29,23 @@
|
|
|
29
29
|
// upload/, auto-discovered page list). CLI flags override the file.
|
|
30
30
|
|
|
31
31
|
import fs from "node:fs";
|
|
32
|
+
import os from "node:os";
|
|
32
33
|
import path from "node:path";
|
|
33
34
|
import { prerenderSite } from "../layout/prerender-site.js";
|
|
35
|
+
import { prerender } from "../layout/prerender.js";
|
|
34
36
|
|
|
35
37
|
// ─── Usage ──────────────────────────────────────────────────────
|
|
36
38
|
|
|
37
39
|
function showUsage() {
|
|
38
40
|
console.log(`Usage:
|
|
39
41
|
nodality prerender [flags] # SSG: render upload/*.html in place
|
|
42
|
+
nodality compile [src/<file>.js] [flags] # Emit Designer output as a companion file
|
|
40
43
|
nodality help
|
|
41
44
|
|
|
45
|
+
Compile flags:
|
|
46
|
+
--out=<path> Override output path (defaults to upload/pages/<name>.designer.js)
|
|
47
|
+
--stdout Print imperative code to stdout instead of writing a file
|
|
48
|
+
|
|
42
49
|
Prerender flags (all optional; fall through to nodality.config.json, then defaults):
|
|
43
50
|
--origin=<url> Public origin, e.g. https://example.com (required if no config)
|
|
44
51
|
--upload=<path> Upload directory (default: ./upload)
|
|
@@ -155,95 +162,6 @@ function loadConfigFile(cwd) {
|
|
|
155
162
|
}
|
|
156
163
|
}
|
|
157
164
|
|
|
158
|
-
// ─── SSG bootstrap ──────────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* On first run, derive the upload/ structure from the project's root
|
|
162
|
-
* files so a freshly scaffolded project can prerender without manual
|
|
163
|
-
* setup. For each `src/<name>.js` we generate `upload/pages/<name>.js`
|
|
164
|
-
* (verbatim copy) and an `upload/<name>.html` whose importmap points
|
|
165
|
-
* at `./lib.bundle.js` and whose `<script src>` points at the page
|
|
166
|
-
* entry. The first src file (alphabetically) is also written as
|
|
167
|
-
* `upload/index.html` when no explicit index entry exists, so the
|
|
168
|
-
* dev server has a default landing page.
|
|
169
|
-
*
|
|
170
|
-
* If the user has already populated upload/ themselves, we touch
|
|
171
|
-
* nothing. Bootstrap only runs when upload/ is missing OR contains
|
|
172
|
-
* no .html files.
|
|
173
|
-
*/
|
|
174
|
-
function bootstrapUpload(cwd, uploadDir) {
|
|
175
|
-
const srcDir = path.join(cwd, "src");
|
|
176
|
-
if (!fs.existsSync(srcDir)) return false;
|
|
177
|
-
|
|
178
|
-
const srcFiles = fs.readdirSync(srcDir).filter((f) => f.endsWith(".js"));
|
|
179
|
-
if (srcFiles.length === 0) return false;
|
|
180
|
-
|
|
181
|
-
fs.mkdirSync(uploadDir, { recursive: true });
|
|
182
|
-
const pagesDir = path.join(uploadDir, "pages");
|
|
183
|
-
fs.mkdirSync(pagesDir, { recursive: true });
|
|
184
|
-
|
|
185
|
-
const projectName = path.basename(cwd);
|
|
186
|
-
let wrote = 0;
|
|
187
|
-
for (const srcFile of srcFiles) {
|
|
188
|
-
const base = path.basename(srcFile, ".js");
|
|
189
|
-
// Convention: src/app.js → upload/index.html + upload/pages/index.js.
|
|
190
|
-
// All others: src/<name>.js → upload/<name>.html + upload/pages/<name>.js.
|
|
191
|
-
const pageName = base === "app" ? "index" : base;
|
|
192
|
-
|
|
193
|
-
const entryPath = path.join(pagesDir, `${pageName}.js`);
|
|
194
|
-
if (!fs.existsSync(entryPath)) {
|
|
195
|
-
// `code: true` toggles Nodality's on-page <pre>/<code> dev panel,
|
|
196
|
-
// which is useful while writing src/ but should be off in the
|
|
197
|
-
// SSG output. Rewrite when cloning into upload/pages/.
|
|
198
|
-
const srcContent = fs.readFileSync(path.join(srcDir, srcFile), "utf8");
|
|
199
|
-
const ssgContent = srcContent.replace(/code:\s*true/g, "code: false");
|
|
200
|
-
fs.writeFileSync(entryPath, ssgContent);
|
|
201
|
-
wrote++;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const htmlPath = path.join(uploadDir, `${pageName}.html`);
|
|
205
|
-
if (!fs.existsSync(htmlPath)) {
|
|
206
|
-
fs.writeFileSync(htmlPath, renderUploadHtml(projectName, pageName));
|
|
207
|
-
wrote++;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (wrote > 0) {
|
|
212
|
-
console.log(`[nodality] Bootstrapped upload/ from src/ (${wrote} file(s) written)`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (!fs.existsSync(path.join(uploadDir, "lib.bundle.js"))) {
|
|
216
|
-
console.warn(
|
|
217
|
-
`[nodality] ⚠ upload/lib.bundle.js missing — run \`npm run build\` first ` +
|
|
218
|
-
`so the prerendered HTML can load the library bundle at runtime.`,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function renderUploadHtml(projectName, pageName) {
|
|
225
|
-
return `<!DOCTYPE html>
|
|
226
|
-
<html lang="en">
|
|
227
|
-
<head>
|
|
228
|
-
<meta charset="UTF-8">
|
|
229
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
230
|
-
<title>${projectName}</title>
|
|
231
|
-
<script type="importmap">
|
|
232
|
-
{
|
|
233
|
-
"imports": {
|
|
234
|
-
"nodality": "./lib.bundle.js"
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
</script>
|
|
238
|
-
</head>
|
|
239
|
-
<body>
|
|
240
|
-
<div id="mount"></div>
|
|
241
|
-
<script type="module" src="./pages/${pageName}.js"></script>
|
|
242
|
-
</body>
|
|
243
|
-
</html>
|
|
244
|
-
`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
165
|
// ─── Page auto-discovery ────────────────────────────────────────
|
|
248
166
|
|
|
249
167
|
/**
|
|
@@ -340,16 +258,6 @@ async function runPrerender(rawArgs) {
|
|
|
340
258
|
);
|
|
341
259
|
}
|
|
342
260
|
|
|
343
|
-
// First-run bootstrap: if upload/ is missing or has no HTML pages
|
|
344
|
-
// yet, derive it from the project's src/ + root index.html. This is
|
|
345
|
-
// what create-nodality projects rely on so the scaffolder doesn't
|
|
346
|
-
// have to ship duplicate copies of source files. No-op if upload/
|
|
347
|
-
// is already populated.
|
|
348
|
-
const needsBootstrap =
|
|
349
|
-
!fs.existsSync(uploadDir) ||
|
|
350
|
-
fs.readdirSync(uploadDir).filter((f) => f.endsWith(".html")).length === 0;
|
|
351
|
-
if (needsBootstrap) bootstrapUpload(cwd, uploadDir);
|
|
352
|
-
|
|
353
261
|
// Explicit `pages` from config wins over auto-discovery. The
|
|
354
262
|
// discovery rules (pages/<base>.js, <base>.js) can't infer
|
|
355
263
|
// irregular pairs like h7-nodality's `index.html → app.js`; for
|
|
@@ -377,6 +285,141 @@ async function runPrerender(rawArgs) {
|
|
|
377
285
|
await prerenderSite(config);
|
|
378
286
|
}
|
|
379
287
|
|
|
288
|
+
// ─── compile subcommand ────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Run the Designer (`src/<name>.js`) in jsdom and emit the imperative
|
|
292
|
+
* code it would have shown in the `code: true` on-page panel into a
|
|
293
|
+
* companion file `upload/pages/<name>.designer.js` (non-destructive —
|
|
294
|
+
* the canonical `upload/pages/<name>.js` is never touched).
|
|
295
|
+
*
|
|
296
|
+
* Developer workflow:
|
|
297
|
+
* 1. Sketch in `src/app.js` using the declarative Designer API.
|
|
298
|
+
* 2. `nodality compile` → emits `upload/pages/index.designer.js`
|
|
299
|
+
* containing `new Text(...).set({...}).render("#mount")` etc.
|
|
300
|
+
* 3. Diff the .designer.js against your canonical
|
|
301
|
+
* `upload/pages/index.js`; copy/cherry-pick what you want;
|
|
302
|
+
* refine by hand. The .designer.js is throwaway — regenerate
|
|
303
|
+
* whenever you sketch new pieces in src/.
|
|
304
|
+
*
|
|
305
|
+
* Flags:
|
|
306
|
+
* --out=<path> write to this file instead of the default companion
|
|
307
|
+
* (use this when you want to overwrite the canonical
|
|
308
|
+
* file; you own the risk)
|
|
309
|
+
* --stdout print the imperative code to stdout instead of
|
|
310
|
+
* writing a file (pipe into pbcopy, etc.)
|
|
311
|
+
*
|
|
312
|
+
* Mechanism: sets `globalThis.NODALITY_EMIT = true` before importing
|
|
313
|
+
* the Designer entry. `Des.set()` short-circuits when this flag is
|
|
314
|
+
* present, stashing `this.code` into `globalThis.__NODALITY_EMITTED__`
|
|
315
|
+
* instead of evaluating it. We then format and emit that array.
|
|
316
|
+
*/
|
|
317
|
+
async function runCompile(rawArgs) {
|
|
318
|
+
const cwd = process.cwd();
|
|
319
|
+
const flags = parseFlags(rawArgs);
|
|
320
|
+
const positionals = rawArgs.filter((a) => !a.startsWith("--"));
|
|
321
|
+
|
|
322
|
+
const srcRel = positionals[0] || "src/app.js";
|
|
323
|
+
const srcAbs = path.resolve(cwd, srcRel);
|
|
324
|
+
if (!fs.existsSync(srcAbs)) {
|
|
325
|
+
console.error(`[nodality] compile: source not found: ${srcRel}`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const base = path.basename(srcAbs, ".js");
|
|
330
|
+
const pageName = base === "app" ? "index" : base;
|
|
331
|
+
const defaultOut = path.join(cwd, "upload", "pages", `${pageName}.designer.js`);
|
|
332
|
+
const outAbs = flags.out ? path.resolve(cwd, flags.out) : defaultOut;
|
|
333
|
+
const toStdout = flags.stdout === true || flags.stdout === "true";
|
|
334
|
+
|
|
335
|
+
// Reuse the full prerender machinery — it already installs every
|
|
336
|
+
// browser shim the Designer's add() pipeline touches (IntersectionObserver,
|
|
337
|
+
// matchMedia, rAF, Element.animate, innerWidth/Height, …). We write
|
|
338
|
+
// throwaway template + output files into a temp dir; the only thing
|
|
339
|
+
// we actually want is the side effect of __NODALITY_EMITTED__ being
|
|
340
|
+
// populated when `Des.set()` short-circuits under NODALITY_EMIT.
|
|
341
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nodality-compile-"));
|
|
342
|
+
const tmplPath = path.join(tmpDir, "template.html");
|
|
343
|
+
const outPath = path.join(tmpDir, "out.html");
|
|
344
|
+
fs.writeFileSync(
|
|
345
|
+
tmplPath,
|
|
346
|
+
`<!DOCTYPE html><html><body><div id="mount"></div></body></html>`,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
globalThis.NODALITY_EMIT = true;
|
|
350
|
+
globalThis.__NODALITY_EMITTED__ = null;
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
await prerender({
|
|
354
|
+
template: tmplPath,
|
|
355
|
+
output: outPath,
|
|
356
|
+
mount: "#mount",
|
|
357
|
+
build: async () => {
|
|
358
|
+
await import(`file://${srcAbs}?t=${Date.now()}`);
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
} catch (e) {
|
|
362
|
+
// Designer-emit mode is purely a code-capture pass; if prerender's
|
|
363
|
+
// own serialize step throws because we short-circuited the render,
|
|
364
|
+
// that's fine as long as we captured something.
|
|
365
|
+
if (!globalThis.__NODALITY_EMITTED__) {
|
|
366
|
+
console.error(`[nodality] compile: failed to load ${srcRel}: ${e.message}`);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
} finally {
|
|
370
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
371
|
+
globalThis.NODALITY_EMIT = false;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const emitted = globalThis.__NODALITY_EMITTED__;
|
|
375
|
+
if (!emitted || !Array.isArray(emitted) || emitted.length === 0) {
|
|
376
|
+
console.error(
|
|
377
|
+
`[nodality] compile: ${srcRel} did not produce Designer output. ` +
|
|
378
|
+
`Make sure it ends with \`new Des().nodes(...).add(...).set({ mount: "#mount" })\`.`,
|
|
379
|
+
);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Wrap captured code strings into a standalone module.
|
|
384
|
+
const body = emitted
|
|
385
|
+
.map((line) => line.trim().replace(/;\s*$/, ""))
|
|
386
|
+
.map((line) => `${line};`)
|
|
387
|
+
.join("\n\n");
|
|
388
|
+
|
|
389
|
+
const out = `// Auto-emitted by \`nodality compile\` from ${srcRel}.
|
|
390
|
+
// This is the imperative form the Designer would have shown in the
|
|
391
|
+
// \`code: true\` panel. It is a throwaway artifact — diff it against
|
|
392
|
+
// your canonical upload/pages/${pageName}.js, copy what you want,
|
|
393
|
+
// then refine by hand. Re-run \`nodality compile\` whenever you
|
|
394
|
+
// sketch new pieces in ${srcRel}.
|
|
395
|
+
|
|
396
|
+
import {
|
|
397
|
+
Text, Image, Link, FlexRow, FlexGrid, Wrapper, Center, Stack,
|
|
398
|
+
Card, ZoomCard, Switcher, MobileBar, DesktopBar, SideNav, UINavBar,
|
|
399
|
+
Dropdown, Modal, Table, Spacer, HScroller, Polygon, Circle, UList,
|
|
400
|
+
Free, Audio, Progress, Code, MetaAdder, TextField,
|
|
401
|
+
FloatingInput, Range, RadioGroup, Picker, FilePickera, DataList,
|
|
402
|
+
Base, Form, Button, Slider, Video, Checkbox,
|
|
403
|
+
} from "nodality";
|
|
404
|
+
|
|
405
|
+
${body}
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
if (toStdout) {
|
|
409
|
+
process.stdout.write(out);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
fs.mkdirSync(path.dirname(outAbs), { recursive: true });
|
|
414
|
+
fs.writeFileSync(outAbs, out);
|
|
415
|
+
const rel = path.relative(cwd, outAbs);
|
|
416
|
+
console.log(`[nodality] compile: emitted ${emitted.length} statement(s) → ${rel}`);
|
|
417
|
+
if (!flags.out) {
|
|
418
|
+
const canonical = path.join("upload", "pages", `${pageName}.js`);
|
|
419
|
+
console.log(`[nodality] diff against your canonical ${canonical} and cherry-pick.`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
380
423
|
// ─── Dispatch ──────────────────────────────────────────────────
|
|
381
424
|
|
|
382
425
|
async function main() {
|
|
@@ -396,6 +439,8 @@ async function main() {
|
|
|
396
439
|
showUsage();
|
|
397
440
|
} else if (command === "prerender") {
|
|
398
441
|
await runPrerender(rest);
|
|
442
|
+
} else if (command === "compile") {
|
|
443
|
+
await runCompile(rest);
|
|
399
444
|
} else {
|
|
400
445
|
console.error(`[nodality] Unknown command: ${command}`);
|
|
401
446
|
showUsage();
|