@ecopages/ecopages-jsx 0.2.0-alpha.23 → 0.2.0-alpha.26
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 +5 -1
- package/package.json +4 -4
- package/src/ecopages-jsx-renderer.d.ts +14 -0
- package/src/ecopages-jsx-renderer.js +127 -5
- package/src/ecopages-jsx.plugin.d.ts +1 -0
- package/src/ecopages-jsx.plugin.js +4 -0
- package/src/ecopages-jsx.types.d.ts +5 -4
- package/src/services/jsx-runtime-bundle.service.d.ts +1 -6
- package/src/services/jsx-runtime-bundle.service.js +38 -113
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
### Bug Fixes
|
|
10
10
|
|
|
11
|
-
-
|
|
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.
|
|
3
|
+
"version": "0.2.0-alpha.26",
|
|
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.
|
|
25
|
-
"@ecopages/jsx": "0.3.0-alpha.
|
|
26
|
-
"@ecopages/radiant": "0.3.0-alpha.
|
|
24
|
+
"@ecopages/core": "0.2.0-alpha.26",
|
|
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 =
|
|
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
|
-
* -
|
|
41
|
-
* -
|
|
42
|
-
*
|
|
43
|
-
*
|
|
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
|
-
*
|
|
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
|
|
69
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
*
|
|
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
|
|
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] =
|
|
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 =
|
|
237
|
-
const jsxModulePath = this.resolvePackageExportModulePath(
|
|
238
|
-
|
|
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,
|
|
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 =
|
|
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 &&
|
|
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
|
};
|