@jxsuite/compiler 5.1.2 → 5.1.3

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 (40) hide show
  1. package/dist/cli.js +220 -351
  2. package/dist/compiler.js +126 -257
  3. package/package.json +4 -4
  4. package/src/cli.ts +12 -14
  5. package/src/compiler.ts +45 -23
  6. package/src/shadow.ts +9 -8
  7. package/src/shared.ts +350 -296
  8. package/src/site/asset-mounts.ts +22 -12
  9. package/src/site/bundler.ts +38 -12
  10. package/src/site/client-runtime.ts +31 -17
  11. package/src/site/csp.ts +20 -11
  12. package/src/site/db-push.ts +38 -24
  13. package/src/site/dev-command.ts +3 -3
  14. package/src/site/format-host.ts +56 -45
  15. package/src/site/headers-emitter.ts +7 -4
  16. package/src/site/i18n.ts +20 -19
  17. package/src/site/image-cache.ts +16 -3
  18. package/src/site/image-optimizer.ts +19 -5
  19. package/src/site/image-transform.ts +56 -60
  20. package/src/site/img-loading.ts +11 -5
  21. package/src/site/layout-resolver.ts +7 -6
  22. package/src/site/link-relations.ts +10 -6
  23. package/src/site/locale-negotiation.ts +1 -1
  24. package/src/site/pages-discovery.ts +65 -47
  25. package/src/site/preview-server.ts +22 -19
  26. package/src/site/project-sections.ts +12 -3
  27. package/src/site/prototype-resolver.ts +94 -42
  28. package/src/site/schema-command.ts +26 -13
  29. package/src/site/service-worker.ts +2 -2
  30. package/src/site/site-build.ts +379 -262
  31. package/src/site/site-loader.ts +6 -3
  32. package/src/site/static-host.ts +3 -3
  33. package/src/site/validate-command.ts +64 -34
  34. package/src/site/well-known.ts +57 -12
  35. package/src/targets/compile-class.ts +17 -5
  36. package/src/targets/compile-client.ts +141 -106
  37. package/src/targets/compile-element.ts +157 -140
  38. package/src/targets/compile-server.ts +11 -5
  39. package/src/targets/compile-static.ts +57 -63
  40. package/src/types.ts +5 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/compiler",
3
- "version": "5.1.2",
3
+ "version": "5.1.3",
4
4
  "description": "Jx static HTML compiler, island detector, and site builder",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,18 +45,18 @@
45
45
  "upgrade": "bun update --latest && bun install"
46
46
  },
47
47
  "dependencies": {
48
- "@apidevtools/json-schema-ref-parser": "^16.0.3",
48
+ "@apidevtools/json-schema-ref-parser": "^16.0.5",
49
49
  "@jxsuite/create": "^1.3.14",
50
50
  "@jxsuite/runtime": "^4.1.0",
51
51
  "@jxsuite/schema": "^3.0.0",
52
52
  "@jxsuite/site": "^2.0.4",
53
- "@vue/reactivity": "3.5.43",
53
+ "@vue/reactivity": "3.5.44",
54
54
  "esbuild": "^0.28.2",
55
55
  "lit-html": "^3.3.3",
56
56
  "sharp": "^0.35.5"
57
57
  },
58
58
  "devDependencies": {
59
- "@happy-dom/global-registrator": "^20.14.6",
59
+ "@happy-dom/global-registrator": "^21.0.3",
60
60
  "@jxsuite/connector": "^0.6.2",
61
61
  "@jxsuite/parser": "^2.0.1",
62
62
  "@types/mdast": "^4.0.4"
package/src/cli.ts CHANGED
@@ -9,6 +9,8 @@
9
9
  */
10
10
 
11
11
  import { relative, resolve } from "node:path";
12
+ import { errorMessage } from "@jxsuite/schema/parse";
13
+ import type { DbPushOptions } from "./site/db-push.ts";
12
14
 
13
15
  // The CLI body runs inside an async function rather than via top-level await: when a test pulls this
14
16
  // Entry in with a dynamic import(), Bun's test runtime drops the continuation after a top-level await
@@ -77,10 +79,11 @@ Options:
77
79
  const dryRun = flags.has("--dry-run");
78
80
  try {
79
81
  const { dbPush } = await import("./site/db-push.ts");
80
- const { results, bindingsPatched, wranglerPath } = await dbPush(projectRoot, {
81
- dryRun,
82
- ...(connectionArg === undefined ? {} : { connection: connectionArg }),
83
- });
82
+ const pushOptions: DbPushOptions = { dryRun };
83
+ if (connectionArg !== undefined) {
84
+ pushOptions.connection = connectionArg;
85
+ }
86
+ const { results, bindingsPatched, wranglerPath } = await dbPush(projectRoot, pushOptions);
84
87
  for (const result of results) {
85
88
  const mode = result.applied ? "applied" : "dry-run";
86
89
  console.log(
@@ -98,8 +101,7 @@ Options:
98
101
  console.log(`Updated bindings in ${relative(projectRoot, wranglerPath)}`);
99
102
  }
100
103
  } catch (error) {
101
- const err = error as Error;
102
- console.error(`db push failed: ${err.message}`);
104
+ console.error(`db push failed: ${errorMessage(error)}`);
103
105
  process.exit(1);
104
106
  }
105
107
  return;
@@ -125,8 +127,7 @@ Options:
125
127
 
126
128
  console.log(`\nDone: ${result.routes} routes → ${result.files} files`);
127
129
  } catch (error) {
128
- const err = error as Error;
129
- console.error(`Build failed: ${err.message}`);
130
+ console.error(`Build failed: ${errorMessage(error)}`);
130
131
  process.exit(1);
131
132
  }
132
133
  } else if (command === "dev") {
@@ -134,8 +135,7 @@ Options:
134
135
  const { runDev } = await import("./site/dev-command.ts");
135
136
  runDev(projectRoot, portArg);
136
137
  } catch (error) {
137
- const err = error as Error;
138
- console.error(err.message);
138
+ console.error(errorMessage(error));
139
139
  process.exit(1);
140
140
  }
