nodality 1.0.163 → 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.
Files changed (102) hide show
  1. package/bin/nodality.js +144 -94
  2. package/dist/bundle.umd.js +1 -1
  3. package/dist/designer.cjs.js +1 -1
  4. package/dist/designer.esm.js +1 -1
  5. package/dist/finalresult.esm.js +1 -1
  6. package/dist/index.cjs.js +1 -1
  7. package/dist/index.esm.js +1 -1
  8. package/layout/animator.js +1 -1
  9. package/layout/audio.js +1 -1
  10. package/layout/audionew.js +1 -1
  11. package/layout/base-2.js +1 -1
  12. package/layout/base.js +1 -1
  13. package/layout/beta-desktop-bar.js +1 -1
  14. package/layout/beta-mobile-bar.js +1 -1
  15. package/layout/box.js +1 -1
  16. package/layout/button.js +1 -1
  17. package/layout/cards.js +1 -1
  18. package/layout/center.js +1 -1
  19. package/layout/checkbox.js +1 -1
  20. package/layout/circle.js +1 -1
  21. package/layout/clean-row.js +1 -1
  22. package/layout/code.js +1 -1
  23. package/layout/container.js +1 -1
  24. package/layout/custom.js +1 -1
  25. package/layout/div-image.js +1 -1
  26. package/layout/dropdown-2025.js +1 -1
  27. package/layout/dropdown.js +1 -1
  28. package/layout/empty-element.js +1 -1
  29. package/layout/external-stylesheet.js +1 -1
  30. package/layout/flex-card.js +1 -1
  31. package/layout/flex-grid.js +1 -1
  32. package/layout/flex-row.js +1 -1
  33. package/layout/footer.js +1 -1
  34. package/layout/form-components/custom.js +1 -1
  35. package/layout/form-components/data-list.js +1 -1
  36. package/layout/form-components/floating-input.js +1 -1
  37. package/layout/form-components/form-all.js +1 -1
  38. package/layout/form-components/form.js +1 -1
  39. package/layout/form-components/image-picker.js +1 -1
  40. package/layout/form-components/picker.js +1 -1
  41. package/layout/form-components/radio.js +1 -1
  42. package/layout/form-components/radiogroup.js +1 -1
  43. package/layout/form-components/range.js +1 -1
  44. package/layout/free.js +1 -1
  45. package/layout/grid-new.js +1 -1
  46. package/layout/grid-switcher.js +1 -1
  47. package/layout/grid.js +1 -1
  48. package/layout/group.js +1 -1
  49. package/layout/header.js +1 -1
  50. package/layout/horizontal-scroller.js +1 -1
  51. package/layout/image-old.js +1 -1
  52. package/layout/image.js +1 -1
  53. package/layout/index.js +1 -1
  54. package/layout/label.js +1 -1
  55. package/layout/link.js +1 -1
  56. package/layout/list-OLD.js +1 -1
  57. package/layout/list.js +1 -1
  58. package/layout/meta-adder.js +1 -1
  59. package/layout/modal-2025.js +1 -1
  60. package/layout/modernwrap.js +1 -1
  61. package/layout/multiswitcher.js +1 -1
  62. package/layout/multiswitcherBeta.js +1 -1
  63. package/layout/nav-bar.js +1 -1
  64. package/layout/nav-factor/custom-div.js +1 -1
  65. package/layout/navBar-OLD.js +1 -1
  66. package/layout/new-flat-adder.js +1 -1
  67. package/layout/new-nav-bar.js +1 -1
  68. package/layout/offset-container.js +1 -1
  69. package/layout/polygon.js +1 -1
  70. package/layout/prerender-site.js +1 -1
  71. package/layout/prerender.js +1 -1
  72. package/layout/progress.js +1 -1
  73. package/layout/row.js +1 -1
  74. package/layout/saved-new-nav-bar.js +1 -1
  75. package/layout/scroll-video.js +1 -1
  76. package/layout/side-bar.js +1 -1
  77. package/layout/side-nav-bar.js +1 -1
  78. package/layout/simple-bar.js +1 -1
  79. package/layout/slider-2025.js +1 -1
  80. package/layout/spacer.js +1 -1
  81. package/layout/stack.js +1 -1
  82. package/layout/styler.js +1 -1
  83. package/layout/svg.js +1 -1
  84. package/layout/switcher.js +1 -1
  85. package/layout/table.js +1 -1
  86. package/layout/text-field.js +1 -1
  87. package/layout/text.js +1 -1
  88. package/layout/ulist.js +1 -1
  89. package/layout/video.js +1 -1
  90. package/layout/without-new.js +1 -1
  91. package/layout/wrap.js +1 -1
  92. package/layout/zoom-card.js +1 -1
  93. package/lib/card-getter.js +1 -1
  94. package/lib/designer.js +14 -1
  95. package/lib/element-mapper.js +1 -1
  96. package/lib/keyframe-animation.js +1 -1
  97. package/lib/link-getter.js +1 -1
  98. package/lib/scroll-video.js +1 -1
  99. package/lib/stacker.js +1 -1
  100. package/lib/theme.js +1 -1
  101. package/lib/transform-anim.js +1 -1
  102. 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,90 +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
