@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.
@@ -1,65 +1,14 @@
1
- import { readFile, readdir } from "node:fs/promises";
2
- import path from "node:path";
3
- import { IntegrationPlugin } from "@ecopages/core/plugins/integration-plugin";
4
- import { AssetFactory } from "@ecopages/core/services/asset-processing-service";
5
- import { VFile } from "vfile";
1
+ import {
2
+ IntegrationPlugin
3
+ } from "@ecopages/core/plugins/integration-plugin";
6
4
  import { ECOPAGES_JSX_PLUGIN_NAME } from "./ecopages-jsx.constants.js";
5
+ import {
6
+ appendMdxExtensions,
7
+ createMdxLoaderPlugin,
8
+ registerBunMdxPlugin,
9
+ resolveMdxCompilerOptions
10
+ } from "./ecopages-jsx-mdx.js";
7
11
  import { EcopagesJsxRenderer } from "./ecopages-jsx-renderer.js";
8
- import { JsxRuntimeBundleService } from "./services/jsx-runtime-bundle.service.js";
9
- const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
- const mergePluginLists = (...lists) => {
11
- const merged = lists.flatMap((list) => list ? [...list] : []);
12
- return merged.length > 0 ? merged : void 0;
13
- };
14
- const createMdxExtensionFilter = (extensions, options) => {
15
- const escaped = extensions.map(escapeRegex);
16
- const suffix = options?.allowQueryString ? "(\\?.*)?$" : "$";
17
- return new RegExp(`(${escaped.join("|")})${suffix}`);
18
- };
19
- const appendMdxExtensions = (target, mdxExtensions) => {
20
- for (const ext of mdxExtensions) {
21
- if (!target.includes(ext)) {
22
- target.push(ext);
23
- }
24
- }
25
- };
26
- const resolveMdxCompilerOptions = (mdxOptions) => {
27
- const { compilerOptions, remarkPlugins, rehypePlugins, recmaPlugins } = mdxOptions;
28
- const resolved = {
29
- format: "detect",
30
- outputFormat: "program",
31
- ...compilerOptions,
32
- jsxImportSource: "@ecopages/jsx",
33
- jsxRuntime: "automatic",
34
- development: process.env.NODE_ENV === "development"
35
- };
36
- const mergedRemark = mergePluginLists(compilerOptions?.remarkPlugins, remarkPlugins);
37
- const mergedRehype = mergePluginLists(compilerOptions?.rehypePlugins, rehypePlugins);
38
- const mergedRecma = mergePluginLists(compilerOptions?.recmaPlugins, recmaPlugins);
39
- if (mergedRemark) resolved.remarkPlugins = mergedRemark;
40
- if (mergedRehype) resolved.rehypePlugins = mergedRehype;
41
- if (mergedRecma) resolved.recmaPlugins = mergedRecma;
42
- return resolved;
43
- };
44
- const createMdxLoaderPlugin = (compilerOptions, extensions) => {
45
- const filter = createMdxExtensionFilter(extensions, { allowQueryString: true });
46
- return {
47
- name: "ecopages-jsx-mdx-loader",
48
- setup(build) {
49
- build.onLoad({ filter }, async (args) => {
50
- const { compile } = await import("@mdx-js/mdx");
51
- const filePath = args.path.includes("?") ? args.path.split("?")[0] : args.path;
52
- const source = await readFile(filePath, "utf-8");
53
- const compiled = await compile(new VFile({ value: source, path: filePath }), compilerOptions);
54
- return {
55
- contents: String(compiled.value),
56
- loader: "js",
57
- resolveDir: path.dirname(filePath)
58
- };
59
- });
60
- }
61
- };
62
- };
63
12
  const resolvePluginOptions = (options) => {
64
13
  const { extensions: userExtensions, radiant, mdx, ...baseConfig } = options ?? {};
65
14
  const extensions = [...userExtensions ?? [".tsx"]];
@@ -79,49 +28,26 @@ const resolvePluginOptions = (options) => {
79
28
  };
80
29
  class EcopagesJsxPlugin extends IntegrationPlugin {
81
30
  renderer = EcopagesJsxRenderer;
82
- customElementAssets = /* @__PURE__ */ new Map();
83
- customElementScriptFiles = /* @__PURE__ */ new Map();
84
31
  includeRadiant;
85
32
  mdxEnabled;
86
33
  mdxCompilerOptions;
87
34
  mdxExtensions;
88
35
  mdxLoaderPlugin;
89
- runtimeBundleService;
90
- runtimeSpecifierMap = {};
91
- runtimeDepsInitialized = false;
92
36
  /** Returns the build plugins required by the JSX integration. */
93
37
  get plugins() {
94
38
  return [this.mdxLoaderPlugin].filter((plugin) => plugin !== void 0);
95
39
  }
96
- /** Returns the browser-only build plugins required by the JSX integration. */
97
- get browserBuildPlugins() {
98
- return [this.runtimeBundleService.getBuildPlugin()];
99
- }
100
- /**
101
- * Exposes the bare-module specifier map used by the import map.
102
- *
103
- * Client bundles keep these imports external so the browser can load the
104
- * shared runtime packages from the generated vendor assets.
105
- */
106
- getRuntimeSpecifierMap() {
107
- return this.runtimeSpecifierMap;
108
- }
109
- /**
110
- * Creates the renderer instance and attaches the discovered intrinsic custom
111
- * element assets before the renderer handles any requests.
112
- */
40
+ /** Creates the renderer instance with the resolved JSX integration runtime options. */
113
41
  initializeRenderer(options) {
114
- const rendererConfig = {
115
- intrinsicCustomElementAssets: this.customElementAssets,
116
- intrinsicCustomElementScriptFiles: this.customElementScriptFiles,
117
- mdxExtensions: this.mdxExtensions,
118
- radiantSsrEnabled: this.includeRadiant
119
- };
120
- const renderer = new this.renderer({
121
- ...this.createRendererOptions(options),
122
- jsxConfig: rendererConfig
123
- });
124
- return this.attachRendererRuntimeServices(renderer);
42
+ return this.attachRendererRuntimeServices(
43
+ new this.renderer({
44
+ ...this.createRendererOptions(options),
45
+ jsxConfig: {
46
+ mdxExtensions: this.mdxExtensions,
47
+ radiantSsrEnabled: this.includeRadiant
48
+ }
49
+ })
50
+ );
125
51
  }
126
52
  constructor(options) {
127
53
  const config = resolvePluginOptions(options);
@@ -133,32 +59,22 @@ class EcopagesJsxPlugin extends IntegrationPlugin {
133
59
  ...baseConfig
134
60
  });
135
61
  this.includeRadiant = includeRadiant;
136
- this.runtimeBundleService = new JsxRuntimeBundleService({ radiant: includeRadiant });
137
62
  this.mdxEnabled = mdxEnabled;
138
63
  this.mdxExtensions = mdxExtensions;
139
64
  this.mdxCompilerOptions = mdxCompilerOptions;
140
65
  }
141
66
  /** Ensures MDX build hooks are ready before Ecopages collects contributions. */
142
67
  async prepareBuildContributions() {
143
- if (!this.runtimeDepsInitialized) {
144
- this.runtimeDepsInitialized = true;
145
- this.runtimeBundleService.setRootDir(this.appConfig?.rootDir);
146
- this.runtimeSpecifierMap = await this.runtimeBundleService.getSpecifierMap();
147
- const vendorDeps = await this.runtimeBundleService.getDependencies();
148
- this.integrationDependencies.unshift(...vendorDeps);
149
- }
150
68
  this.ensureMdxLoaderPlugin();
151
69
  }
152
70
  /**
153
- * Registers MDX tooling, discovers intrinsic custom-element assets, and then
154
- * completes the base integration setup.
71
+ * Registers MDX tooling and completes the base integration setup.
155
72
  */
156
73
  async setup() {
157
74
  this.ensureMdxLoaderPlugin();
158
75
  if (typeof Bun !== "undefined" && this.mdxEnabled && this.mdxCompilerOptions) {
159
76
  await this.registerMdxBunPlugin();
160
77
  }
161
- await this.buildCustomElementRegistry();
162
78
  await super.setup();
163
79
  }
164
80
  ensureMdxLoaderPlugin() {
@@ -177,98 +93,7 @@ class EcopagesJsxPlugin extends IntegrationPlugin {
177
93
  if (typeof Bun === "undefined" || !this.mdxCompilerOptions) {
178
94
  return;
179
95
  }
180
- const compilerOptions = this.mdxCompilerOptions;
181
- const filter = createMdxExtensionFilter(this.mdxExtensions);
182
- Bun.plugin({
183
- name: "ecopages-jsx-mdx",
184
- setup(build) {
185
- build.onLoad({ filter }, async (args) => {
186
- const { compile } = await import("@mdx-js/mdx");
187
- const source = await readFile(args.path, "utf-8");
188
- const compiled = await compile(new VFile({ value: source, path: args.path }), compilerOptions);
189
- return { contents: String(compiled.value), loader: "js" };
190
- });
191
- }
192
- });
193
- }
194
- /**
195
- * Scans `src/` for custom-element entry scripts and pre-resolves their assets.
196
- *
197
- * The renderer's server-side custom-element hook relies on this registry to
198
- * attach browser scripts without per-render file-system lookups.
199
- */
200
- async buildCustomElementRegistry() {
201
- if (!this.appConfig || !this.assetProcessingService) {
202
- return;
203
- }
204
- this.customElementAssets.clear();
205
- this.customElementScriptFiles.clear();
206
- const scriptFiles = await this.collectScriptEntries(this.appConfig.absolutePaths.srcDir);
207
- for (const scriptFile of scriptFiles) {
208
- const tagNames = await this.extractCustomElementTagNames(scriptFile);
209
- if (tagNames.length === 0) {
210
- continue;
211
- }
212
- const asset = await this.resolveCustomElementAsset(scriptFile);
213
- if (!asset) {
214
- continue;
215
- }
216
- for (const tagName of tagNames) {
217
- this.customElementAssets.set(tagName, [asset]);
218
- this.customElementScriptFiles.set(tagName, scriptFile);
219
- }
220
- }
221
- }
222
- async collectScriptEntries(directory) {
223
- const entries = await readdir(directory, { withFileTypes: true });
224
- const scripts = [];
225
- for (const entry of entries) {
226
- const entryPath = path.join(directory, entry.name);
227
- if (entry.isDirectory()) {
228
- scripts.push(...await this.collectScriptEntries(entryPath));
229
- continue;
230
- }
231
- if (/\.script\.(?:ts|tsx)$/.test(entry.name)) {
232
- scripts.push(entryPath);
233
- }
234
- }
235
- return scripts;
236
- }
237
- async resolveCustomElementAsset(scriptFile) {
238
- if (!this.assetProcessingService) {
239
- return void 0;
240
- }
241
- const [asset] = await this.assetProcessingService.processDependencies(
242
- [
243
- AssetFactory.createFileScript({
244
- filepath: scriptFile,
245
- position: "head",
246
- attributes: {
247
- type: "module",
248
- defer: ""
249
- }
250
- })
251
- ],
252
- `${this.name}:custom-elements:${scriptFile}`
253
- );
254
- return asset;
255
- }
256
- async extractCustomElementTagNames(scriptFile) {
257
- const source = await readFile(scriptFile, "utf8");
258
- const tagNames = /* @__PURE__ */ new Set();
259
- for (const match of source.matchAll(/@customElement\(\s*['"]([^'"]+)['"]/g)) {
260
- const tagName = match[1];
261
- if (tagName) {
262
- tagNames.add(tagName);
263
- }
264
- }
265
- for (const match of source.matchAll(/customElement\(\s*['"]([^'"]+)['"]\s*\)\s*\(/g)) {
266
- const tagName = match[1];
267
- if (tagName) {
268
- tagNames.add(tagName);
269
- }
270
- }
271
- return [...tagNames];
96
+ await registerBunMdxPlugin(this.mdxCompilerOptions, this.mdxExtensions);
272
97
  }
273
98
  }
274
99
  const ecopagesJsxPlugin = (options) => new EcopagesJsxPlugin(options);
@@ -1,7 +1,8 @@
1
1
  import type { CompileOptions } from '@mdx-js/mdx';
2
2
  import type { IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
3
3
  import type { AssetDefinition, AssetProcessingService, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
4
- import type { EcoPagesAppConfig } from '@ecopages/core/internal-types';
4
+ import type { EcoPagesAppConfig } from '@ecopages/core';
5
+ import type { EcopagesJsxRadiantSsrPolicy } from './ecopages-jsx-radiant-ssr-policy.js';
5
6
  type MdxPluginList = NonNullable<CompileOptions['remarkPlugins']>;
6
7
  export type EcopagesJsxMdxCompileOptions = Omit<CompileOptions, 'jsxImportSource' | 'jsxRuntime' | 'remarkPlugins' | 'rehypePlugins' | 'recmaPlugins'> & {
7
8
  remarkPlugins?: MdxPluginList;
@@ -33,14 +34,10 @@ export type EcopagesJsxPluginOptions = Omit<IntegrationPluginConfig, 'name' | 'e
33
34
  /** Optional JSX route extensions. Defaults to `.tsx`. */
34
35
  extensions?: string[];
35
36
  /**
36
- * Whether to include the Radiant integration contract for JSX apps.
37
+ * Whether to enable the Radiant SSR contract for JSX apps.
37
38
  *
38
- * When enabled, Ecopages JSX:
39
- * - imports `@ecopages/radiant/server/render-component` before Radiant SSR
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
39
+ * When enabled, Ecopages JSX installs the Radiant server SSR runtime so
40
+ * intrinsic custom elements can render specialized host markup during SSR.
44
41
  *
45
42
  * Set to `false` when pages do not use Radiant web components.
46
43
  * @default true
@@ -50,9 +47,8 @@ export type EcopagesJsxPluginOptions = Omit<IntegrationPluginConfig, 'name' | 'e
50
47
  mdx?: EcopagesJsxMdxOptions;
51
48
  };
52
49
  export type EcopagesJsxRendererConfig = {
53
- intrinsicCustomElementAssets?: Map<string, readonly ProcessedAsset[]>;
54
- intrinsicCustomElementScriptFiles?: Map<string, string>;
55
50
  mdxExtensions?: string[];
51
+ radiantSsrPolicy?: EcopagesJsxRadiantSsrPolicy;
56
52
  radiantSsrEnabled?: boolean;
57
53
  };
58
54
  export type EcopagesJsxRendererOptions = {
@@ -1,54 +0,0 @@
1
- /**
2
- * Runtime bundle service for the JSX integration.
3
- *
4
- * Owns creation of browser runtime vendor assets, the import map specifier
5
- * mapping, and the build external plugin. Radiant sub-path specifiers are
6
- * derived at runtime from `@ecopages/radiant/package.json` exports so the
7
- * list stays in sync with whatever version is installed.
8
- *
9
- * @module
10
- */
11
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
12
- import { type AssetDefinition } from '@ecopages/core/services/asset-processing-service';
13
- export interface JsxRuntimeBundleServiceConfig {
14
- radiant: boolean;
15
- rootDir?: string;
16
- }
17
- export declare class JsxRuntimeBundleService {
18
- private readonly config;
19
- private cachedSpecifierMap;
20
- private cachedJsxEntryModulePath;
21
- private cachedRadiantEntryModulePath;
22
- constructor(config: JsxRuntimeBundleServiceConfig);
23
- setRootDir(rootDir: string | undefined): void;
24
- /**
25
- * Returns the build plugin that aliases JSX and Radiant runtime specifiers to
26
- * their emitted browser vendor URLs.
27
- *
28
- * @remarks
29
- * The returned plugin both externalizes the mapped specifiers during bundle
30
- * resolution and exposes alias metadata so Ecopages can rewrite any emitted JS
31
- * imports that still reference bare runtime specifiers.
32
- */
33
- getBuildPlugin(): EcoBuildPlugin;
34
- /**
35
- * Builds the bare-specifier-to-vendor-URL map for the browser import map.
36
- *
37
- * JSX sub-paths are always included. When `radiant` is enabled, radiant
38
- * sub-paths are derived from `@ecopages/radiant/package.json` exports and
39
- * the result is cached for the lifetime of this service instance.
40
- */
41
- getSpecifierMap(): Promise<Record<string, string>>;
42
- /**
43
- * Builds the full list of vendor asset definitions: the import map inline
44
- * assets plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
45
- */
46
- getDependencies(): Promise<AssetDefinition[]>;
47
- private getArtifactsDir;
48
- private getEntryImportPath;
49
- private getOrCreateSpecifierMap;
50
- private getOrCreateJsxEntryModulePath;
51
- private getRadiantBrowserRuntimeModules;
52
- private resolvePackageExportModulePath;
53
- private getOrCreateRadiantEntryModulePath;
54
- }
@@ -1,251 +0,0 @@
1
- import path from "node:path";
2
- import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
3
- import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
4
- import {
5
- buildBrowserRuntimeAssetUrl,
6
- createBrowserRuntimeScriptAsset
7
- } from "@ecopages/core/services/asset-processing-service";
8
- const VENDOR_FILE_NAMES = {
9
- jsx: "ecopages-jsx-esm.js",
10
- radiant: "ecopages-radiant-esm.js"
11
- };
12
- function getNamedExportNamesFromModuleSource(source) {
13
- const exportNames = /* @__PURE__ */ new Set();
14
- for (const match of source.matchAll(/export\s*\{([^}]+)\}/g)) {
15
- for (const specifier of match[1].split(",")) {
16
- const trimmedSpecifier = specifier.trim();
17
- if (!trimmedSpecifier) {
18
- continue;
19
- }
20
- const aliasMatch = trimmedSpecifier.match(/(?:.+\s+as\s+)?([A-Z_a-z$][\w$]*)$/);
21
- if (aliasMatch?.[1] && aliasMatch[1] !== "default") {
22
- exportNames.add(aliasMatch[1]);
23
- }
24
- }
25
- }
26
- for (const match of source.matchAll(
27
- /export\s+(?:async\s+)?(?:const|function|class|let|var)\s+([A-Z_a-z$][\w$]*)/g
28
- )) {
29
- if (match[1] !== "default") {
30
- exportNames.add(match[1]);
31
- }
32
- }
33
- return [...exportNames].sort();
34
- }
35
- function isBrowserRuntimeRadiantSpecifier(exportKey) {
36
- if (exportKey === "." || exportKey.startsWith("./context/")) {
37
- return true;
38
- }
39
- if (exportKey === "./controller-registry") {
40
- return true;
41
- }
42
- if (exportKey === "./client/hydrator") {
43
- return true;
44
- }
45
- if (exportKey === "./client/install-hydrator") {
46
- return true;
47
- }
48
- if (exportKey.startsWith("./decorators/") || exportKey.startsWith("./helpers/")) {
49
- return true;
50
- }
51
- return exportKey === "./core/radiant-controller" || exportKey === "./core/radiant-element";
52
- }
53
- function isObjectRecord(value) {
54
- return typeof value === "object" && value !== null;
55
- }
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
- };
67
- }
68
- function findPackageManifestPath(packageName) {
69
- let currentDir = path.dirname(new URL(import.meta.url).pathname);
70
- while (true) {
71
- const candidatePath = path.join(currentDir, "node_modules", packageName, "package.json");
72
- if (existsSync(candidatePath)) {
73
- return candidatePath;
74
- }
75
- const parentDir = path.dirname(currentDir);
76
- if (parentDir === currentDir) {
77
- break;
78
- }
79
- currentDir = parentDir;
80
- }
81
- throw new Error(`Could not locate ${packageName}/package.json from ${import.meta.url}`);
82
- }
83
- class JsxRuntimeBundleService {
84
- config;
85
- cachedSpecifierMap;
86
- cachedJsxEntryModulePath;
87
- cachedRadiantEntryModulePath;
88
- constructor(config) {
89
- this.config = config;
90
- }
91
- setRootDir(rootDir) {
92
- this.config.rootDir = rootDir;
93
- }
94
- /**
95
- * Returns the build plugin that aliases JSX and Radiant runtime specifiers to
96
- * their emitted browser vendor URLs.
97
- *
98
- * @remarks
99
- * The returned plugin both externalizes the mapped specifiers during bundle
100
- * resolution and exposes alias metadata so Ecopages can rewrite any emitted JS
101
- * imports that still reference bare runtime specifiers.
102
- */
103
- getBuildPlugin() {
104
- return createRuntimeSpecifierAliasPlugin(this.getOrCreateSpecifierMap(), {
105
- name: "ecopages-jsx-runtime-alias"
106
- });
107
- }
108
- /**
109
- * Builds the bare-specifier-to-vendor-URL map for the browser import map.
110
- *
111
- * JSX sub-paths are always included. When `radiant` is enabled, radiant
112
- * sub-paths are derived from `@ecopages/radiant/package.json` exports and
113
- * the result is cached for the lifetime of this service instance.
114
- */
115
- async getSpecifierMap() {
116
- return this.getOrCreateSpecifierMap();
117
- }
118
- /**
119
- * Builds the full list of vendor asset definitions: the import map inline
120
- * assets plus one `createBrowserRuntimeScriptAsset` per vendor bundle.
121
- */
122
- async getDependencies() {
123
- const jsxEntryModulePath = await this.getOrCreateJsxEntryModulePath();
124
- const deps = [];
125
- deps.push(
126
- createBrowserRuntimeScriptAsset({
127
- importPath: jsxEntryModulePath,
128
- name: "ecopages-jsx-esm",
129
- fileName: VENDOR_FILE_NAMES.jsx
130
- })
131
- );
132
- if (this.config.radiant) {
133
- const radiantEntryModulePath = await this.getOrCreateRadiantEntryModulePath();
134
- deps.push(
135
- createBrowserRuntimeScriptAsset({
136
- importPath: radiantEntryModulePath,
137
- name: "ecopages-radiant-esm",
138
- fileName: VENDOR_FILE_NAMES.radiant
139
- })
140
- );
141
- }
142
- return deps;
143
- }
144
- getArtifactsDir() {
145
- const rootDir = this.config.rootDir ?? process.cwd();
146
- return path.join(rootDir, "node_modules", ".cache", "ecopages-browser-runtime");
147
- }
148
- getEntryImportPath(fromDir, targetPath) {
149
- const relativeModulePath = path.relative(fromDir, targetPath).split(path.sep).join("/");
150
- return relativeModulePath.startsWith(".") ? relativeModulePath : `./${relativeModulePath}`;
151
- }
152
- getOrCreateSpecifierMap() {
153
- if (this.cachedSpecifierMap) {
154
- return this.cachedSpecifierMap;
155
- }
156
- const jsxVendorUrl = buildBrowserRuntimeAssetUrl(VENDOR_FILE_NAMES.jsx);
157
- const specifierMap = {
158
- "@ecopages/jsx": jsxVendorUrl,
159
- "@ecopages/jsx/client": jsxVendorUrl,
160
- "@ecopages/jsx/jsx-runtime": jsxVendorUrl,
161
- "@ecopages/jsx/jsx-dev-runtime": jsxVendorUrl
162
- };
163
- if (this.config.radiant) {
164
- const radiantVendorUrl = buildBrowserRuntimeAssetUrl(VENDOR_FILE_NAMES.radiant);
165
- const radiantPkg = readRadiantPackageJson(findPackageManifestPath("@ecopages/radiant"));
166
- for (const key of Object.keys(radiantPkg.exports ?? {})) {
167
- if (!isBrowserRuntimeRadiantSpecifier(key)) {
168
- continue;
169
- }
170
- const specifier = key === "." ? "@ecopages/radiant" : `@ecopages/radiant${key.slice(1)}`;
171
- specifierMap[specifier] = radiantVendorUrl;
172
- }
173
- }
174
- this.cachedSpecifierMap = specifierMap;
175
- return specifierMap;
176
- }
177
- async getOrCreateJsxEntryModulePath() {
178
- if (this.cachedJsxEntryModulePath) {
179
- return this.cachedJsxEntryModulePath;
180
- }
181
- const artifactsDir = this.getArtifactsDir();
182
- const filePath = path.join(artifactsDir, "ecopages-jsx-esm-entry.mjs");
183
- const manifestPath = findPackageManifestPath("@ecopages/jsx");
184
- const packageDir = path.dirname(realpathSync(manifestPath));
185
- const jsxPkg = readRadiantPackageJson(manifestPath);
186
- const jsxModulePath = this.resolvePackageExportModulePath(packageDir, ".", jsxPkg.exports?.["."]);
187
- const jsxRuntimeSource = readFileSync(jsxModulePath, "utf8");
188
- mkdirSync(artifactsDir, { recursive: true });
189
- writeFileSync(filePath, jsxRuntimeSource, "utf8");
190
- this.cachedJsxEntryModulePath = filePath;
191
- return filePath;
192
- }
193
- getRadiantBrowserRuntimeModules() {
194
- const manifestPath = findPackageManifestPath("@ecopages/radiant");
195
- const packageDir = path.dirname(realpathSync(manifestPath));
196
- const radiantPkg = readRadiantPackageJson(manifestPath);
197
- return Object.entries(radiantPkg.exports ?? {}).filter(([key]) => isBrowserRuntimeRadiantSpecifier(key) && key !== ".").sort(([left], [right]) => left.localeCompare(right)).map(([exportKey, exportTarget]) => ({
198
- exportKey,
199
- modulePath: this.resolvePackageExportModulePath(packageDir, exportKey, exportTarget)
200
- })).filter((module) => existsSync(module.modulePath));
201
- }
202
- resolvePackageExportModulePath(packageDir, exportKey, exportTarget) {
203
- if (typeof exportTarget === "string") {
204
- return path.resolve(packageDir, exportTarget);
205
- }
206
- if (isObjectRecord(exportTarget) && "import" in exportTarget) {
207
- const importTarget = exportTarget.import;
208
- if (typeof importTarget === "string") {
209
- return path.resolve(packageDir, importTarget);
210
- }
211
- }
212
- throw new Error(`Missing import target for @ecopages/radiant export ${exportKey}`);
213
- }
214
- async getOrCreateRadiantEntryModulePath() {
215
- if (this.cachedRadiantEntryModulePath) {
216
- return this.cachedRadiantEntryModulePath;
217
- }
218
- const artifactsDir = this.getArtifactsDir();
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
- );
228
- const seenExports = /* @__PURE__ */ new Set();
229
- const statements = [
230
- `import '${this.getEntryImportPath(artifactsDir, installHydratorModulePath)}';`
231
- ];
232
- mkdirSync(artifactsDir, { recursive: true });
233
- for (const module of this.getRadiantBrowserRuntimeModules()) {
234
- const exportNames = getNamedExportNamesFromModuleSource(readFileSync(module.modulePath, "utf8")).filter((name) => !seenExports.has(name)).filter((name) => /^[$A-Z_a-z][$\w]*$/.test(name)).sort();
235
- if (exportNames.length === 0) {
236
- continue;
237
- }
238
- const entryImportPath = this.getEntryImportPath(artifactsDir, module.modulePath);
239
- statements.push(`export { ${exportNames.join(", ")} } from '${entryImportPath}';`);
240
- for (const exportName of exportNames) {
241
- seenExports.add(exportName);
242
- }
243
- }
244
- writeFileSync(filePath, statements.join("\n"), "utf8");
245
- this.cachedRadiantEntryModulePath = filePath;
246
- return filePath;
247
- }
248
- }
249
- export {
250
- JsxRuntimeBundleService
251
- };