141
141
  } else if (command === "preview") {
@@ -156,8 +156,7 @@ Options:
156
156
  console.log(`Wrote ${relative(projectRoot, projectSchemaPath)}`);
157
157
  console.log(`Wrote ${relative(projectRoot, documentSchemaPath)}`);
158
158
  } catch (error) {
159
- const err = error as Error;
160
- console.error(`Schema generation failed: ${err.message}`);
159
+ console.error(`Schema generation failed: ${errorMessage(error)}`);
161
160
  process.exit(1);
162
161
  }
163
162
  } else if (command === "validate") {
@@ -180,8 +179,7 @@ Options:
180
179
  process.exit(1);
181
180
  }
182
181
  } catch (error) {
183
- const err = error as Error;
184
- console.error(`Validation failed: ${err.message}`);
182
+ console.error(`Validation failed: ${errorMessage(error)}`);
185
183
  process.exit(1);
186
184
  }
187
185
  } else {
package/src/compiler.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Jx-compiler.js — Compiler orchestrator
3
- * @version 3.0.0
4
- * @license MIT
5
3
  *
6
4
  * Routes Jx documents to the appropriate compilation target:
7
- * - Fully static → compile-static.js (plain HTML/CSS, zero JS)
8
- * - Custom element (-) → compile-element.js (lit-html web component)
9
- * - Dynamic (standard) → compile-client.js (pre-rendered HTML + reactive bindings)
10
- * - Server → compile-server.js (Hono server handler)
11
5
  *
12
- * Usage (CLI):
13
- * bun packages/compiler/src/compile-cli.js <source.json> [output.html]
6
+ * - Fully static → compile-static.js (plain HTML/CSS, zero JS)
7
+ * - Custom element (-) → compile-element.js (lit-html web component)
8
+ * - Dynamic (standard) → compile-client.js (pre-rendered HTML + reactive bindings)
9
+ * - Server → compile-server.js (Hono server handler)
10
+ *
11
+ * Usage (CLI): `bun packages/compiler/src/compile-cli.js <source.json> [output.html]`
12
+ *
13
+ * @license MIT
14
+ * @version 3.0.0
14
15
  */
15
16
 
16
17
  import { readFileSync } from "node:fs";
@@ -27,7 +28,7 @@ import { compileServer } from "./targets/compile-server.ts";
27
28
  import { emitElementModule } from "./targets/compile-element.ts";
28
29
  import { compileStaticPage } from "./targets/compile-static.ts";
29
30
  import { compileClient } from "./targets/compile-client.ts";
30
- import { isClassDef } from "@jxsuite/schema/guards";
31
+ import { isClassDef, isString } from "@jxsuite/schema/guards";
31
32
  import { parseJxDocument } from "@jxsuite/schema/parse";
32
33
  import type { JxDocument, JxStyle } from "@jxsuite/schema/types";
33
34
  import type { FormatRegistry } from "@jxsuite/schema/format-registry";
@@ -53,7 +54,17 @@ export interface CompileOptions {
53
54
  * true). The site pipeline disables this and injects via the merged <head> instead.
54
55
  */
55
56
  prePaintScheme?: boolean;
56
- [key: string]: unknown;
57
+ /**
58
+ * The page language the site pipeline passes alongside the other options. No compile target reads
59
+ * it today: the document's own `lang` is what reaches the emitted markup.
60
+ */
61
+ lang?: string;
62
+ }
63
+
64
+ /** The page-target options {@link compile} forwards only when its caller set them. */
65
+ interface PageTargetOverrides {
66
+ prePaintScheme?: false;
67
+ rewriteSrc?: (specifier: string) => string;
57
68
  }
58
69
 