- fs.copyFileSync(path.join(srcDir, srcFile), entryPath);
196
- wrote++;
197
- }
198
-
199
- const htmlPath = path.join(uploadDir, `${pageName}.html`);
200
- if (!fs.existsSync(htmlPath)) {
201
- fs.writeFileSync(htmlPath, renderUploadHtml(projectName, pageName));
202
- wrote++;
203
- }
204
- }
205
-
206
- if (wrote > 0) {
207
- console.log(`[nodality] Bootstrapped upload/ from src/ (${wrote} file(s) written)`);
208
- }
209
-
210
- if (!fs.existsSync(path.join(uploadDir, "lib.bundle.js"))) {
211
- console.warn(
212
- `[nodality] ⚠ upload/lib.bundle.js missing — run \`npm run build\` first ` +
213
- `so the prerendered HTML can load the library bundle at runtime.`,
214
- );
215
- }
216
- return true;
217
- }
218
-
219
- function renderUploadHtml(projectName, pageName) {
220
- return `<!DOCTYPE html>
221
- <html lang="en">
222
- <head>
223
- <meta charset="UTF-8">
224
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
225
- <title>${projectName}</title>
226
- <script type="importmap">
227
- {
228
- "imports": {
229
- "nodality": "./lib.bundle.js"
230
- }
231
- }
232
- </script>
233
- </head>
234
- <body>
235
- <div id="mount"></div>
236
- <script type="module" src="./pages/${pageName}.js"></script>
237
- </body>
238
- </html>
239
- `;
240
- }
241
-
242
165
  // ─── Page auto-discovery ────────────────────────────────────────
243
166
 
244
167
  /**
@@ -335,16 +258,6 @@ async function runPrerender(rawArgs) {
335
258
  );
336
259
  }
337
260
 
338
- // First-run bootstrap: if upload/ is missing or has no HTML pages
339
- // yet, derive it from the project's src/ + root index.html. This is
340
- // what create-nodality projects rely on so the scaffolder doesn't
341
- // have to ship duplicate copies of source files. No-op if upload/
342
- // is already populated.
343
- const needsBootstrap =
344
- !fs.existsSync(uploadDir) ||
345
- fs.readdirSync(uploadDir).filter((f) => f.endsWith(".html")).length === 0;
346
- if (needsBootstrap) bootstrapUpload(cwd, uploadDir);
347
-
348
261
  // Explicit `pages` from config wins over auto-discovery. The
349
262
  // discovery rules (pages/<base>.js, <base>.js) can't infer
350
263
  // irregular pairs like h7-nodality's `index.html → app.js`; for
@@ -372,6 +285,141 @@ async function runPrerender(rawArgs) {
372
285
  await prerenderSite(config);
373
286
  }
374
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
+
375
423
  // ─── Dispatch ──────────────────────────────────────────────────
376
424
 
377
425
  async function main() {
@@ -391,6 +439,8 @@ async function main() {
391
439
  showUsage();
392
440
  } else if (command === "prerender") {
393
441
  await runPrerender(rest);
442
+ } else if (command === "compile") {
443
+ await runCompile(rest);
394
444
  } else {
395
445
  console.error(`[nodality] Unknown command: ${command}`);
396
446
  showUsage();