@ecopages/ecopages-jsx 0.2.0-alpha.23 → 0.2.0-alpha.25

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/CHANGELOG.md CHANGED
@@ -8,7 +8,10 @@
8
8
 
9
9
  ### Bug Fixes
10
10
 
11
- - Patched the Ecopages JSX browser runtime bundle so nested SVG templates restore canonical camel-cased SVG tag names during client rendering.
11
+ - Fixed lazy Ecopages JSX custom-element dependencies to stay as standalone assets instead of being folded into page-owned bundles, restoring trigger-driven loading for docs components like `theme-toggle`.
12
+ - Fixed Ecopages JSX page-owned browser bundles to inline their JSX and Radiant runtime imports while skipping separate intrinsic custom-element script tags when the current component tree already imports those scripts.
13
+ - Fixed intrinsic custom-element script suppression to honor dependency-declared script ownership instead of relying only on source import scanning.
14
+ - Aligned the Ecopages JSX browser runtime bundle with the upstream `@ecopages/jsx` runtime shipped by current alpha releases.
12
15
  - Aligned Ecopages JSX peer dependency ranges with the current `@ecopages/jsx` and `@ecopages/radiant` alpha releases.
13
16
  - Aligned Radiant SSR and hydration wiring with the public `@ecopages/radiant/server/render-component` and `@ecopages/radiant/client/hydrator` entrypoints so JSX apps install an explicit client hydrator bootstrap instead of relying on implicit side effects.
14
17
  - Updated the Ecopages JSX Radiant browser runtime for the `RadiantElement` and `RadiantController` API surface and switched the explicit hydrator bootstrap to `@ecopages/radiant/client/install-hydrator`.
@@ -21,6 +24,7 @@
21
24
 
22
25
  ### Refactoring
23
26
 
27
+ - Removed the JSX browser import-map asset and folded the Radiant install-hydrator entry into the emitted Radiant vendor bundle.
24
28
  - Replaced Ecopages JSX renderer static and post-construction configuration with instance-owned renderer wiring and extracted shared plugin and renderer types into a dedicated module.
25
29
 
26
30
  ### Tests
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/ecopages-jsx",
3
- "version": "0.2.0-alpha.23",
3
+ "version": "0.2.0-alpha.25",
4
4
  "description": "JSX integration plugin for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -21,8 +21,8 @@
21
21
  "vfile": "^6.0.3"
22
22
  },
23
23
  "peerDependencies": {
24
- "@ecopages/core": "0.2.0-alpha.23",
25
- "@ecopages/jsx": "0.3.0-alpha.5",
26
- "@ecopages/radiant": "0.3.0-alpha.5"
24
+ "@ecopages/core": "0.2.0-alpha.25",
25
+ "@ecopages/jsx": "0.3.0-alpha.10",
26
+ "@ecopages/radiant": "0.3.0-alpha.10"
27
27
  }
28
28
  }
