@ecopages/core 0.2.0-alpha.53 → 0.2.0-alpha.55
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/README.md +2 -3
- package/package.json +34 -14
- package/src/adapters/bun/server-adapter.d.ts +7 -0
- package/src/adapters/bun/server-adapter.js +8 -3
- package/src/adapters/node/server-adapter.d.ts +1 -1
- package/src/adapters/node/server-adapter.js +2 -4
- package/src/build/README.md +57 -73
- package/src/build/browser-runtime-plugin-helpers.d.ts +26 -0
- package/src/build/browser-runtime-plugin-helpers.js +14 -0
- package/src/build/browser-runtime-plugin.d.ts +78 -0
- package/src/build/{browser-runtime-import-rewrite-plugin.js → browser-runtime-plugin.js} +49 -42
- package/src/build/build-adapter.d.ts +350 -93
- package/src/build/build-adapter.js +61 -492
- package/src/build/build-manifest.js +3 -6
- package/src/build/build-types.d.ts +2 -2
- package/src/build/rolldown-build-adapter.d.ts +32 -0
- package/src/build/rolldown-build-adapter.js +260 -0
- package/src/build/rolldown-plugin-bridge.d.ts +50 -0
- package/src/build/rolldown-plugin-bridge.js +194 -0
- package/src/build/runtime-build-executor.d.ts +14 -7
- package/src/build/runtime-build-executor.js +8 -11
- package/src/build/runtime-build-output-normalizer.d.ts +3 -0
- package/src/build/runtime-build-output-normalizer.js +111 -0
- package/src/build/serialized-build-executor.d.ts +64 -0
- package/src/build/serialized-build-executor.js +63 -0
- package/src/build/server-side-css-shim-plugin.d.ts +41 -0
- package/src/build/server-side-css-shim-plugin.js +35 -0
- package/src/cache/index.d.ts +6 -0
- package/src/cache/index.js +6 -0
- package/src/cache/module-parse-cache.d.ts +65 -0
- package/src/cache/module-parse-cache.js +75 -0
- package/src/config/README.md +1 -1
- package/src/config/config-builder.d.ts +3 -3
- package/src/config/config-builder.js +5 -14
- package/src/eco/eco.types.d.ts +2 -5
- package/src/hmr/strategies/js-hmr-strategy.d.ts +2 -2
- package/src/hmr/strategies/js-hmr-strategy.js +2 -2
- package/src/plugins/alias-resolver-cache.d.ts +68 -0
- package/src/plugins/alias-resolver-cache.js +106 -0
- package/src/plugins/alias-resolver-plugin.d.ts +4 -1
- package/src/plugins/alias-resolver-plugin.js +9 -5
- package/src/plugins/eco-component-meta-plugin.js +2 -2
- package/src/plugins/foreign-jsx-override-plugin.d.ts +1 -1
- package/src/route-renderer/orchestration/render-output.utils.d.ts +1 -1
- package/src/services/assets/browser-bundle.service.d.ts +1 -1
- package/src/services/module-loading/app-module-loader.service.d.ts +1 -1
- package/src/services/module-loading/app-server-module-transpiler.service.js +8 -29
- package/src/services/module-loading/page-module-import.service.js +2 -0
- package/src/types/internal-types.d.ts +1 -1
- package/src/build/browser-runtime-import-rewrite-plugin.d.ts +0 -26
- package/src/build/dev-build-coordinator.d.ts +0 -72
- package/src/build/dev-build-coordinator.js +0 -154
- package/src/build/esbuild-build-adapter.d.ts +0 -79
- package/src/build/esbuild-build-adapter.js +0 -521
- package/src/build/runtime-specifier-alias-plugin.d.ts +0 -15
- package/src/build/runtime-specifier-alias-plugin.js +0 -31
- package/src/services/module-loading/node-bootstrap-plugin.d.ts +0 -38
- package/src/services/module-loading/node-bootstrap-plugin.js +0 -215
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { rolldown } from "rolldown";
|
|
4
|
+
import { isDeclaredAppPackageImport, normalizeNodeRuntimeBuildOutputs } from "./runtime-build-output-normalizer.js";
|
|
5
|
+
import { collectBrowserRuntimeImportRewriteMap, rewriteBrowserRuntimeImports } from "./browser-runtime-plugin.js";
|
|
6
|
+
import { createRolldownPluginBridge } from "./rolldown-plugin-bridge.js";
|
|
7
|
+
import { createServerSideCssShimPlugin } from "./server-side-css-shim-plugin.js";
|
|
8
|
+
const moduleRequire = createRequire(import.meta.url);
|
|
9
|
+
const corePackageRequire = createRequire(new URL("../../package.json", import.meta.url));
|
|
10
|
+
function tryResolveModule(id, resolver) {
|
|
11
|
+
try {
|
|
12
|
+
return resolver.resolve(id);
|
|
13
|
+
} catch {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function transpileProfileToOptions(profile) {
|
|
18
|
+
switch (profile) {
|
|
19
|
+
case "browser-script":
|
|
20
|
+
case "hmr-runtime":
|
|
21
|
+
case "hmr-entrypoint":
|
|
22
|
+
return {
|
|
23
|
+
target: "browser",
|
|
24
|
+
format: "esm",
|
|
25
|
+
sourcemap: "none"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function mapRolldownFormat(value) {
|
|
30
|
+
switch (value) {
|
|
31
|
+
case "cjs":
|
|
32
|
+
return "cjs";
|
|
33
|
+
case "iife":
|
|
34
|
+
return "iife";
|
|
35
|
+
case "esm":
|
|
36
|
+
default:
|
|
37
|
+
return "esm";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function mapRolldownPlatform(value) {
|
|
41
|
+
if (value === "browser") return "browser";
|
|
42
|
+
if (value === "node") return "node";
|
|
43
|
+
if (value && /^(?:es\d+|chrome\d+|edge\d+|firefox\d+|safari\d+|hermes|deno\d+|ios\d+)$/.test(value)) {
|
|
44
|
+
return "node";
|
|
45
|
+
}
|
|
46
|
+
return "neutral";
|
|
47
|
+
}
|
|
48
|
+
function mapRolldownSourcemap(value) {
|
|
49
|
+
switch (value) {
|
|
50
|
+
case "none":
|
|
51
|
+
return false;
|
|
52
|
+
case "inline":
|
|
53
|
+
return "inline";
|
|
54
|
+
case "external":
|
|
55
|
+
case "linked":
|
|
56
|
+
return "hidden";
|
|
57
|
+
default:
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function mapRolldownJsx(jsx) {
|
|
62
|
+
const { factory, fragment, ...rest } = jsx;
|
|
63
|
+
const out = { ...rest };
|
|
64
|
+
if (factory !== void 0) {
|
|
65
|
+
out.pragma = factory;
|
|
66
|
+
}
|
|
67
|
+
if (fragment !== void 0) {
|
|
68
|
+
out.pragmaFrag = fragment;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
function toEntryFileNamesPattern(value) {
|
|
73
|
+
if (!value) {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
const literal = !/\[[^\]]+\]/u.test(value);
|
|
77
|
+
const stripped = value.replaceAll(/\.?\[ext\]/g, "");
|
|
78
|
+
if (stripped.length === 0) {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
return { pattern: stripped, literal };
|
|
82
|
+
}
|
|
83
|
+
function getJavaScriptOutExtension(options, literal) {
|
|
84
|
+
if (literal) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
if (options.target === "browser") {
|
|
88
|
+
return ".js";
|
|
89
|
+
}
|
|
90
|
+
if (options.format === "cjs") {
|
|
91
|
+
return ".cjs";
|
|
92
|
+
}
|
|
93
|
+
return ".mjs";
|
|
94
|
+
}
|
|
95
|
+
function normalizeOutputPath(outputPath, outdir) {
|
|
96
|
+
return path.isAbsolute(outputPath) ? path.normalize(outputPath) : path.normalize(path.join(outdir, outputPath));
|
|
97
|
+
}
|
|
98
|
+
function normalizeModulePath(modulePath, contextRoot) {
|
|
99
|
+
return path.isAbsolute(modulePath) ? path.normalize(modulePath) : path.normalize(path.resolve(contextRoot, modulePath));
|
|
100
|
+
}
|
|
101
|
+
function extractDependencyGraph(chunks, contextRoot) {
|
|
102
|
+
const entrypoints = {};
|
|
103
|
+
for (const chunk of chunks) {
|
|
104
|
+
if (!chunk.facadeModuleId) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const entrypointPath = normalizeModulePath(chunk.facadeModuleId, contextRoot);
|
|
108
|
+
entrypoints[entrypointPath] = Array.from(
|
|
109
|
+
/* @__PURE__ */ new Set([entrypointPath, ...chunk.moduleIds.map((id) => normalizeModulePath(id, contextRoot))])
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return { entrypoints };
|
|
113
|
+
}
|
|
114
|
+
function toBuildLogs(error) {
|
|
115
|
+
if (error instanceof Error) {
|
|
116
|
+
return [{ message: error.message }];
|
|
117
|
+
}
|
|
118
|
+
return [{ message: "Unknown build error" }];
|
|
119
|
+
}
|
|
120
|
+
function isPackageImport(id) {
|
|
121
|
+
return !id.startsWith(".") && !path.isAbsolute(id) && !id.startsWith("/") && !id.startsWith("node:") && !id.startsWith("@/") && !id.startsWith("~/") && !id.startsWith("#") && !id.includes(":");
|
|
122
|
+
}
|
|
123
|
+
function shouldBundlePackageImport(id, contextRoot) {
|
|
124
|
+
if (isDeclaredAppPackageImport(id, contextRoot)) {
|
|
125
|
+
const appRootRequire = createRequire(path.join(contextRoot, "package.json"));
|
|
126
|
+
const appResolvedPath = tryResolveModule(id, appRootRequire);
|
|
127
|
+
return Boolean(appResolvedPath && /\.(?:[cm]?ts|tsx|jsx)$/u.test(appResolvedPath));
|
|
128
|
+
}
|
|
129
|
+
const coreResolvedPath = tryResolveModule(id, corePackageRequire);
|
|
130
|
+
return Boolean(coreResolvedPath && /\.(?:[cm]?ts|tsx|jsx)$/u.test(coreResolvedPath));
|
|
131
|
+
}
|
|
132
|
+
function createExternalMatcher(options) {
|
|
133
|
+
const explicitExternals = new Set(options.external ?? []);
|
|
134
|
+
const externalPackages = options.externalPackages === true;
|
|
135
|
+
const contextRoot = options.root ? path.resolve(options.root) : process.cwd();
|
|
136
|
+
return (id) => {
|
|
137
|
+
if (explicitExternals.has(id)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
if (!externalPackages || !isPackageImport(id)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return !shouldBundlePackageImport(id, contextRoot);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function rewriteBrowserRuntimeImportsInOutputs(result, contextRoot, plugins) {
|
|
147
|
+
if (!result.success || result.outputs.length === 0) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
const specifierMap = collectBrowserRuntimeImportRewriteMap(plugins);
|
|
151
|
+
if (specifierMap.size === 0) {
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
const moduleRequireFromContext = createRequire(path.join(contextRoot, "package.json"));
|
|
155
|
+
for (const output of result.outputs) {
|
|
156
|
+
if (!/\.(?:[cm]?js)$/u.test(output.path)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const code = moduleRequireFromContext("node:fs").readFileSync(output.path, "utf-8");
|
|
160
|
+
const rewritten = rewriteBrowserRuntimeImports(code, specifierMap, output.path);
|
|
161
|
+
if (rewritten !== code) {
|
|
162
|
+
moduleRequireFromContext("node:fs").writeFileSync(output.path, rewritten);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
function rewriteNodeRuntimeImportsInOutputs(result, contextRoot) {
|
|
168
|
+
if (!result.success || result.outputs.length === 0) {
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
normalizeNodeRuntimeBuildOutputs(
|
|
172
|
+
result.outputs.map((output) => output.path),
|
|
173
|
+
contextRoot
|
|
174
|
+
);
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
class RolldownBuildAdapter {
|
|
178
|
+
ownership = "rolldown";
|
|
179
|
+
async buildOrThrow(options) {
|
|
180
|
+
const contextRoot = options.root ? path.resolve(options.root) : process.cwd();
|
|
181
|
+
const outdir = path.resolve(options.outdir ?? "dist/assets");
|
|
182
|
+
const plugins = options.plugins ?? [];
|
|
183
|
+
const external = createExternalMatcher(options);
|
|
184
|
+
const transformOptions = {};
|
|
185
|
+
if (options.define) {
|
|
186
|
+
transformOptions.define = options.define;
|
|
187
|
+
}
|
|
188
|
+
if (options.jsx) {
|
|
189
|
+
transformOptions.jsx = mapRolldownJsx(options.jsx);
|
|
190
|
+
}
|
|
191
|
+
if (options.target && /^(?:es|chrome|edge|firefox|safari|hermes|deno|ios)/.test(options.target)) {
|
|
192
|
+
transformOptions.target = options.target;
|
|
193
|
+
}
|
|
194
|
+
const bundle = await rolldown({
|
|
195
|
+
input: options.entrypoints,
|
|
196
|
+
cwd: contextRoot,
|
|
197
|
+
external,
|
|
198
|
+
platform: mapRolldownPlatform(options.target),
|
|
199
|
+
transform: Object.keys(transformOptions).length > 0 ? transformOptions : void 0,
|
|
200
|
+
resolve: options.conditions ? { conditionNames: options.conditions } : void 0,
|
|
201
|
+
treeshake: typeof options.treeshaking === "boolean" ? options.treeshaking : true,
|
|
202
|
+
plugins: [createServerSideCssShimPlugin(), ...createRolldownPluginBridge(plugins, contextRoot)]
|
|
203
|
+
});
|
|
204
|
+
const entryFileNames = toEntryFileNamesPattern(options.naming);
|
|
205
|
+
const jsExtension = getJavaScriptOutExtension(options, entryFileNames?.literal ?? false);
|
|
206
|
+
const finalEntryFileNames = entryFileNames ? jsExtension ? `${entryFileNames.pattern}${jsExtension}` : entryFileNames.pattern : jsExtension ? `[name]${jsExtension}` : "[name]";
|
|
207
|
+
const output = await bundle.write({
|
|
208
|
+
dir: outdir,
|
|
209
|
+
format: mapRolldownFormat(options.format),
|
|
210
|
+
minify: !!options.minify,
|
|
211
|
+
entryFileNames: finalEntryFileNames,
|
|
212
|
+
chunkFileNames: "[name]-[hash].js",
|
|
213
|
+
assetFileNames: "[name]-[hash][extname]",
|
|
214
|
+
sourcemap: mapRolldownSourcemap(options.sourcemap)
|
|
215
|
+
});
|
|
216
|
+
await bundle.close();
|
|
217
|
+
const outputs = output.output.map((entry) => ({ path: normalizeOutputPath(entry.fileName, outdir) }));
|
|
218
|
+
const entryChunks = output.output.filter(
|
|
219
|
+
(entry) => entry.type === "chunk" && entry.isEntry
|
|
220
|
+
);
|
|
221
|
+
const dependencyGraph = extractDependencyGraph(
|
|
222
|
+
entryChunks.map((chunk) => ({ facadeModuleId: chunk.facadeModuleId, moduleIds: chunk.moduleIds })),
|
|
223
|
+
contextRoot
|
|
224
|
+
);
|
|
225
|
+
const baseResult = {
|
|
226
|
+
success: true,
|
|
227
|
+
logs: [],
|
|
228
|
+
outputs,
|
|
229
|
+
dependencyGraph
|
|
230
|
+
};
|
|
231
|
+
return rewriteNodeRuntimeImportsInOutputs(
|
|
232
|
+
rewriteBrowserRuntimeImportsInOutputs(baseResult, contextRoot, plugins),
|
|
233
|
+
contextRoot
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
async build(options) {
|
|
237
|
+
try {
|
|
238
|
+
return await this.buildOrThrow(options);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
logs: toBuildLogs(error),
|
|
243
|
+
outputs: []
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
resolve(importPath, rootDir) {
|
|
248
|
+
return moduleRequire.resolve(importPath, { paths: [rootDir] });
|
|
249
|
+
}
|
|
250
|
+
getTranspileOptions(profile) {
|
|
251
|
+
return transpileProfileToOptions(profile);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function createRolldownBuildAdapter() {
|
|
255
|
+
return new RolldownBuildAdapter();
|
|
256
|
+
}
|
|
257
|
+
export {
|
|
258
|
+
RolldownBuildAdapter,
|
|
259
|
+
createRolldownBuildAdapter
|
|
260
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundler plugin bridge.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Translates an array of `EcoBuildPlugin` instances (the runtime-agnostic
|
|
6
|
+
* plugin contract used by Ecopages processors and integrations) into
|
|
7
|
+
* the bundler's native `Plugin` array. The bridge exposes the same
|
|
8
|
+
* three hooks (`onResolve`, `onLoad`, `module`) the `EcoBuildPlugin`
|
|
9
|
+
* contract uses, but maps them to the bundler's `resolveId`/`load`
|
|
10
|
+
* hooks.
|
|
11
|
+
*
|
|
12
|
+
* The shared `EcoBuildPlugin` contract stays the boundary between
|
|
13
|
+
* integrations and bundler backends.
|
|
14
|
+
*
|
|
15
|
+
* **Namespace handling.**
|
|
16
|
+
*
|
|
17
|
+
* The shared `EcoBuildPlugin` contract scopes `onResolve`/`onLoad`
|
|
18
|
+
* handlers with a `namespace` string. The bundler encodes namespaces
|
|
19
|
+
* into the module id (no separate field). The bridge prepends
|
|
20
|
+
* `<namespace>:` to the resolved id when the result includes a
|
|
21
|
+
* namespace, and matches `onLoad`/`onResolve` filters against that
|
|
22
|
+
* prefix. The bridge strips the prefix before forwarding the id back
|
|
23
|
+
* to the callback so plugin code keeps seeing the same `path` shape
|
|
24
|
+
* it did on the historical contract.
|
|
25
|
+
*
|
|
26
|
+
* **Plugin ordering is semantically significant.**
|
|
27
|
+
*
|
|
28
|
+
* The bundler's `resolveId` and `load` are "first" hooks: the first
|
|
29
|
+
* plugin that returns a non-null value wins. Because the bridge
|
|
30
|
+
* translates each `EcoBuildPlugin` into its own bundler plugin and
|
|
31
|
+
* preserves the array order, the position of each plugin in the
|
|
32
|
+
* `plugins` array determines its priority:
|
|
33
|
+
*
|
|
34
|
+
* - **Index 0** has the highest priority.
|
|
35
|
+
* - **Last index** has the lowest priority.
|
|
36
|
+
*
|
|
37
|
+
* When adding new integrations or processors, ensure security-critical
|
|
38
|
+
* plugins (e.g. `ecopages-client-graph-boundary`) are placed **before**
|
|
39
|
+
* general-purpose loaders in the array so they always get first refusal
|
|
40
|
+
* on every source file.
|
|
41
|
+
*/
|
|
42
|
+
import type { Plugin } from 'rolldown';
|
|
43
|
+
import type { EcoBuildPlugin } from './build-types.js';
|
|
44
|
+
/**
|
|
45
|
+
* Creates a Rolldown `Plugin` array that drives the supplied
|
|
46
|
+
* `EcoBuildPlugin` instances. Each `EcoBuildPlugin` becomes its own
|
|
47
|
+
* Rolldown plugin so that the order of registrations is preserved as
|
|
48
|
+
* plugin priority.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createRolldownPluginBridge(plugins: EcoBuildPlugin[], contextRoot: string): Plugin[];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { escapeRegExp } from "./browser-runtime-plugin-helpers.js";
|
|
3
|
+
const NAMESPACE_SEPARATOR = ":";
|
|
4
|
+
function joinNamespace(namespace, value) {
|
|
5
|
+
return namespace ? `${namespace}${NAMESPACE_SEPARATOR}${value}` : value;
|
|
6
|
+
}
|
|
7
|
+
function splitNamespace(id) {
|
|
8
|
+
const separatorIndex = id.indexOf(NAMESPACE_SEPARATOR);
|
|
9
|
+
if (separatorIndex <= 0) {
|
|
10
|
+
return { namespace: void 0, path: id };
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
namespace: id.slice(0, separatorIndex),
|
|
14
|
+
path: id.slice(separatorIndex + 1)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function inferRolldownModuleTypeFromPath(filePath) {
|
|
18
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
19
|
+
switch (extension) {
|
|
20
|
+
case ".ts":
|
|
21
|
+
return "ts";
|
|
22
|
+
case ".tsx":
|
|
23
|
+
return "tsx";
|
|
24
|
+
case ".jsx":
|
|
25
|
+
return "jsx";
|
|
26
|
+
case ".json":
|
|
27
|
+
return "json";
|
|
28
|
+
case ".css":
|
|
29
|
+
return "css";
|
|
30
|
+
default:
|
|
31
|
+
return "js";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function normalizeRolldownModuleType(loader) {
|
|
35
|
+
switch (loader) {
|
|
36
|
+
case "js":
|
|
37
|
+
case "jsx":
|
|
38
|
+
case "ts":
|
|
39
|
+
case "tsx":
|
|
40
|
+
case "json":
|
|
41
|
+
case "css":
|
|
42
|
+
case "text":
|
|
43
|
+
case "base64":
|
|
44
|
+
case "dataurl":
|
|
45
|
+
case "binary":
|
|
46
|
+
case "empty":
|
|
47
|
+
return loader;
|
|
48
|
+
case "global-css":
|
|
49
|
+
case "local-css":
|
|
50
|
+
return "css";
|
|
51
|
+
case "file":
|
|
52
|
+
case "copy":
|
|
53
|
+
return "asset";
|
|
54
|
+
default:
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function convertLoadResultToModuleSource(result) {
|
|
59
|
+
if (result.loader === "object" && result.exports && typeof result.exports === "object") {
|
|
60
|
+
return Object.entries(result.exports).map(
|
|
61
|
+
([key, value]) => key === "default" ? `export default ${JSON.stringify(value)};` : `export const ${key} = ${JSON.stringify(value)};`
|
|
62
|
+
).join("\n");
|
|
63
|
+
}
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
function buildIdFilter(filter, namespace) {
|
|
67
|
+
if (!namespace) {
|
|
68
|
+
return filter;
|
|
69
|
+
}
|
|
70
|
+
return new RegExp(`^${escapeRegExp(namespace)}${NAMESPACE_SEPARATOR}${filter.source.slice(1)}`);
|
|
71
|
+
}
|
|
72
|
+
function resolvePluginPath(value, importer, contextRoot) {
|
|
73
|
+
if (path.isAbsolute(value)) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
if (value.startsWith(".") || value.startsWith("..")) {
|
|
77
|
+
const baseDir = importer ? path.dirname(importer) : contextRoot;
|
|
78
|
+
return path.resolve(baseDir, value);
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function convertPluginOnLoadResult(args, result) {
|
|
83
|
+
if (!result || typeof result !== "object") {
|
|
84
|
+
return void 0;
|
|
85
|
+
}
|
|
86
|
+
const candidate = result;
|
|
87
|
+
const { path: sourcePath } = splitNamespace(args.id);
|
|
88
|
+
const sourceFromExports = convertLoadResultToModuleSource(candidate);
|
|
89
|
+
if (sourceFromExports) {
|
|
90
|
+
const description = {
|
|
91
|
+
code: sourceFromExports,
|
|
92
|
+
moduleType: "js"
|
|
93
|
+
};
|
|
94
|
+
return description;
|
|
95
|
+
}
|
|
96
|
+
if (typeof candidate.contents === "string" || candidate.contents instanceof Uint8Array) {
|
|
97
|
+
const code = typeof candidate.contents === "string" ? candidate.contents : new TextDecoder().decode(candidate.contents);
|
|
98
|
+
const description = {
|
|
99
|
+
code,
|
|
100
|
+
moduleType: normalizeRolldownModuleType(candidate.loader) ?? inferRolldownModuleTypeFromPath(sourcePath)
|
|
101
|
+
};
|
|
102
|
+
return description;
|
|
103
|
+
}
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
function convertPluginOnResolveResult(result, importer, contextRoot) {
|
|
107
|
+
if (!result || typeof result !== "object") {
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
const candidate = result;
|
|
111
|
+
if (typeof candidate.path !== "string" && typeof candidate.namespace !== "string") {
|
|
112
|
+
if (typeof candidate.external !== "boolean") {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const partial = { id: "" };
|
|
117
|
+
if (typeof candidate.path === "string") {
|
|
118
|
+
partial.id = joinNamespace(candidate.namespace, resolvePluginPath(candidate.path, importer, contextRoot));
|
|
119
|
+
} else if (typeof candidate.namespace === "string") {
|
|
120
|
+
partial.id = joinNamespace(candidate.namespace, "");
|
|
121
|
+
}
|
|
122
|
+
if (typeof candidate.external === "boolean") {
|
|
123
|
+
partial.external = candidate.external;
|
|
124
|
+
}
|
|
125
|
+
return partial;
|
|
126
|
+
}
|
|
127
|
+
function wrapEcoPlugin(ecoPlugin, contextRoot, moduleCounter) {
|
|
128
|
+
const resolveRegistrations = [];
|
|
129
|
+
const loadRegistrations = [];
|
|
130
|
+
const registerResolve = (filter, callback) => {
|
|
131
|
+
resolveRegistrations.push({ filter, callback });
|
|
132
|
+
};
|
|
133
|
+
const registerLoad = (filter, callback) => {
|
|
134
|
+
loadRegistrations.push({ filter, callback });
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
name: `ecopages-plugin-bridge:${ecoPlugin.name}`,
|
|
138
|
+
buildStart: async () => {
|
|
139
|
+
const bridge = {
|
|
140
|
+
onResolve: (options, callback) => {
|
|
141
|
+
registerResolve(buildIdFilter(options.filter, options.namespace), callback);
|
|
142
|
+
},
|
|
143
|
+
onLoad: (options, callback) => {
|
|
144
|
+
registerLoad(buildIdFilter(options.filter, options.namespace), callback);
|
|
145
|
+
},
|
|
146
|
+
module: (specifier, callback) => {
|
|
147
|
+
const namespace = `ecopages-module-${moduleCounter.value}`;
|
|
148
|
+
moduleCounter.value += 1;
|
|
149
|
+
const filter = new RegExp(`^${escapeRegExp(specifier)}$`);
|
|
150
|
+
registerResolve(filter, async () => ({
|
|
151
|
+
path: joinNamespace(namespace, specifier)
|
|
152
|
+
}));
|
|
153
|
+
registerLoad(buildIdFilter(/.*/, namespace), async () => callback());
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
await ecoPlugin.setup(bridge);
|
|
157
|
+
},
|
|
158
|
+
resolveId: async (source, importer, _extraOptions) => {
|
|
159
|
+
for (const { filter, callback } of resolveRegistrations) {
|
|
160
|
+
if (!filter.test(source)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const { namespace, path: sourcePath } = splitNamespace(source);
|
|
164
|
+
const result = await callback({ path: sourcePath, importer, namespace });
|
|
165
|
+
const converted = convertPluginOnResolveResult(result, importer, contextRoot);
|
|
166
|
+
if (converted !== void 0) {
|
|
167
|
+
return converted;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return void 0;
|
|
171
|
+
},
|
|
172
|
+
load: async (id) => {
|
|
173
|
+
for (const { filter, callback } of loadRegistrations) {
|
|
174
|
+
if (!filter.test(id)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const { namespace, path: sourcePath } = splitNamespace(id);
|
|
178
|
+
const result = await callback({ path: sourcePath, namespace });
|
|
179
|
+
const converted = convertPluginOnLoadResult({ id }, result);
|
|
180
|
+
if (converted !== void 0) {
|
|
181
|
+
return converted;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return void 0;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function createRolldownPluginBridge(plugins, contextRoot) {
|
|
189
|
+
const moduleCounter = { value: 0 };
|
|
190
|
+
return plugins.map((ecoPlugin) => wrapEcoPlugin(ecoPlugin, contextRoot, moduleCounter));
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
createRolldownPluginBridge
|
|
194
|
+
};
|
|
@@ -4,11 +4,18 @@ import { type BuildExecutor } from './build-adapter.js';
|
|
|
4
4
|
* Installs the app-owned runtime build executor for one app instance.
|
|
5
5
|
*
|
|
6
6
|
* @remarks
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Wraps the app-owned adapter in a {@link SerializedBuildExecutor} so
|
|
8
|
+
* every dev-watch caller issues builds against a single FIFO queue.
|
|
9
|
+
* Plugin injection is applied here via {@link withBuildExecutorPlugins}
|
|
10
|
+
* so app-owned plugins are merged into every rebuild without callers
|
|
11
|
+
* having to know about the manifest.
|
|
12
|
+
*
|
|
13
|
+
* Idempotent across calls: re-invoking replaces the existing executor
|
|
14
|
+
* on `appConfig.runtime` with a fresh wrapper.
|
|
15
|
+
*
|
|
16
|
+
* @param appConfig - The app config whose runtime state is updated.
|
|
17
|
+
* The function reads the existing executor (falling back to the
|
|
18
|
+
* app-owned adapter) and writes the wrapped executor back.
|
|
19
|
+
* @returns The installed {@link BuildExecutor}.
|
|
11
20
|
*/
|
|
12
|
-
export declare function installAppRuntimeBuildExecutor(appConfig: EcoPagesAppConfig
|
|
13
|
-
development: boolean;
|
|
14
|
-
}): BuildExecutor;
|
|
21
|
+
export declare function installAppRuntimeBuildExecutor(appConfig: EcoPagesAppConfig): BuildExecutor;
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAppBuildAdapter,
|
|
3
3
|
getAppBuildExecutor,
|
|
4
|
-
getAppBuildOwnership,
|
|
5
4
|
getAppServerBuildPlugins,
|
|
6
|
-
setAppBuildExecutor
|
|
5
|
+
setAppBuildExecutor,
|
|
6
|
+
withBuildExecutorPlugins
|
|
7
7
|
} from "./build-adapter.js";
|
|
8
|
-
import {
|
|
9
|
-
function installAppRuntimeBuildExecutor(appConfig
|
|
10
|
-
const
|
|
11
|
-
const buildExecutor =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
currentExecutor: getAppBuildExecutor(appConfig),
|
|
15
|
-
getPlugins: () => getAppServerBuildPlugins(appConfig)
|
|
16
|
-
}) : withBuildExecutorPlugins(getAppBuildAdapter(appConfig), () => getAppServerBuildPlugins(appConfig));
|
|
8
|
+
import { SerializedBuildExecutor } from "./serialized-build-executor.js";
|
|
9
|
+
function installAppRuntimeBuildExecutor(appConfig) {
|
|
10
|
+
const baseExecutor = getAppBuildExecutor(appConfig) ?? getAppBuildAdapter(appConfig);
|
|
11
|
+
const buildExecutor = new SerializedBuildExecutor(
|
|
12
|
+
withBuildExecutorPlugins(baseExecutor, () => getAppServerBuildPlugins(appConfig))
|
|
13
|
+
);
|
|
17
14
|
setAppBuildExecutor(appConfig, buildExecutor);
|
|
18
15
|
return buildExecutor;
|
|
19
16
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function isDeclaredAppPackageImport(specifier: string, rootDir: string): boolean;
|
|
2
|
+
export declare function normalizeNodeRuntimeBuildOutputFile(filePath: string, rootDir: string): void;
|
|
3
|
+
export declare function normalizeNodeRuntimeBuildOutputs(outputPaths: string[], rootDir: string): void;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
const corePackageRequire = createRequire(new URL("../../package.json", import.meta.url));
|
|
6
|
+
const appDeclaredPackageCache = /* @__PURE__ */ new Map();
|
|
7
|
+
function tryResolveRuntimeImport(specifier, resolver) {
|
|
8
|
+
try {
|
|
9
|
+
return resolver.resolve(specifier);
|
|
10
|
+
} catch {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function isBareRuntimeImport(specifier) {
|
|
15
|
+
return !specifier.startsWith(".") && !path.isAbsolute(specifier) && !specifier.startsWith("/") && !specifier.startsWith("node:") && !specifier.startsWith("@/") && !specifier.startsWith("~/") && !specifier.startsWith("#") && !specifier.includes(":");
|
|
16
|
+
}
|
|
17
|
+
function getPackageNameFromSpecifier(specifier) {
|
|
18
|
+
if (specifier.startsWith("@")) {
|
|
19
|
+
return specifier.split("/").slice(0, 2).join("/");
|
|
20
|
+
}
|
|
21
|
+
return specifier.split("/")[0] ?? specifier;
|
|
22
|
+
}
|
|
23
|
+
function getDeclaredAppPackages(rootDir) {
|
|
24
|
+
const cacheKey = path.resolve(rootDir);
|
|
25
|
+
const cached = appDeclaredPackageCache.get(cacheKey);
|
|
26
|
+
if (cached) {
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
const packageJsonPath = path.resolve(rootDir, "package.json");
|
|
30
|
+
const declaredPackages = /* @__PURE__ */ new Set();
|
|
31
|
+
try {
|
|
32
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
33
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
34
|
+
const entries = packageJson[field];
|
|
35
|
+
if (!entries || typeof entries !== "object") {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const packageName of Object.keys(entries)) {
|
|
39
|
+
declaredPackages.add(packageName);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
appDeclaredPackageCache.set(cacheKey, declaredPackages);
|
|
44
|
+
return declaredPackages;
|
|
45
|
+
}
|
|
46
|
+
appDeclaredPackageCache.set(cacheKey, declaredPackages);
|
|
47
|
+
return declaredPackages;
|
|
48
|
+
}
|
|
49
|
+
function isDeclaredAppPackageImport(specifier, rootDir) {
|
|
50
|
+
return getDeclaredAppPackages(rootDir).has(getPackageNameFromSpecifier(specifier));
|
|
51
|
+
}
|
|
52
|
+
function rewriteRuntimeImportSpecifier(specifier, quote, rootDir) {
|
|
53
|
+
if (!isBareRuntimeImport(specifier)) {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
if (isDeclaredAppPackageImport(specifier, rootDir)) {
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
const resolvedPath = tryResolveRuntimeImport(specifier, corePackageRequire);
|
|
60
|
+
if (!resolvedPath || !/\.(?:[cm]?[jt]s|tsx|jsx)$/u.test(resolvedPath)) {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
return `${quote}${pathToFileURL(resolvedPath).href}${quote}`;
|
|
64
|
+
}
|
|
65
|
+
function rewriteRuntimeBuildOutputImports(code, rootDir) {
|
|
66
|
+
const rewriteSpecifier = (prefix, quote, specifier) => {
|
|
67
|
+
const rewrittenSpecifier = rewriteRuntimeImportSpecifier(specifier, quote, rootDir);
|
|
68
|
+
return `${prefix}${rewrittenSpecifier ?? `${quote}${specifier}${quote}`}`;
|
|
69
|
+
};
|
|
70
|
+
const withResolvedPackages = code.replace(
|
|
71
|
+
/(\bfrom\s+)(['"])([^'"\\]+)\2/g,
|
|
72
|
+
(_match, prefix, quote, specifier) => rewriteSpecifier(prefix, quote, specifier)
|
|
73
|
+
).replace(
|
|
74
|
+
/(\bimport\s+)(['"])([^'"\\]+)\2/g,
|
|
75
|
+
(_match, prefix, quote, specifier) => rewriteSpecifier(prefix, quote, specifier)
|
|
76
|
+
).replace(
|
|
77
|
+
/(\bimport\s*\(\s*)(['"])([^'"\\]+)\2/g,
|
|
78
|
+
(_match, prefix, quote, specifier) => rewriteSpecifier(prefix, quote, specifier)
|
|
79
|
+
);
|
|
80
|
+
return withResolvedPackages.replace(
|
|
81
|
+
/(['"])(@oxc-project\/runtime\/(?:helpers(?:\/esm)?\/[^'"\\]+))\1/g,
|
|
82
|
+
(match, quote, specifier) => {
|
|
83
|
+
const resolvedPath = tryResolveRuntimeImport(specifier, corePackageRequire);
|
|
84
|
+
return resolvedPath ? `${quote}${pathToFileURL(resolvedPath).href}${quote}` : match;
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
function normalizeNodeRuntimeBuildOutputFile(filePath, rootDir) {
|
|
89
|
+
if (!/\.(?:[cm]?js)$/u.test(filePath)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const fileSystem = corePackageRequire("node:fs");
|
|
93
|
+
if (!fileSystem.existsSync(filePath)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const code = fileSystem.readFileSync(filePath, "utf-8");
|
|
97
|
+
const rewritten = rewriteRuntimeBuildOutputImports(code, rootDir);
|
|
98
|
+
if (rewritten !== code) {
|
|
99
|
+
fileSystem.writeFileSync(filePath, rewritten);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function normalizeNodeRuntimeBuildOutputs(outputPaths, rootDir) {
|
|
103
|
+
for (const outputPath of outputPaths) {
|
|
104
|
+
normalizeNodeRuntimeBuildOutputFile(outputPath, rootDir);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
isDeclaredAppPackageImport,
|
|
109
|
+
normalizeNodeRuntimeBuildOutputFile,
|
|
110
|
+
normalizeNodeRuntimeBuildOutputs
|
|
111
|
+
};
|