59
70
  /**
@@ -78,20 +89,19 @@ export async function compile(sourcePath: string | JxDocument, opts: CompileOpti
78
89
  } = opts;
79
90
 
80
91
  let raw: JxDocument;
81
- if (typeof sourcePath === "string") {
92
+ if (isString(sourcePath)) {
82
93
  const source = readFileSync(sourcePath, "utf8");
83
94
  if (sourcePath.endsWith(".json")) {
84
95
  raw = parseJxDocument(source, sourcePath);
85
96
  } else {
86
97
  const { extname } = await import("node:path");
87
98
  const ext = extname(sourcePath).toLowerCase();
88
- const entry = opts.formats?.byExtension?.(ext, "parse");
99
+ const entry = opts.formats?.byExtension(ext, "parse");
89
100
  if (!entry) {
90
101
  const { unknownFormatError } = await import("./site/format-host.ts");
91
102
  throw unknownFormatError(sourcePath, ext);
92
103
  }
93
- // Format plugins contractually parse source text into a Jx document.
94
- raw = (await entry.call("parse", source)) as JxDocument;
104
+ raw = await entry.parseDocument(source);
95
105
  }
96
106
  } else {
97
107
  raw = sourcePath;
@@ -100,11 +110,10 @@ export async function compile(sourcePath: string | JxDocument, opts: CompileOpti
100
110
  // Route 0: .class.json schema-defined class → JS class module
101
111
  if (isClassDef(raw)) {
102
112
  const { compileClassJson } = await import("./targets/compile-class.js");
103
- const jsContent = compileClassJson(raw, opts);
104
- const outputPath =
105
- typeof sourcePath === "string"
106
- ? sourcePath.replace(/\.class\.json$/, ".js")
107
- : `${raw.title}.js`;
113
+ const jsContent = compileClassJson(raw);
114
+ const outputPath = isString(sourcePath)
115
+ ? sourcePath.replace(/\.class\.json$/, ".js")
116
+ : `${raw.title}.js`;
108
117
  return { files: [{ content: jsContent, path: outputPath }], html: "" };
109
118
  }
110
119
 
@@ -114,8 +123,7 @@ export async function compile(sourcePath: string | JxDocument, opts: CompileOpti
114
123
  litHtmlSrc,
115
124
  projectStyle,
116
125
  reactivitySrc,
117
- ...(opts.prePaintScheme === false ? { prePaintScheme: false } : {}),
118
- ...(opts.rewriteSrc ? { rewriteSrc: opts.rewriteSrc } : {}),
126
+ ...pageTargetOverrides(opts),
119
127
  title,
120
128
  });
121
129
  }
@@ -162,12 +170,26 @@ export async function compile(sourcePath: string | JxDocument, opts: CompileOpti
162
170
  litHtmlSrc,
163
171
  projectStyle,
164
172
  reactivitySrc,
165
- ...(opts.prePaintScheme === false ? { prePaintScheme: false } : {}),
166
- ...(opts.rewriteSrc ? { rewriteSrc: opts.rewriteSrc } : {}),
173
+ ...pageTargetOverrides(opts),
167
174
  title,
168
175
  });
169
176
  }
170
177
 
178
+ /**
179
+ * The `prePaintScheme` and `rewriteSrc` options a page target receives, each present only when the
180
+ * caller set it, so the target's own default applies otherwise.
181
+ */
182
+ function pageTargetOverrides(opts: CompileOptions): PageTargetOverrides {
183
+ const overrides: PageTargetOverrides = {};
184
+ if (opts.prePaintScheme === false) {
185
+ overrides.prePaintScheme = false;
186
+ }
187
+ if (opts.rewriteSrc) {
188
+ overrides.rewriteSrc = opts.rewriteSrc;
189
+ }
190
+ return overrides;
191
+ }
192
+
171
193
  // ─── CLI ──────────────────────────────────────────────────────────────────────
172
194
 
173
195
  /**
package/src/shadow.ts CHANGED
@@ -23,11 +23,9 @@ export type ShadowMode = "open" | "closed";
23
23
  /** A value `$shadow` or `defaults.shadow` may carry. */
24
24
  export type ShadowSetting = ShadowMode | false;
25
25
 
26
- function asSetting(value: unknown): ShadowSetting | undefined {
27
- if (value === false || value === "open" || value === "closed") {
28
- return value;
29
- }
30
- return undefined;
26
+ /** Whether a document value is one of the settings `$shadow` / `defaults.shadow` may carry. */
27
+ function isShadowSetting(value: unknown): value is ShadowSetting {
28
+ return value === false || value === "open" || value === "closed";
31
29
  }
32
30
 
33
31
  /**
@@ -44,9 +42,12 @@ export function resolveShadowMode(
44
42
  doc: JxDocument | JxElement | null | undefined,
45
43
  defaults?: ProjectConfig["defaults"],
46
44
  ): ShadowMode | null {
47
- const own = asSetting((doc as { $shadow?: unknown } | null | undefined)?.$shadow);
48
- const setting = own ?? asSetting(defaults?.shadow) ?? false;
49
- return setting === false ? null : setting;
45
+ const own = doc?.$shadow;
46
+ if (isShadowSetting(own)) {
47
+ return own === false ? null : own;
48
+ }
49
+ const projectDefault = defaults?.shadow;
50
+ return isShadowSetting(projectDefault) && projectDefault !== false ? projectDefault : null;
50
51
  }
51
52
 
52
53
  /**