@@ -17,8 +17,11 @@ type MdxPageModule = EcoPageFile<{
17
17
  export declare class EcopagesJsxRenderer extends IntegrationRenderer<JsxRenderable> {
18
18
  name: string;
19
19
  private static radiantServerRuntimeInstallPromise;
20
+ private static readonly SCRIPT_IMPORT_RE;
20
21
  private readonly intrinsicCustomElementAssets;
22
+ private readonly intrinsicCustomElementScriptFiles;
21
23
  private collectedAssetFrames;
24
+ private importedIntrinsicScriptFrames;
22
25
  private readonly mdxExtensions;
23
26
  private readonly radiantSsrEnabled;
24
27
  /**
@@ -57,6 +60,17 @@ export declare class EcopagesJsxRenderer extends IntegrationRenderer<JsxRenderab
57
60
  private renderJsx;
58
61
  private renderEcoComponent;
59
62
  private recordCollectedAssets;
63
+ private beginImportedIntrinsicScriptFrame;
64
+ private endImportedIntrinsicScriptFrame;
65
+ private getActiveImportedIntrinsicScriptFiles;
66
+ /**
67
+ * Collects intrinsic custom-element script files already owned by the current
68
+ * component tree through direct source imports or dependency declarations.
69
+ */
70
+ private collectImportedIntrinsicScriptFiles;
71
+ private extractImportedIntrinsicScriptFiles;
72
+ private extractConfiguredDependencyScriptFiles;
73
+ private resolveImportedIntrinsicScriptFile;
60
74
  private ensureRadiantServerRuntimeIfEnabled;
61
75
  private ensureRadiantServerRuntimeInstalled;
62
76
  private isFunctionComponent;
@@ -1,4 +1,6 @@
1
1
  import { rapidhash } from "@ecopages/core/hash";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import path from "node:path";
2
4
  import {
3
5
  IntegrationRenderer
4
6
  } from "@ecopages/core/route-renderer/integration-renderer";
@@ -8,8 +10,11 @@ import { ECOPAGES_JSX_PLUGIN_NAME } from "./ecopages-jsx.constants.js";
8
10
  class EcopagesJsxRenderer extends IntegrationRenderer {
9
11
  name = ECOPAGES_JSX_PLUGIN_NAME;
10
12
  static radiantServerRuntimeInstallPromise;
13
+ static SCRIPT_IMPORT_RE = /import\s+(?:[^'";]+\s+from\s+)?['"](\.[^'"\n]*\.script(?:\.[cm]?[jt]sx?)?)['"]/g;
11
14
  intrinsicCustomElementAssets;
15
+ intrinsicCustomElementScriptFiles;
12
16
  collectedAssetFrames = [];
17
+ importedIntrinsicScriptFrames = [];
13
18
  mdxExtensions;
14
19
  radiantSsrEnabled;
15
20
  /**
@@ -63,6 +68,7 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
63
68
  runtimeOrigin
64
69
  });
65
70
  this.intrinsicCustomElementAssets = jsxConfig?.intrinsicCustomElementAssets ?? /* @__PURE__ */ new Map();
71
+ this.intrinsicCustomElementScriptFiles = jsxConfig?.intrinsicCustomElementScriptFiles ?? /* @__PURE__ */ new Map();
66
72
  this.mdxExtensions = jsxConfig?.mdxExtensions ?? [".mdx"];
67
73
  this.radiantSsrEnabled = jsxConfig?.radiantSsrEnabled ?? false;
68
74
  }
@@ -76,6 +82,11 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
76
82
  return this.isMdxFile(file) ? this.normalizeMdxPageModule(file, module) : module;
77
83
  }
78
84
  async render(options) {
85
+ const importedScriptFrame = this.beginImportedIntrinsicScriptFrame([
86
+ options.Page,
87
+ options.Layout,
88
+ options.HtmlTemplate
89
+ ]);
79
90
  try {
80
91
  return await this.renderPageWithDocumentShell({
81
92
  page: {
@@ -98,18 +109,18 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
98
109
  });
99
110
  } catch (error) {
100
111
  throw this.createRenderError("Error rendering page", error);
112
+ } finally {
113
+ this.endImportedIntrinsicScriptFrame(importedScriptFrame);
101
114
  }
102
115
  }
103
116
  async renderComponent(input) {
117
+ const importedScriptFrame = this.beginImportedIntrinsicScriptFrame([input.component]);
104
118
  const assetFrame = this.beginCollectedAssetFrame();
105
119
  try {
106
120
  if (!this.isFunctionComponent(input.component)) {
107
121
  throw new TypeError("JSX renderer expected a callable component.");
108
122
  }
109
- const content = await this.renderEcoComponent(
110
- input.component,
111
- this.createComponentProps(input)
112
- );
123
+ const content = await this.renderEcoComponent(input.component, this.createComponentProps(input));
113
124
  const rendered = await this.renderJsx(content);
114
125
  const queuedBoundaryResolution = await this.resolveOwnedBoundaryHtml(
115
126
  rendered.html,
@@ -131,6 +142,8 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
131
142
  } catch (error) {
132
143
  this.endCollectedAssetFrame(assetFrame);
133
144
  throw this.createRenderError("Error rendering component", error);
145
+ } finally {
146
+ this.endImportedIntrinsicScriptFrame(importedScriptFrame);
134
147
  }
135
148
  }
136
149
  createComponentBoundaryRuntime(options) {
@@ -140,6 +153,7 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
140
153
  });
141
154
  }
142
155
  async renderToResponse(view, props, ctx) {
156
+ const importedScriptFrame = this.beginImportedIntrinsicScriptFrame([view, view.config?.layout]);
143
157
  try {
144
158
  if (!this.isFunctionComponent(view)) {
145
159
  throw new TypeError("JSX renderer expected a callable view component.");
@@ -152,6 +166,8 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
152
166
  });
153
167
  } catch (error) {
154
168
  throw this.createRenderError("Error rendering view", error);
169
+ } finally {
170
+ this.endImportedIntrinsicScriptFrame(importedScriptFrame);
155
171
  }
156
172
  }
157
173
  /**
@@ -162,6 +178,9 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
162
178
  * any layout or document-shell logic runs.
163
179
  */
164
180
  normalizeMdxPageModule(file, module) {
181
+ if (!this.isFunctionComponent(module.default)) {
182
+ throw new TypeError("MDX file must export a callable default component.");
183
+ }
165
184
  const Page = module.default;
166
185
  const normalizedConfig = {
167
186
  ...module.config ?? Page.config ?? {},
@@ -221,6 +240,104 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
221
240
  }
222
241
  return dedupedAssets;
223
242
  }
243
+ beginImportedIntrinsicScriptFrame(components) {
244
+ const frame = this.collectImportedIntrinsicScriptFiles(components);
245
+ this.importedIntrinsicScriptFrames.push(frame);
246
+ return frame;
247
+ }
248
+ endImportedIntrinsicScriptFrame(frame) {
249
+ const activeFrame = this.importedIntrinsicScriptFrames.pop();
250
+ if (activeFrame !== frame) {
251
+ this.importedIntrinsicScriptFrames = this.importedIntrinsicScriptFrames.filter((entry) => entry !== frame);
252
+ }
253
+ }
254
+ getActiveImportedIntrinsicScriptFiles() {
255
+ return this.importedIntrinsicScriptFrames[this.importedIntrinsicScriptFrames.length - 1];
256
+ }
257
+ /**
258
+ * Collects intrinsic custom-element script files already owned by the current
259
+ * component tree through direct source imports or dependency declarations.
260
+ */
261
+ collectImportedIntrinsicScriptFiles(components) {
262
+ const importedScriptFiles = /* @__PURE__ */ new Set();
263
+ const visitedFiles = /* @__PURE__ */ new Set();
264
+ const visit = (component) => {
265
+ const file = component?.config?.__eco?.file;
266
+ if (!file || visitedFiles.has(file)) {
267
+ return;
268
+ }
269
+ visitedFiles.add(file);
270
+ for (const scriptFile of this.extractConfiguredDependencyScriptFiles(component, path.dirname(file))) {
271
+ importedScriptFiles.add(scriptFile);
272
+ }
273
+ for (const scriptFile of this.extractImportedIntrinsicScriptFiles(file)) {
274
+ importedScriptFiles.add(scriptFile);
275
+ }
276
+ for (const nestedComponent of component?.config?.dependencies?.components ?? []) {
277
+ visit(nestedComponent);
278
+ }
279
+ visit(component?.config?.layout);
280
+ };
281
+ for (const component of components) {
282
+ visit(component);
283
+ }
284
+ return importedScriptFiles;
285
+ }
286
+ extractImportedIntrinsicScriptFiles(file) {
287
+ let source;
288
+ try {
289
+ source = readFileSync(file, "utf8");
290
+ } catch {
291
+ return [];
292
+ }
293
+ const scriptFiles = /* @__PURE__ */ new Set();
294
+ const directory = path.dirname(file);
295
+ for (const match of source.matchAll(EcopagesJsxRenderer.SCRIPT_IMPORT_RE)) {
296
+ const specifier = match[1];
297
+ if (!specifier) {
298
+ continue;
299
+ }
300
+ const resolvedScriptFile = this.resolveImportedIntrinsicScriptFile(directory, specifier);
301
+ if (resolvedScriptFile) {
302
+ scriptFiles.add(resolvedScriptFile);
303
+ }
304
+ }
305
+ return [...scriptFiles];
306
+ }
307
+ extractConfiguredDependencyScriptFiles(component, directory) {
308
+ const scriptFiles = /* @__PURE__ */ new Set();
309
+ for (const script of component?.config?.dependencies?.scripts ?? []) {
310
+ const specifier = typeof script === "string" ? script : script.src;
311
+ if (!specifier) {
312
+ continue;
313
+ }
314
+ const resolvedScriptFile = this.resolveImportedIntrinsicScriptFile(directory, specifier);
315
+ if (resolvedScriptFile) {
316
+ scriptFiles.add(resolvedScriptFile);
317
+ }
318
+ }
319
+ return [...scriptFiles];
320
+ }
321
+ resolveImportedIntrinsicScriptFile(directory, specifier) {
322
+ const basePath = path.resolve(directory, specifier);
323
+ const candidatePaths = [
324
+ basePath,
325
+ `${basePath}.ts`,
326
+ `${basePath}.tsx`,
327
+ `${basePath}.js`,
328
+ `${basePath}.jsx`,
329
+ `${basePath}.mts`,
330
+ `${basePath}.cts`,
331
+ `${basePath}.mjs`,
332
+ `${basePath}.cjs`
333
+ ];
334
+ for (const candidatePath of candidatePaths) {
335
+ if (existsSync(candidatePath)) {
336
+ return candidatePath;
337
+ }
338
+ }
339
+ return void 0;
340
+ }
224
341
  async ensureRadiantServerRuntimeIfEnabled() {
225
342
  if (!this.radiantSsrEnabled) {
226
343
  return;
@@ -270,7 +387,7 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
270
387
  config,
271
388
  metadata
272
389
  }) {
273
- const wrappedPage = (async (props) => this.invokeComponent(page, props));
390
+ const wrappedPage = async (props) => await this.invokeComponent(page, props);
274
391
  wrappedPage.config = config;
275
392
  if (metadata) {
276
393
  wrappedPage.metadata = metadata;
@@ -279,6 +396,11 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
279
396
  }
280
397
  createIntrinsicCustomElementRenderHook(target) {
281
398
  return ({ tagName }) => {
399
+ const currentImportedScriptFiles = this.getActiveImportedIntrinsicScriptFiles();
400
+ const intrinsicScriptFile = this.intrinsicCustomElementScriptFiles.get(tagName);
401
+ if (intrinsicScriptFile && currentImportedScriptFiles?.has(intrinsicScriptFile)) {
402
+ return void 0;
403
+ }
282
404
  const assets = this.intrinsicCustomElementAssets.get(tagName);
283
405
  if (assets) {
284
406
  target.push(...assets);
@@ -8,6 +8,7 @@ export type { EcopagesJsxMdxCompileOptions, EcopagesJsxMdxOptions, EcopagesJsxPl
8
8
  export declare class EcopagesJsxPlugin extends IntegrationPlugin<JsxRenderable> {
9
9
  renderer: typeof EcopagesJsxRenderer;
10
10
  private customElementAssets;
11
+ private customElementScriptFiles;
11
12
  private includeRadiant;
12
13
  private mdxEnabled;
13
14
  private mdxCompilerOptions?;
@@ -80,6 +80,7 @@ const resolvePluginOptions = (options) => {
80
80
  class EcopagesJsxPlugin extends IntegrationPlugin {
81
81
  renderer = EcopagesJsxRenderer;
82
82
  customElementAssets = /* @__PURE__ */ new Map();
83
+ customElementScriptFiles = /* @__PURE__ */ new Map();
83
84
  includeRadiant;
84
85
  mdxEnabled;
85
86
  mdxCompilerOptions;
@@ -112,6 +113,7 @@ class EcopagesJsxPlugin extends IntegrationPlugin {
112
113
  initializeRenderer(options) {
113
114
  const rendererConfig = {
114
115
  intrinsicCustomElementAssets: this.customElementAssets,
116
+ intrinsicCustomElementScriptFiles: this.customElementScriptFiles,
115
117
  mdxExtensions: this.mdxExtensions,
116
118
  radiantSsrEnabled: this.includeRadiant
117
119
  };
@@ -200,6 +202,7 @@ class EcopagesJsxPlugin extends IntegrationPlugin {
200
202
  return;
201
203
  }
202
204
  this.customElementAssets.clear();
205
+ this.customElementScriptFiles.clear();
203
206
  const scriptFiles = await this.collectScriptEntries(this.appConfig.absolutePaths.srcDir);
204
207
  for (const scriptFile of scriptFiles) {
205
208
  const tagNames = await this.extractCustomElementTagNames(scriptFile);
@@ -212,6 +215,7 @@ class EcopagesJsxPlugin extends IntegrationPlugin {
212
215
  }
213
216
  for (const tagName of tagNames) {
214
217
  this.customElementAssets.set(tagName, [asset]);
218
+ this.customElementScriptFiles.set(tagName, scriptFile);
215
219
  }
216
220
  }
217
221
  }
@@ -37,10 +37,10 @@ export type EcopagesJsxPluginOptions = Omit<IntegrationPluginConfig, 'name' | 'e
37
37
  *
38
38
  * When enabled, Ecopages JSX:
39
39
  * - imports `@ecopages/radiant/server/render-component` before Radiant SSR
40
- * - exposes browser-safe Radiant bare specifiers through the runtime import map
41
- * - injects an explicit client bootstrap that imports
42
- * `@ecopages/radiant/client/install-hydrator` before intrinsic
43
- * custom-element modules load
40
+ * - rewrites browser runtime specifiers to emitted vendor assets at build time
41
+ * - folds `@ecopages/radiant/client/install-hydrator` into the emitted
42
+ * Radiant vendor so intrinsic custom-element modules install the
43
+ * hydrator before they connect
44
44
  *
45
45
  * Set to `false` when pages do not use Radiant web components.
46
46
  * @default true
@@ -51,6 +51,7 @@ export type EcopagesJsxPluginOptions = Omit<IntegrationPluginConfig, 'name' | 'e
51
51
  };
52
52
  export type EcopagesJsxRendererConfig = {
53
53
  intrinsicCustomElementAssets?: Map<string, readonly ProcessedAsset[]>;
54
+ intrinsicCustomElementScriptFiles?: Map<string, string>;
54
55
  mdxExtensions?: string[];
55
56
  radiantSsrEnabled?: boolean;
56
57
  };
@@ -10,7 +10,6 @@
10
10
  */
11
11
  import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
12
12
  import { type AssetDefinition } from '@ecopages/core/services/asset-processing-service';
13
- export declare const RADIANT_HYDRATOR_BOOTSTRAP_ATTRIBUTE = "data-ecopages-jsx-radiant-hydrator";
14
13
  export interface JsxRuntimeBundleServiceConfig {
15
14
  radiant: boolean;
16
15
  rootDir?: string;
@@ -20,7 +19,6 @@ export declare class JsxRuntimeBundleService {
20
19
  private cachedSpecifierMap;
21
20
  private cachedJsxEntryModulePath;
22
21
  private cachedRadiantEntryModulePath;
23
- private cachedRadiantInstallHydratorEntryModulePath;
24
22
  constructor(config: JsxRuntimeBundleServiceConfig);
25
23
  setRootDir(rootDir: string | undefined): void;
26
24
  /**
@@ -43,11 +41,9 @@ export declare class JsxRuntimeBundleService {
43
41
  getSpecifierMap(): Promise<Record<string, string>>;
44
42
  /**
45
43
  * Builds the full list of vendor asset definitions: the import map inline
46
- * script plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
44
+ * assets plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
47
45
  */
48
46
  getDependencies(): Promise<AssetDefinition[]>;
49
- private createRadiantHydratorBootstrapAsset;
50
- private createRadiantHydratorBootstrapSource;
51
47
  private getArtifactsDir;
52
48
  private getEntryImportPath;
53
49
  private getOrCreateSpecifierMap;
@@ -55,5 +51,4 @@ export declare class JsxRuntimeBundleService {
55
51
  private getRadiantBrowserRuntimeModules;
56
52
  private resolvePackageExportModulePath;
57
53
  private getOrCreateRadiantEntryModulePath;
58
- private getOrCreateRadiantInstallHydratorEntryModulePath;
59
54
  }
@@ -2,28 +2,13 @@ import path from "node:path";
2
2
  import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
3
3
  import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
4
4
  import {
5
- BROWSER_RUNTIME_SCRIPT_ATTRIBUTES,
6
5
  buildBrowserRuntimeAssetUrl,
7
- createBrowserRuntimeScriptAsset,
8
- AssetFactory
6
+ createBrowserRuntimeScriptAsset
9
7
  } from "@ecopages/core/services/asset-processing-service";
10
8
  const VENDOR_FILE_NAMES = {
11
9
  jsx: "ecopages-jsx-esm.js",
12
- radiant: "ecopages-radiant-esm.js",
13
- radiantInstallHydrator: "ecopages-radiant-install-hydrator-esm.js"
10
+ radiant: "ecopages-radiant-esm.js"
14
11
  };
15
- const RADIANT_HYDRATOR_BOOTSTRAP_ATTRIBUTE = "data-ecopages-jsx-radiant-hydrator";
16
- const JSX_RUNTIME_NAMESPACE_REPAIR_SNIPPET = "function rG(W,G,J){let j=G instanceof Element?G:G?.parentElement,U=j?.namespaceURI??K9,$=j?.localName,X=W.firstElementChild;if(!X)return;let F=J??X.localName,Z=O9(U,$,F);if(X.namespaceURI===Z&&X.localName===F)return;W.replaceChild(tG(X,Z,F),X)}function tG(W,G,J){let j=document.createElementNS(G,J);for(let U of Array.from(W.attributes)){if(U.namespaceURI){j.setAttributeNS(U.namespaceURI,U.name,U.value);continue}n(j,U.name,U.value)}return j.append(...W.childNodes),j}";
17
- const JSX_RUNTIME_NAMESPACE_REPAIR_PATCH = [
18
- "const eopHtmlNamespace='http://www.w3.org/1999/xhtml',eopSvgNamespace='http://www.w3.org/2000/svg',eopCanonicalSvgLocalNames={altglyph:'altGlyph',altglyphdef:'altGlyphDef',altglyphitem:'altGlyphItem',animatemotion:'animateMotion',animatetransform:'animateTransform',clippath:'clipPath',feblend:'feBlend',fecolormatrix:'feColorMatrix',fecomponenttransfer:'feComponentTransfer',fecomposite:'feComposite',feconvolvematrix:'feConvolveMatrix',fediffuselighting:'feDiffuseLighting',fedisplacementmap:'feDisplacementMap',fedistantlight:'feDistantLight',fedropshadow:'feDropShadow',feflood:'feFlood',fefunca:'feFuncA',fefuncb:'feFuncB',fefuncg:'feFuncG',fefuncr:'feFuncR',fegaussianblur:'feGaussianBlur',feimage:'feImage',femerge:'feMerge',femergenode:'feMergeNode',femorphology:'feMorphology',feoffset:'feOffset',fepointlight:'fePointLight',fespecularlighting:'feSpecularLighting',fespotlight:'feSpotLight',fetile:'feTile',feturbulence:'feTurbulence',foreignobject:'foreignObject',glyphref:'glyphRef',lineargradient:'linearGradient',radialgradient:'radialGradient',textpath:'textPath'};",
19
- "function eopGetCanonicalSvgLocalName(W){return eopCanonicalSvgLocalNames[W]??W}",
20
- "function eopIsSvgNamespace(W){return W===eopSvgNamespace}",
21
- "function rG(W,G,J){let j=G instanceof Element?G:G?.parentElement,U=j?.namespaceURI??K9,$=j?.localName;eopRepairNamespaceFragment(W,U??eopHtmlNamespace,$,J)}",
22
- "function eopRepairNamespaceFragment(W,G,J,j){let U=W.firstElementChild;if(!U)return;let $=j??U.localName,X=O9(G,J,$),F=eopIsSvgNamespace(X)?eopGetCanonicalSvgLocalName($):$;eopRepairNamespaceElement(W,U,X,F)}",
23
- "function eopRepairNamespaceElement(W,G,J,j){let U=G;if(G.namespaceURI!==J||G.localName!==j)U=tG(G,J,j),W.replaceChild(U,G);eopRepairNamespaceChildren(U,J,j)}",
24
- "function eopRepairNamespaceChildren(W,G,J){for(let j of Array.from(W.children)){let U=O9(G,J,j.localName),$=eopIsSvgNamespace(U)?eopGetCanonicalSvgLocalName(j.localName):j.localName,X=j;if(j.namespaceURI!==U||j.localName!==$)X=tG(j,U,$),W.replaceChild(X,j);eopRepairNamespaceChildren(X,U,$)}}",
25
- "function tG(W,G,J){let j=document.createElementNS(G,eopIsSvgNamespace(G)?eopGetCanonicalSvgLocalName(J):J);for(let U of Array.from(W.attributes)){if(U.namespaceURI){j.setAttributeNS(U.namespaceURI,U.name,U.value);continue}n(j,U.name,U.value)}return j.append(...W.childNodes),j}"
26
- ].join("");
27
12
  function getNamedExportNamesFromModuleSource(source) {
28
13
  const exportNames = /* @__PURE__ */ new Set();
29
14
  for (const match of source.matchAll(/export\s*\{([^}]+)\}/g)) {
@@ -65,19 +50,20 @@ function isBrowserRuntimeRadiantSpecifier(exportKey) {
65
50
  }
66
51
  return exportKey === "./core/radiant-controller" || exportKey === "./core/radiant-element";
67
52
  }
68
- function replaceExactOnce(source, search, replacement, label) {
69
- if (!source.includes(search)) {
70
- throw new Error(`Could not find ${label} in @ecopages/jsx browser runtime source`);
71
- }
72
- return source.replace(search, replacement);
53
+ function isObjectRecord(value) {
54
+ return typeof value === "object" && value !== null;
73
55
  }
74
- function createPatchedJsxBrowserRuntimeSource(source) {
75
- return replaceExactOnce(
76
- source,
77
- JSX_RUNTIME_NAMESPACE_REPAIR_SNIPPET,
78
- JSX_RUNTIME_NAMESPACE_REPAIR_PATCH,
79
- "SVG namespace repair snippet"
80
- );
56
+ function readRadiantPackageJson(manifestPath) {
57
+ const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
58
+ if (!isObjectRecord(parsed)) {
59
+ throw new Error(`Invalid package manifest at ${manifestPath}`);
60
+ }
61
+ if (parsed.exports !== void 0 && !isObjectRecord(parsed.exports)) {
62
+ throw new Error(`Invalid package exports in ${manifestPath}`);
63
+ }
64
+ return {
65
+ exports: parsed.exports
66
+ };
81
67
  }
82
68
  function findPackageManifestPath(packageName) {
83
69
  let currentDir = path.dirname(new URL(import.meta.url).pathname);
@@ -99,7 +85,6 @@ class JsxRuntimeBundleService {
99
85
  cachedSpecifierMap;
100
86
  cachedJsxEntryModulePath;
101
87
  cachedRadiantEntryModulePath;
102
- cachedRadiantInstallHydratorEntryModulePath;
103
88
  constructor(config) {
104
89
  this.config = config;
105
90
  }
@@ -132,22 +117,11 @@ class JsxRuntimeBundleService {
132
117
  }
133
118
  /**
134
119
  * Builds the full list of vendor asset definitions: the import map inline
135
- * script plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
120
+ * assets plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
136
121
  */
137
122
  async getDependencies() {
138
- const specifierMap = await this.getSpecifierMap();
139
123
  const jsxEntryModulePath = await this.getOrCreateJsxEntryModulePath();
140
- const deps = [
141
- AssetFactory.createInlineContentScript({
142
- position: "head",
143
- bundle: false,
144
- content: JSON.stringify({ imports: specifierMap }, null, 2),
145
- attributes: { type: "importmap" }
146
- })
147
- ];
148
- if (this.config.radiant) {
149
- deps.push(this.createRadiantHydratorBootstrapAsset());
150
- }
124
+ const deps = [];
151
125
  deps.push(
152
126
  createBrowserRuntimeScriptAsset({
153
127
  importPath: jsxEntryModulePath,
@@ -157,36 +131,16 @@ class JsxRuntimeBundleService {
157
131
  );
158
132
  if (this.config.radiant) {
159
133
  const radiantEntryModulePath = await this.getOrCreateRadiantEntryModulePath();
160
- const radiantInstallHydratorEntryModulePath = await this.getOrCreateRadiantInstallHydratorEntryModulePath();
161
134
  deps.push(
162
135
  createBrowserRuntimeScriptAsset({
163
136
  importPath: radiantEntryModulePath,
164
137
  name: "ecopages-radiant-esm",
165
138
  fileName: VENDOR_FILE_NAMES.radiant
166
- }),
167
- createBrowserRuntimeScriptAsset({
168
- importPath: radiantInstallHydratorEntryModulePath,
169
- name: "ecopages-radiant-install-hydrator-esm",
170
- fileName: VENDOR_FILE_NAMES.radiantInstallHydrator
171
139
  })
172
140
  );
173
141
  }
174
142
  return deps;
175
143
  }
176
- createRadiantHydratorBootstrapAsset() {
177
- return AssetFactory.createInlineContentScript({
178
- position: "head",
179
- bundle: false,
180
- content: this.createRadiantHydratorBootstrapSource(),
181
- attributes: {
182
- ...BROWSER_RUNTIME_SCRIPT_ATTRIBUTES,
183
- [RADIANT_HYDRATOR_BOOTSTRAP_ATTRIBUTE]: "true"
184
- }
185
- });
186
- }
187
- createRadiantHydratorBootstrapSource() {
188
- return "import '@ecopages/radiant/client/install-hydrator';";
189
- }
190
144
  getArtifactsDir() {
191
145
  const rootDir = this.config.rootDir ?? process.cwd();
192
146
  return path.join(rootDir, "node_modules", ".cache", "ecopages-browser-runtime");
@@ -208,18 +162,13 @@ class JsxRuntimeBundleService {
208
162
  };
209
163
  if (this.config.radiant) {
210
164
  const radiantVendorUrl = buildBrowserRuntimeAssetUrl(VENDOR_FILE_NAMES.radiant);
211
- const radiantInstallHydratorVendorUrl = buildBrowserRuntimeAssetUrl(
212
- VENDOR_FILE_NAMES.radiantInstallHydrator
213
- );
214
- const radiantPkg = JSON.parse(
215
- readFileSync(findPackageManifestPath("@ecopages/radiant"), "utf8")
216
- );
165
+ const radiantPkg = readRadiantPackageJson(findPackageManifestPath("@ecopages/radiant"));
217
166
  for (const key of Object.keys(radiantPkg.exports ?? {})) {
218
167
  if (!isBrowserRuntimeRadiantSpecifier(key)) {
219
168
  continue;
220
169
  }
221
170
  const specifier = key === "." ? "@ecopages/radiant" : `@ecopages/radiant${key.slice(1)}`;
222
- specifierMap[specifier] = key === "./client/install-hydrator" ? radiantInstallHydratorVendorUrl : radiantVendorUrl;
171
+ specifierMap[specifier] = radiantVendorUrl;
223
172
  }
224
173
  }
225
174
  this.cachedSpecifierMap = specifierMap;
@@ -233,36 +182,28 @@ class JsxRuntimeBundleService {
233
182
  const filePath = path.join(artifactsDir, "ecopages-jsx-esm-entry.mjs");
234
183
  const manifestPath = findPackageManifestPath("@ecopages/jsx");
235
184
  const packageDir = path.dirname(realpathSync(manifestPath));
236
- const jsxPkg = JSON.parse(readFileSync(manifestPath, "utf8"));
237
- const jsxModulePath = this.resolvePackageExportModulePath(
238
- packageDir,
239
- ".",
240
- jsxPkg.exports?.["."]
241
- );
242
- const patchedRuntimeSource = createPatchedJsxBrowserRuntimeSource(readFileSync(jsxModulePath, "utf8"));
185
+ const jsxPkg = readRadiantPackageJson(manifestPath);
186
+ const jsxModulePath = this.resolvePackageExportModulePath(packageDir, ".", jsxPkg.exports?.["."]);
187
+ const jsxRuntimeSource = readFileSync(jsxModulePath, "utf8");
243
188
  mkdirSync(artifactsDir, { recursive: true });
244
- writeFileSync(filePath, patchedRuntimeSource, "utf8");
189
+ writeFileSync(filePath, jsxRuntimeSource, "utf8");
245
190
  this.cachedJsxEntryModulePath = filePath;
246
191
  return filePath;
247
192
  }
248
193
  getRadiantBrowserRuntimeModules() {
249
194
  const manifestPath = findPackageManifestPath("@ecopages/radiant");
250
195
  const packageDir = path.dirname(realpathSync(manifestPath));
251
- const radiantPkg = JSON.parse(readFileSync(manifestPath, "utf8"));
196
+ const radiantPkg = readRadiantPackageJson(manifestPath);
252
197
  return Object.entries(radiantPkg.exports ?? {}).filter(([key]) => isBrowserRuntimeRadiantSpecifier(key) && key !== ".").sort(([left], [right]) => left.localeCompare(right)).map(([exportKey, exportTarget]) => ({
253
198
  exportKey,
254
- modulePath: this.resolvePackageExportModulePath(
255
- packageDir,
256
- exportKey,
257
- exportTarget
258
- )
199
+ modulePath: this.resolvePackageExportModulePath(packageDir, exportKey, exportTarget)
259
200
  })).filter((module) => existsSync(module.modulePath));
260
201
  }
261
202
  resolvePackageExportModulePath(packageDir, exportKey, exportTarget) {
262
203
  if (typeof exportTarget === "string") {
263
204
  return path.resolve(packageDir, exportTarget);
264
205
  }
265
- if (exportTarget && typeof exportTarget === "object" && "import" in exportTarget) {
206
+ if (isObjectRecord(exportTarget) && "import" in exportTarget) {
266
207
  const importTarget = exportTarget.import;
267
208
  if (typeof importTarget === "string") {
268
209
  return path.resolve(packageDir, importTarget);
@@ -276,8 +217,18 @@ class JsxRuntimeBundleService {
276
217
  }
277
218
  const artifactsDir = this.getArtifactsDir();
278
219
  const filePath = path.join(artifactsDir, "ecopages-radiant-esm-entry.mjs");
220
+ const manifestPath = findPackageManifestPath("@ecopages/radiant");
221
+ const packageDir = path.dirname(realpathSync(manifestPath));
222
+ const radiantPkg = readRadiantPackageJson(manifestPath);
223
+ const installHydratorModulePath = this.resolvePackageExportModulePath(
224
+ packageDir,
225
+ "./client/install-hydrator",
226
+ radiantPkg.exports?.["./client/install-hydrator"]
227
+ );
279
228
  const seenExports = /* @__PURE__ */ new Set();
280
- const statements = [];
229
+ const statements = [
230
+ `import '${this.getEntryImportPath(artifactsDir, installHydratorModulePath)}';`
231
+ ];
281
232
  mkdirSync(artifactsDir, { recursive: true });
282
233
  for (const module of this.getRadiantBrowserRuntimeModules()) {
283
234
  const exportNames = getNamedExportNamesFromModuleSource(readFileSync(module.modulePath, "utf8")).filter((name) => !seenExports.has(name)).filter((name) => /^[$A-Z_a-z][$\w]*$/.test(name)).sort();
@@ -294,33 +245,7 @@ class JsxRuntimeBundleService {
294
245
  this.cachedRadiantEntryModulePath = filePath;
295
246
  return filePath;
296
247
  }
297
- async getOrCreateRadiantInstallHydratorEntryModulePath() {
298
- if (this.cachedRadiantInstallHydratorEntryModulePath) {
299
- return this.cachedRadiantInstallHydratorEntryModulePath;
300
- }
301
- const artifactsDir = this.getArtifactsDir();
302
- const filePath = path.join(artifactsDir, "ecopages-radiant-install-hydrator-esm-entry.mjs");
303
- const manifestPath = findPackageManifestPath("@ecopages/radiant");
304
- const packageDir = path.dirname(realpathSync(manifestPath));
305
- const radiantPkg = JSON.parse(readFileSync(manifestPath, "utf8"));
306
- const modulePath = this.resolvePackageExportModulePath(
307
- packageDir,
308
- "./client/install-hydrator",
309
- radiantPkg.exports?.["./client/install-hydrator"]
310
- );
311
- mkdirSync(artifactsDir, { recursive: true });
312
- writeFileSync(
313
- filePath,
314
- `import '${this.getEntryImportPath(artifactsDir, modulePath)}';
315
- export {};
316
- `,
317
- "utf8"
318
- );
319
- this.cachedRadiantInstallHydratorEntryModulePath = filePath;
320
- return filePath;
321
- }
322
248
  }
323
249
  export {
324
- JsxRuntimeBundleService,
325
- RADIANT_HYDRATOR_BOOTSTRAP_ATTRIBUTE
250
+ JsxRuntimeBundleService
326
251
  };