@ecopages/ecopages-jsx 0.2.0-alpha.26 → 0.2.0-alpha.28
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 +9 -5
- package/README.md +12 -3
- package/package.json +4 -4
- package/src/ecopages-jsx-mdx.d.ts +21 -0
- package/src/ecopages-jsx-mdx.js +113 -0
- package/src/ecopages-jsx-radiant-ssr-policy.d.ts +33 -0
- package/src/ecopages-jsx-radiant-ssr-policy.js +80 -0
- package/src/ecopages-jsx-render-session.d.ts +39 -0
- package/src/ecopages-jsx-render-session.js +78 -0
- package/src/ecopages-jsx-renderer.d.ts +11 -54
- package/src/ecopages-jsx-renderer.js +117 -330
- package/src/ecopages-jsx.plugin.d.ts +4 -33
- package/src/ecopages-jsx.plugin.js +21 -196
- package/src/ecopages-jsx.types.d.ts +6 -10
- package/src/services/jsx-runtime-bundle.service.d.ts +0 -54
- package/src/services/jsx-runtime-bundle.service.js +0 -251
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import { rapidhash } from "@ecopages/core/hash";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
1
|
import {
|
|
5
2
|
IntegrationRenderer
|
|
6
3
|
} from "@ecopages/core/route-renderer/integration-renderer";
|
|
7
4
|
import { createMarkupNodeLike } from "@ecopages/jsx";
|
|
8
5
|
import { renderToString, withServerCustomElementRenderHook } from "@ecopages/jsx/server";
|
|
9
6
|
import { ECOPAGES_JSX_PLUGIN_NAME } from "./ecopages-jsx.constants.js";
|
|
7
|
+
import {
|
|
8
|
+
isMdxFile,
|
|
9
|
+
normalizeMdxPageModule
|
|
10
|
+
} from "./ecopages-jsx-mdx.js";
|
|
11
|
+
import { EcopagesJsxRenderSession } from "./ecopages-jsx-render-session.js";
|
|
12
|
+
import { EcopagesJsxRadiantSsrPolicy } from "./ecopages-jsx-radiant-ssr-policy.js";
|
|
10
13
|
class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
11
14
|
name = ECOPAGES_JSX_PLUGIN_NAME;
|
|
12
|
-
static radiantServerRuntimeInstallPromise;
|
|
13
|
-
static SCRIPT_IMPORT_RE = /import\s+(?:[^'";]+\s+from\s+)?['"](\.[^'"\n]*\.script(?:\.[cm]?[jt]sx?)?)['"]/g;
|
|
14
|
-
intrinsicCustomElementAssets;
|
|
15
|
-
intrinsicCustomElementScriptFiles;
|
|
16
|
-
collectedAssetFrames = [];
|
|
17
|
-
importedIntrinsicScriptFrames = [];
|
|
18
15
|
mdxExtensions;
|
|
19
|
-
|
|
16
|
+
renderSession;
|
|
17
|
+
radiantSsrPolicy;
|
|
20
18
|
/**
|
|
21
19
|
* Re-renders queued JSX children inside the owning renderer so nested custom
|
|
22
|
-
* elements and queued foreign
|
|
20
|
+
* elements and queued foreign subtrees contribute assets to the same frame.
|
|
23
21
|
*/
|
|
24
|
-
async
|
|
22
|
+
async renderQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
|
|
25
23
|
if (children === void 0) {
|
|
26
24
|
return { assets: [] };
|
|
27
25
|
}
|
|
@@ -34,24 +32,32 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
34
32
|
html = renderedChildren.html;
|
|
35
33
|
assets = renderedChildren.assets;
|
|
36
34
|
}
|
|
37
|
-
html = await this.
|
|
35
|
+
html = await this.foreignSubtreeExecutionService.resolveQueuedTokens(
|
|
36
|
+
html,
|
|
37
|
+
queuedResolutionsByToken,
|
|
38
|
+
resolveToken
|
|
39
|
+
);
|
|
38
40
|
return {
|
|
39
41
|
assets,
|
|
40
42
|
html
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
|
-
* Resolves queued foreign
|
|
46
|
+
* Resolves queued foreign subtrees after JSX has been stringified.
|
|
45
47
|
*
|
|
46
|
-
* JSX content needs one extra render pass because child
|
|
48
|
+
* JSX content needs one extra render pass because child foreign subtrees may emit
|
|
47
49
|
* additional browser assets while also replacing placeholder tokens.
|
|
48
50
|
*/
|
|
49
|
-
async
|
|
50
|
-
return this.
|
|
51
|
+
async resolveOwnedForeignSubtreeHtml(html, runtimeContext) {
|
|
52
|
+
return this.foreignSubtreeExecutionService.resolveQueuedHtml({
|
|
53
|
+
currentIntegrationName: this.name,
|
|
51
54
|
html,
|
|
52
55
|
runtimeContext,
|
|
53
56
|
queueLabel: "Ecopages JSX",
|
|
54
|
-
|
|
57
|
+
getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
|
|
58
|
+
applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
|
|
59
|
+
dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
|
|
60
|
+
renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => this.renderQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken)
|
|
55
61
|
});
|
|
56
62
|
}
|
|
57
63
|
constructor({
|
|
@@ -67,345 +73,126 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
67
73
|
resolvedIntegrationDependencies,
|
|
68
74
|
runtimeOrigin
|
|
69
75
|
});
|
|
70
|
-
this.intrinsicCustomElementAssets = jsxConfig?.intrinsicCustomElementAssets ?? /* @__PURE__ */ new Map();
|
|
71
|
-
this.intrinsicCustomElementScriptFiles = jsxConfig?.intrinsicCustomElementScriptFiles ?? /* @__PURE__ */ new Map();
|
|
72
76
|
this.mdxExtensions = jsxConfig?.mdxExtensions ?? [".mdx"];
|
|
73
|
-
this.
|
|
77
|
+
this.renderSession = new EcopagesJsxRenderSession(
|
|
78
|
+
(assets) => this.htmlTransformer.dedupeProcessedAssets(assets)
|
|
79
|
+
);
|
|
80
|
+
this.radiantSsrPolicy = jsxConfig?.radiantSsrPolicy ?? new EcopagesJsxRadiantSsrPolicy(jsxConfig?.radiantSsrEnabled ?? false);
|
|
74
81
|
}
|
|
75
82
|
/** Returns whether the requested page file should be treated as MDX. */
|
|
76
83
|
isMdxFile(filePath) {
|
|
77
|
-
return this.mdxExtensions
|
|
84
|
+
return isMdxFile(filePath, this.mdxExtensions);
|
|
78
85
|
}
|
|
79
86
|
async importPageFile(file, options) {
|
|
80
|
-
await this.
|
|
87
|
+
await this.radiantSsrPolicy.prepareRuntime();
|
|
81
88
|
const module = await super.importPageFile(file, options);
|
|
82
|
-
return this.isMdxFile(file) ?
|
|
89
|
+
return this.isMdxFile(file) ? normalizeMdxPageModule(file, module) : module;
|
|
83
90
|
}
|
|
84
91
|
async render(options) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} catch (error) {
|
|
111
|
-
throw this.createRenderError("Error rendering page", error);
|
|
112
|
-
} finally {
|
|
113
|
-
this.endImportedIntrinsicScriptFrame(importedScriptFrame);
|
|
114
|
-
}
|
|
92
|
+
return await this.renderSession.withActiveScope(async () => {
|
|
93
|
+
try {
|
|
94
|
+
return await this.renderPageWithDocumentShell({
|
|
95
|
+
page: {
|
|
96
|
+
component: options.Page,
|
|
97
|
+
props: {
|
|
98
|
+
...options.pageProps,
|
|
99
|
+
locals: options.pageLocals
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
layout: options.Layout ? {
|
|
103
|
+
component: options.Layout,
|
|
104
|
+
props: {
|
|
105
|
+
...options.pageProps,
|
|
106
|
+
locals: options.locals
|
|
107
|
+
}
|
|
108
|
+
} : void 0,
|
|
109
|
+
htmlTemplate: options.HtmlTemplate,
|
|
110
|
+
metadata: options.metadata,
|
|
111
|
+
pageProps: options.pageProps ?? {}
|
|
112
|
+
});
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw this.createRenderError("Error rendering page", error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
115
117
|
}
|
|
116
118
|
async renderComponent(input) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
return await this.renderSession.withActiveScope(async () => {
|
|
120
|
+
const assetFrame = this.renderSession.beginCollectedAssetFrame();
|
|
121
|
+
try {
|
|
122
|
+
if (typeof input.component !== "function") {
|
|
123
|
+
throw new TypeError("JSX renderer expected a callable component.");
|
|
124
|
+
}
|
|
125
|
+
const component = input.component;
|
|
126
|
+
const componentProps = input.children === void 0 ? input.props : {
|
|
127
|
+
...input.props,
|
|
128
|
+
children: typeof input.children === "string" ? createMarkupNodeLike(input.children) : input.children
|
|
129
|
+
};
|
|
130
|
+
const componentAssetsFromRender = [];
|
|
131
|
+
const content = await this.withCustomElementRenderHook(
|
|
132
|
+
componentAssetsFromRender,
|
|
133
|
+
() => component(componentProps)
|
|
134
|
+
);
|
|
135
|
+
this.renderSession.recordCollectedAssets(componentAssetsFromRender);
|
|
136
|
+
const rendered = await this.renderJsx(content);
|
|
137
|
+
const queuedForeignSubtreeResolution = await this.resolveOwnedForeignSubtreeHtml(
|
|
138
|
+
rendered.html,
|
|
139
|
+
this.getQueuedForeignSubtreeResolutionContext(input)
|
|
140
|
+
);
|
|
141
|
+
const componentAssets = input.component.config?.dependencies && typeof this.assetProcessingService?.processDependencies === "function" ? await this.processComponentDependencies([input.component]) : [];
|
|
142
|
+
const assets = this.htmlTransformer.dedupeProcessedAssets([
|
|
143
|
+
...this.renderSession.endCollectedAssetFrame(assetFrame),
|
|
144
|
+
...queuedForeignSubtreeResolution.assets,
|
|
145
|
+
...componentAssets
|
|
146
|
+
]);
|
|
147
|
+
return {
|
|
148
|
+
html: queuedForeignSubtreeResolution.html,
|
|
149
|
+
canAttachAttributes: true,
|
|
150
|
+
rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
|
|
151
|
+
integrationName: this.name,
|
|
152
|
+
assets
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
this.renderSession.endCollectedAssetFrame(assetFrame);
|
|
156
|
+
throw this.createRenderError("Error rendering component", error);
|
|
122
157
|
}
|
|
123
|
-
const content = await this.renderEcoComponent(input.component, this.createComponentProps(input));
|
|
124
|
-
const rendered = await this.renderJsx(content);
|
|
125
|
-
const queuedBoundaryResolution = await this.resolveOwnedBoundaryHtml(
|
|
126
|
-
rendered.html,
|
|
127
|
-
this.getQueuedBoundaryRuntime(input)
|
|
128
|
-
);
|
|
129
|
-
const componentAssets = await this.collectComponentAssets(input.component);
|
|
130
|
-
const assets = this.htmlTransformer.dedupeProcessedAssets([
|
|
131
|
-
...this.endCollectedAssetFrame(assetFrame),
|
|
132
|
-
...queuedBoundaryResolution.assets,
|
|
133
|
-
...componentAssets
|
|
134
|
-
]);
|
|
135
|
-
return {
|
|
136
|
-
html: queuedBoundaryResolution.html,
|
|
137
|
-
canAttachAttributes: true,
|
|
138
|
-
rootTag: this.getRootTagName(queuedBoundaryResolution.html),
|
|
139
|
-
integrationName: this.name,
|
|
140
|
-
assets
|
|
141
|
-
};
|
|
142
|
-
} catch (error) {
|
|
143
|
-
this.endCollectedAssetFrame(assetFrame);
|
|
144
|
-
throw this.createRenderError("Error rendering component", error);
|
|
145
|
-
} finally {
|
|
146
|
-
this.endImportedIntrinsicScriptFrame(importedScriptFrame);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
createComponentBoundaryRuntime(options) {
|
|
150
|
-
return this.createQueuedBoundaryRuntime({
|
|
151
|
-
boundaryInput: options.boundaryInput,
|
|
152
|
-
rendererCache: options.rendererCache
|
|
153
158
|
});
|
|
154
159
|
}
|
|
155
160
|
async renderToResponse(view, props, ctx) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
return await this.renderSession.withActiveScope(async () => {
|
|
162
|
+
try {
|
|
163
|
+
if (typeof view !== "function") {
|
|
164
|
+
throw new TypeError("JSX renderer expected a callable view component.");
|
|
165
|
+
}
|
|
166
|
+
const viewComponent = view;
|
|
167
|
+
return await this.renderViewWithDocumentShell({
|
|
168
|
+
view: viewComponent,
|
|
169
|
+
props,
|
|
170
|
+
ctx,
|
|
171
|
+
layout: viewComponent.config?.layout
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw this.createRenderError("Error rendering view", error);
|
|
160
175
|
}
|
|
161
|
-
return await this.renderViewWithDocumentShell({
|
|
162
|
-
view,
|
|
163
|
-
props,
|
|
164
|
-
ctx,
|
|
165
|
-
layout: view.config?.layout
|
|
166
|
-
});
|
|
167
|
-
} catch (error) {
|
|
168
|
-
throw this.createRenderError("Error rendering view", error);
|
|
169
|
-
} finally {
|
|
170
|
-
this.endImportedIntrinsicScriptFrame(importedScriptFrame);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Normalizes MDX modules into the same page contract as JSX route modules.
|
|
175
|
-
*
|
|
176
|
-
* MDX files export page metadata alongside generated component code, so the
|
|
177
|
-
* renderer folds those exports back into the Ecopages component shape before
|
|
178
|
-
* any layout or document-shell logic runs.
|
|
179
|
-
*/
|
|
180
|
-
normalizeMdxPageModule(file, module) {
|
|
181
|
-
if (!this.isFunctionComponent(module.default)) {
|
|
182
|
-
throw new TypeError("MDX file must export a callable default component.");
|
|
183
|
-
}
|
|
184
|
-
const Page = module.default;
|
|
185
|
-
const normalizedConfig = {
|
|
186
|
-
...module.config ?? Page.config ?? {},
|
|
187
|
-
...module.layout ? { layout: module.layout } : {},
|
|
188
|
-
__eco: module.config?.__eco ?? Page.config?.__eco ?? this.createEcoMeta(file)
|
|
189
|
-
};
|
|
190
|
-
const wrappedPage = this.wrapMdxPage(Page, {
|
|
191
|
-
config: normalizedConfig,
|
|
192
|
-
metadata: module.getMetadata ?? Page.metadata
|
|
193
176
|
});
|
|
194
|
-
return {
|
|
195
|
-
...module,
|
|
196
|
-
default: wrappedPage,
|
|
197
|
-
config: normalizedConfig
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
beginCollectedAssetFrame() {
|
|
201
|
-
const frame = [];
|
|
202
|
-
this.collectedAssetFrames.push(frame);
|
|
203
|
-
return frame;
|
|
204
|
-
}
|
|
205
|
-
endCollectedAssetFrame(frame) {
|
|
206
|
-
const activeFrame = this.collectedAssetFrames.pop();
|
|
207
|
-
if (!activeFrame || activeFrame !== frame) {
|
|
208
|
-
return this.htmlTransformer.dedupeProcessedAssets(frame);
|
|
209
|
-
}
|
|
210
|
-
return this.htmlTransformer.dedupeProcessedAssets(activeFrame);
|
|
211
177
|
}
|
|
212
178
|
async renderJsx(value) {
|
|
213
|
-
await this.ensureRadiantServerRuntimeIfEnabled();
|
|
214
179
|
const collectedAssets = [];
|
|
215
|
-
const html =
|
|
216
|
-
|
|
217
|
-
() => renderToString(value)
|
|
218
|
-
);
|
|
219
|
-
const dedupedAssets = this.recordCollectedAssets(collectedAssets);
|
|
180
|
+
const html = await this.withCustomElementRenderHook(collectedAssets, () => renderToString(value));
|
|
181
|
+
const dedupedAssets = this.renderSession.recordCollectedAssets(collectedAssets);
|
|
220
182
|
return {
|
|
221
183
|
assets: dedupedAssets,
|
|
222
184
|
html
|
|
223
185
|
};
|
|
224
186
|
}
|
|
225
|
-
async
|
|
226
|
-
await this.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this.createIntrinsicCustomElementRenderHook(collectedAssets),
|
|
230
|
-
() => this.invokeComponent(component, props)
|
|
187
|
+
async withCustomElementRenderHook(target, render) {
|
|
188
|
+
await this.radiantSsrPolicy.prepareRuntime();
|
|
189
|
+
return await this.radiantSsrPolicy.withRuntime(
|
|
190
|
+
() => withServerCustomElementRenderHook(this.createIntrinsicCustomElementRenderHook(target), render)
|
|
231
191
|
);
|
|
232
|
-
this.recordCollectedAssets(collectedAssets);
|
|
233
|
-
return rendered;
|
|
234
|
-
}
|
|
235
|
-
recordCollectedAssets(collectedAssets) {
|
|
236
|
-
const dedupedAssets = this.htmlTransformer.dedupeProcessedAssets(collectedAssets);
|
|
237
|
-
const activeFrame = this.collectedAssetFrames[this.collectedAssetFrames.length - 1];
|
|
238
|
-
if (activeFrame) {
|
|
239
|
-
activeFrame.push(...dedupedAssets);
|
|
240
|
-
}
|
|
241
|
-
return dedupedAssets;
|
|
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
192
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
}
|
|
341
|
-
async ensureRadiantServerRuntimeIfEnabled() {
|
|
342
|
-
if (!this.radiantSsrEnabled) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
await this.ensureRadiantServerRuntimeInstalled();
|
|
346
|
-
}
|
|
347
|
-
async ensureRadiantServerRuntimeInstalled() {
|
|
348
|
-
if (!EcopagesJsxRenderer.radiantServerRuntimeInstallPromise) {
|
|
349
|
-
EcopagesJsxRenderer.radiantServerRuntimeInstallPromise = Promise.all([
|
|
350
|
-
import("@ecopages/radiant/server/render-component"),
|
|
351
|
-
import("@ecopages/radiant/server/light-dom-shim").then((module) => {
|
|
352
|
-
module.installLightDomShim();
|
|
353
|
-
})
|
|
354
|
-
]).then(() => void 0);
|
|
355
|
-
}
|
|
356
|
-
await EcopagesJsxRenderer.radiantServerRuntimeInstallPromise;
|
|
357
|
-
}
|
|
358
|
-
isFunctionComponent(component) {
|
|
359
|
-
return typeof component === "function";
|
|
360
|
-
}
|
|
361
|
-
createComponentProps(input) {
|
|
362
|
-
if (input.children === void 0) {
|
|
363
|
-
return input.props;
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
...input.props,
|
|
367
|
-
children: typeof input.children === "string" ? createMarkupNodeLike(input.children) : input.children
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
async collectComponentAssets(component) {
|
|
371
|
-
if (!component.config?.dependencies || typeof this.assetProcessingService?.processDependencies !== "function") {
|
|
372
|
-
return [];
|
|
373
|
-
}
|
|
374
|
-
return this.processComponentDependencies([component]);
|
|
375
|
-
}
|
|
376
|
-
async invokeComponent(component, props) {
|
|
377
|
-
return await component(props);
|
|
378
|
-
}
|
|
379
|
-
createEcoMeta(file) {
|
|
380
|
-
return {
|
|
381
|
-
id: String(rapidhash(file)),
|
|
382
|
-
file,
|
|
383
|
-
integration: ECOPAGES_JSX_PLUGIN_NAME
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
wrapMdxPage(page, {
|
|
387
|
-
config,
|
|
388
|
-
metadata
|
|
389
|
-
}) {
|
|
390
|
-
const wrappedPage = async (props) => await this.invokeComponent(page, props);
|
|
391
|
-
wrappedPage.config = config;
|
|
392
|
-
if (metadata) {
|
|
393
|
-
wrappedPage.metadata = metadata;
|
|
394
|
-
}
|
|
395
|
-
return wrappedPage;
|
|
396
|
-
}
|
|
397
|
-
createIntrinsicCustomElementRenderHook(target) {
|
|
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
|
-
}
|
|
404
|
-
const assets = this.intrinsicCustomElementAssets.get(tagName);
|
|
405
|
-
if (assets) {
|
|
406
|
-
target.push(...assets);
|
|
407
|
-
}
|
|
408
|
-
return void 0;
|
|
193
|
+
createIntrinsicCustomElementRenderHook(_target) {
|
|
194
|
+
return ({ instance }) => {
|
|
195
|
+
return instance ? this.radiantSsrPolicy.renderIntrinsicElementMarkup(instance) : void 0;
|
|
409
196
|
};
|
|
410
197
|
}
|
|
411
198
|
}
|
|
@@ -1,37 +1,19 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { IntegrationPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
1
|
+
import { IntegrationPlugin, type EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
3
2
|
import type { JsxRenderable } from '@ecopages/jsx';
|
|
4
3
|
import { EcopagesJsxRenderer } from './ecopages-jsx-renderer.js';
|
|
5
4
|
import type { EcopagesJsxPluginOptions } from './ecopages-jsx.types.js';
|
|
6
|
-
export type {
|
|
5
|
+
export type { EcopagesJsxMdxOptions, EcopagesJsxPluginOptions, EcopagesJsxRendererConfig, } from './ecopages-jsx.types.js';
|
|
7
6
|
/** JSX integration plugin for Ecopages, supporting `.tsx` templates and optional Radiant web components. */
|
|
8
7
|
export declare class EcopagesJsxPlugin extends IntegrationPlugin<JsxRenderable> {
|
|
9
8
|
renderer: typeof EcopagesJsxRenderer;
|
|
10
|
-
private customElementAssets;
|
|
11
|
-
private customElementScriptFiles;
|
|
12
9
|
private includeRadiant;
|
|
13
10
|
private mdxEnabled;
|
|
14
11
|
private mdxCompilerOptions?;
|
|
15
12
|
private mdxExtensions;
|
|
16
13
|
private mdxLoaderPlugin?;
|
|
17
|
-
private runtimeBundleService;
|
|
18
|
-
private runtimeSpecifierMap;
|
|
19
|
-
private runtimeDepsInitialized;
|
|
20
14
|
/** Returns the build plugins required by the JSX integration. */
|
|
21
15
|
get plugins(): EcoBuildPlugin[];
|
|
22
|
-
/**
|
|
23
|
-
get browserBuildPlugins(): EcoBuildPlugin[];
|
|
24
|
-
/**
|
|
25
|
-
* Exposes the bare-module specifier map used by the import map.
|
|
26
|
-
*
|
|
27
|
-
* Client bundles keep these imports external so the browser can load the
|
|
28
|
-
* shared runtime packages from the generated vendor assets.
|
|
29
|
-
*/
|
|
30
|
-
getRuntimeSpecifierMap(): Record<string, string>;
|
|
31
|
-
/**
|
|
32
|
-
* Creates the renderer instance and attaches the discovered intrinsic custom
|
|
33
|
-
* element assets before the renderer handles any requests.
|
|
34
|
-
*/
|
|
16
|
+
/** Creates the renderer instance with the resolved JSX integration runtime options. */
|
|
35
17
|
initializeRenderer(options?: {
|
|
36
18
|
rendererModules?: unknown;
|
|
37
19
|
}): EcopagesJsxRenderer;
|
|
@@ -39,8 +21,7 @@ export declare class EcopagesJsxPlugin extends IntegrationPlugin<JsxRenderable>
|
|
|
39
21
|
/** Ensures MDX build hooks are ready before Ecopages collects contributions. */
|
|
40
22
|
prepareBuildContributions(): Promise<void>;
|
|
41
23
|
/**
|
|
42
|
-
* Registers MDX tooling
|
|
43
|
-
* completes the base integration setup.
|
|
24
|
+
* Registers MDX tooling and completes the base integration setup.
|
|
44
25
|
*/
|
|
45
26
|
setup(): Promise<void>;
|
|
46
27
|
private ensureMdxLoaderPlugin;
|
|
@@ -51,16 +32,6 @@ export declare class EcopagesJsxPlugin extends IntegrationPlugin<JsxRenderable>
|
|
|
51
32
|
* this hook stays isolated from manifest preparation.
|
|
52
33
|
*/
|
|
53
34
|
private registerMdxBunPlugin;
|
|
54
|
-
/**
|
|
55
|
-
* Scans `src/` for custom-element entry scripts and pre-resolves their assets.
|
|
56
|
-
*
|
|
57
|
-
* The renderer's server-side custom-element hook relies on this registry to
|
|
58
|
-
* attach browser scripts without per-render file-system lookups.
|
|
59
|
-
*/
|
|
60
|
-
private buildCustomElementRegistry;
|
|
61
|
-
private collectScriptEntries;
|
|
62
|
-
private resolveCustomElementAsset;
|
|
63
|
-
private extractCustomElementTagNames;
|
|
64
35
|
}
|
|
65
36
|
/**
|
|
66
37
|
* Creates the JSX integration plugin with resolved defaults.
|