@ecopages/core 0.2.0-beta.7 → 0.2.0-beta.8
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/package.json +26 -2
- package/src/adapters/abstract/application-adapter.d.ts +12 -1
- package/src/adapters/abstract/application-adapter.js +13 -0
- package/src/adapters/abstract/server-adapter.d.ts +3 -0
- package/src/adapters/bun/client-bridge.d.ts +1 -0
- package/src/adapters/bun/client-bridge.js +3 -0
- package/src/adapters/bun/create-app.d.ts +2 -0
- package/src/adapters/bun/create-app.js +28 -9
- package/src/adapters/bun/server-adapter.d.ts +13 -0
- package/src/adapters/bun/server-adapter.js +29 -4
- package/src/adapters/create-app.d.ts +13 -0
- package/src/adapters/create-app.js +10 -1
- package/src/adapters/node/create-app.d.ts +1 -0
- package/src/adapters/node/create-app.js +17 -7
- package/src/adapters/node/node-client-bridge.d.ts +1 -0
- package/src/adapters/node/node-client-bridge.js +3 -0
- package/src/adapters/node/server-adapter.d.ts +10 -13
- package/src/adapters/node/server-adapter.js +37 -82
- package/src/adapters/node/static-content-server.d.ts +1 -0
- package/src/adapters/node/static-content-server.js +3 -0
- package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
- package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
- package/src/adapters/shared/runtime-app-bootstrap.d.ts +0 -1
- package/src/adapters/shared/runtime-app-bootstrap.js +1 -6
- package/src/adapters/shared/server-static-builder.d.ts +4 -6
- package/src/adapters/shared/server-static-builder.js +90 -34
- package/src/adapters/shared/shared-hmr-manager.d.ts +1 -0
- package/src/adapters/shared/shared-hmr-manager.js +15 -2
- package/src/build/README.md +44 -4
- package/src/build/build-adapter.d.ts +8 -2
- package/src/build/build-adapter.js +71 -33
- package/src/build/build-input-fingerprint.d.ts +28 -0
- package/src/build/build-input-fingerprint.js +31 -0
- package/src/build/jsx-ownership-plugins.d.ts +10 -0
- package/src/build/jsx-ownership-plugins.js +17 -0
- package/src/build/pages-unified-graph-build.d.ts +31 -0
- package/src/build/pages-unified-graph-build.js +208 -0
- package/src/build/parallel-build-executor.d.ts +21 -0
- package/src/build/parallel-build-executor.js +52 -0
- package/src/build/rolldown-adapter-helpers.js +5 -0
- package/src/build/rolldown-build-adapter.js +2 -0
- package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
- package/src/build/rolldown-build-invocation-metrics.js +43 -0
- package/src/build/rolldown-dev-build-adapter.d.ts +2 -2
- package/src/build/rolldown-dev-build-adapter.js +30 -14
- package/src/build/runtime-build-executor.d.ts +7 -13
- package/src/build/runtime-build-executor.js +31 -7
- package/src/build/server-entry-build-cache.d.ts +44 -0
- package/src/build/server-entry-build-cache.js +169 -0
- package/src/hmr/hmr.test.e2e.js +15 -4
- package/src/plugins/integration-plugin.d.ts +19 -8
- package/src/plugins/integration-plugin.js +7 -2
- package/src/plugins/processor.d.ts +5 -0
- package/src/plugins/processor.js +7 -0
- package/src/route-renderer/GRAPH.md +4 -4
- package/src/route-renderer/README.md +6 -5
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +1 -3
- package/src/route-renderer/orchestration/integration-renderer.d.ts +10 -2
- package/src/route-renderer/orchestration/integration-renderer.js +32 -12
- package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
- package/src/route-renderer/orchestration/render-output.utils.js +4 -0
- package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +2 -2
- package/src/route-renderer/orchestration/route-render-orchestrator.js +17 -16
- package/src/router/client/navigation-coordinator.js +10 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
- package/src/services/assets/browser-bundle.service.d.ts +1 -0
- package/src/services/assets/browser-bundle.service.js +9 -3
- package/src/services/html/html-transformer.service.d.ts +1 -1
- package/src/services/html/html-transformer.service.js +4 -4
- package/src/services/invalidation/development-invalidation.service.d.ts +10 -1
- package/src/services/invalidation/development-invalidation.service.js +29 -0
- package/src/services/module-loading/README.md +49 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +4 -17
- package/src/services/module-loading/page-module-import.service.d.ts +4 -1
- package/src/services/module-loading/page-module-import.service.js +52 -29
- package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
- package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
- package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
- package/src/services/module-loading/route-module-build-cache.js +38 -0
- package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
- package/src/services/module-loading/route-module-build-cache.store.js +203 -0
- package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
- package/src/services/module-loading/route-module-build-manifest.js +124 -0
- package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
- package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
- package/src/services/module-loading/server-module-transpiler.service.js +1 -1
- package/src/static-site-generator/README.md +33 -0
- package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
- package/src/static-site-generator/static-build-invalidation.js +78 -0
- package/src/static-site-generator/static-export-context.d.ts +22 -0
- package/src/static-site-generator/static-export-context.js +0 -0
- package/src/static-site-generator/static-site-generator.d.ts +17 -8
- package/src/static-site-generator/static-site-generator.js +176 -62
- package/src/types/internal-types.d.ts +8 -0
- package/src/utils/parse-cli-args.d.ts +1 -0
- package/src/utils/parse-cli-args.js +3 -0
- package/src/watchers/project-watcher.d.ts +8 -0
- package/src/watchers/project-watcher.js +32 -12
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { mergeBrowserRuntimeManifests } from "./browser-runtime-manifest.js";
|
|
2
2
|
import { normalizeNodeRuntimeBuildOutputs } from "./runtime-build-output-normalizer.js";
|
|
3
|
-
import { createForeignJsxOverridePlugin } from "../plugins/foreign-jsx-override-plugin.js";
|
|
4
3
|
import {
|
|
5
4
|
createAppBuildManifest,
|
|
6
5
|
getBrowserBuildPlugins,
|
|
7
6
|
getServerBuildPlugins
|
|
8
7
|
} from "./build-manifest.js";
|
|
8
|
+
import { getJsxOwnershipPlugins } from "./jsx-ownership-plugins.js";
|
|
9
9
|
import { createRolldownBuildAdapter } from "./rolldown-build-adapter.js";
|
|
10
10
|
import { createRolldownDevBuildAdapter } from "./rolldown-dev-build-adapter.js";
|
|
11
|
+
import { appLogger } from "../global/app-logger.js";
|
|
11
12
|
function mergeBuildExecutorPlugins(existing, appPlugins) {
|
|
12
13
|
if (!existing || existing.length === 0) {
|
|
13
14
|
return appPlugins;
|
|
@@ -96,7 +97,7 @@ function setAppBuildAdapter(appConfig, buildAdapter) {
|
|
|
96
97
|
}
|
|
97
98
|
function getAppBuildManifest(appConfig) {
|
|
98
99
|
return appConfig.runtime?.buildManifest ?? createAppBuildManifest({
|
|
99
|
-
loaderPlugins: Array.from(appConfig.loaders
|
|
100
|
+
loaderPlugins: Array.from(appConfig.loaders?.values() ?? [])
|
|
100
101
|
});
|
|
101
102
|
}
|
|
102
103
|
function setAppBuildManifest(appConfig, buildManifest) {
|
|
@@ -143,27 +144,55 @@ async function collectConfiguredAppBuildManifestContributions(appConfig) {
|
|
|
143
144
|
};
|
|
144
145
|
}
|
|
145
146
|
async function setupAppRuntimePlugins(options) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
if (options.appConfig.runtime?.runtimeAssetsPrepared) {
|
|
148
|
+
appLogger.debug("Skipped setupAppRuntimePlugins: runtime assets already prepared");
|
|
149
|
+
for (const loader of options.appConfig.loaders.values()) {
|
|
150
|
+
options.onRuntimePlugin?.(loader);
|
|
151
|
+
}
|
|
152
|
+
for (const processor of options.appConfig.processors.values()) {
|
|
153
|
+
if (processor.plugins) {
|
|
154
|
+
for (const plugin of processor.plugins) {
|
|
155
|
+
options.onRuntimePlugin?.(plugin);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const integration of options.appConfig.integrations) {
|
|
160
|
+
for (const plugin of integration.plugins) {
|
|
153
161
|
options.onRuntimePlugin?.(plugin);
|
|
154
162
|
}
|
|
155
163
|
}
|
|
164
|
+
return;
|
|
156
165
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
integration.setHmrManager(options.hmrManager);
|
|
166
|
+
appLogger.debugTime("setupAppRuntimePlugins");
|
|
167
|
+
try {
|
|
168
|
+
for (const loader of options.appConfig.loaders.values()) {
|
|
169
|
+
options.onRuntimePlugin?.(loader);
|
|
162
170
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
for (const processor of options.appConfig.processors.values()) {
|
|
172
|
+
await processor.setup();
|
|
173
|
+
if (processor.plugins) {
|
|
174
|
+
for (const plugin of processor.plugins) {
|
|
175
|
+
options.onRuntimePlugin?.(plugin);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
166
178
|
}
|
|
179
|
+
for (const integration of options.appConfig.integrations) {
|
|
180
|
+
integration.setConfig(options.appConfig);
|
|
181
|
+
integration.setRuntimeOrigin(options.runtimeOrigin);
|
|
182
|
+
if (options.hmrManager) {
|
|
183
|
+
integration.setHmrManager(options.hmrManager);
|
|
184
|
+
}
|
|
185
|
+
await integration.setup();
|
|
186
|
+
for (const plugin of integration.plugins) {
|
|
187
|
+
options.onRuntimePlugin?.(plugin);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
options.appConfig.runtime = {
|
|
191
|
+
...options.appConfig.runtime ?? {},
|
|
192
|
+
runtimeAssetsPrepared: true
|
|
193
|
+
};
|
|
194
|
+
} finally {
|
|
195
|
+
appLogger.debugTimeEnd("setupAppRuntimePlugins");
|
|
167
196
|
}
|
|
168
197
|
}
|
|
169
198
|
function getAppServerBuildPlugins(appConfig) {
|
|
@@ -171,24 +200,16 @@ function getAppServerBuildPlugins(appConfig) {
|
|
|
171
200
|
}
|
|
172
201
|
function getAppBrowserBuildPlugins(appConfig) {
|
|
173
202
|
const manifest = getAppBuildManifest(appConfig);
|
|
174
|
-
|
|
175
|
-
return [...getBrowserBuildPlugins(manifest), ...jsxOwnershipPlugins];
|
|
176
|
-
}
|
|
177
|
-
function collectJsxOwnershipPlugins(appConfig) {
|
|
178
|
-
const jsxExtensions = (appConfig.integrations ?? []).filter((integration) => integration.jsxImportSource).flatMap(
|
|
179
|
-
(integration) => integration.extensions.filter((extension) => extension.endsWith(".tsx") || extension.endsWith(".jsx")).map((extension) => ({ integration, extension }))
|
|
180
|
-
).sort((left, right) => right.extension.length - left.extension.length);
|
|
181
|
-
return jsxExtensions.map(
|
|
182
|
-
({ integration, extension }) => createForeignJsxOverridePlugin({
|
|
183
|
-
hostJsxImportSource: integration.jsxImportSource,
|
|
184
|
-
foreignExtensions: [extension],
|
|
185
|
-
excludeExtensions: jsxExtensions.filter((candidate) => candidate.extension.length > extension.length).filter((candidate) => candidate.extension.endsWith(extension)).map((candidate) => candidate.extension),
|
|
186
|
-
name: `ecopages-jsx-ownership-${integration.name}-${extension.replace(/[^a-zA-Z0-9]+/g, "-")}`
|
|
187
|
-
})
|
|
188
|
-
);
|
|
203
|
+
return [...getBrowserBuildPlugins(manifest), ...getJsxOwnershipPlugins(appConfig)];
|
|
189
204
|
}
|
|
190
205
|
function getAppBuildExecutor(appConfig) {
|
|
191
|
-
return appConfig.runtime?.buildExecutor ?? getAppBuildAdapter(appConfig);
|
|
206
|
+
return appConfig.runtime?.routeModuleBuildExecutor ?? appConfig.runtime?.buildExecutor ?? getAppBuildAdapter(appConfig);
|
|
207
|
+
}
|
|
208
|
+
function getAppHmrBuildExecutor(appConfig) {
|
|
209
|
+
return appConfig.runtime?.hmrBuildExecutor ?? getAppBuildExecutor(appConfig);
|
|
210
|
+
}
|
|
211
|
+
function getAppRouteModuleBuildExecutor(appConfig) {
|
|
212
|
+
return appConfig.runtime?.routeModuleBuildExecutor ?? getAppBuildExecutor(appConfig);
|
|
192
213
|
}
|
|
193
214
|
function setAppBuildExecutor(appConfig, buildExecutor) {
|
|
194
215
|
appConfig.runtime = {
|
|
@@ -196,6 +217,19 @@ function setAppBuildExecutor(appConfig, buildExecutor) {
|
|
|
196
217
|
buildExecutor
|
|
197
218
|
};
|
|
198
219
|
}
|
|
220
|
+
function setAppHmrBuildExecutor(appConfig, buildExecutor) {
|
|
221
|
+
appConfig.runtime = {
|
|
222
|
+
...appConfig.runtime ?? {},
|
|
223
|
+
hmrBuildExecutor: buildExecutor
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function setAppRouteModuleBuildExecutor(appConfig, buildExecutor) {
|
|
227
|
+
appConfig.runtime = {
|
|
228
|
+
...appConfig.runtime ?? {},
|
|
229
|
+
routeModuleBuildExecutor: buildExecutor,
|
|
230
|
+
buildExecutor
|
|
231
|
+
};
|
|
232
|
+
}
|
|
199
233
|
function build(options, executor = defaultRolldownBuildAdapter) {
|
|
200
234
|
return executor.build(options).then((result) => {
|
|
201
235
|
if (result.success) {
|
|
@@ -228,6 +262,8 @@ export {
|
|
|
228
262
|
getAppBuildExecutor,
|
|
229
263
|
getAppBuildManifest,
|
|
230
264
|
getAppBuildOwnership,
|
|
265
|
+
getAppHmrBuildExecutor,
|
|
266
|
+
getAppRouteModuleBuildExecutor,
|
|
231
267
|
getAppServerBuildPlugins,
|
|
232
268
|
getAppTranspileOptions,
|
|
233
269
|
getBuildAdapterOwnership,
|
|
@@ -237,6 +273,8 @@ export {
|
|
|
237
273
|
setAppBuildExecutor,
|
|
238
274
|
setAppBuildManifest,
|
|
239
275
|
setAppBuildOwnership,
|
|
276
|
+
setAppHmrBuildExecutor,
|
|
277
|
+
setAppRouteModuleBuildExecutor,
|
|
240
278
|
setupAppRuntimePlugins,
|
|
241
279
|
updateAppBuildManifest,
|
|
242
280
|
withBuildExecutorPlugins
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IntegrationPlugin } from '../plugins/integration-plugin.js';
|
|
2
|
+
import type { Processor } from '../plugins/processor.js';
|
|
3
|
+
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Optional contributor surface for build-time cache invalidation.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Processors and integrations may override {@link didChange} to report that
|
|
9
|
+
* their build inputs changed independently of route source files.
|
|
10
|
+
*/
|
|
11
|
+
export interface BuildInputChangeContributor {
|
|
12
|
+
/**
|
|
13
|
+
* Returns `true` when this contributor's build inputs changed since the last
|
|
14
|
+
* persisted incremental build metadata was written.
|
|
15
|
+
*/
|
|
16
|
+
didChange?(): boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Returns whether a processor or integration signals changed build inputs. */
|
|
19
|
+
export declare function didBuildInputContributorChange(contributor: BuildInputChangeContributor): boolean;
|
|
20
|
+
/** Hashes the app's eco.config.ts file when present. */
|
|
21
|
+
export declare function hashAppConfigFile(appConfig: EcoPagesAppConfig): string;
|
|
22
|
+
/** Fingerprints processor/integration build-input state for cache invalidation. */
|
|
23
|
+
export declare function createBuildInputsFingerprint(appConfig: EcoPagesAppConfig): string;
|
|
24
|
+
/** Returns true when any processor or integration reports changed build inputs. */
|
|
25
|
+
export declare function haveBuildInputsChanged(appConfig: EcoPagesAppConfig): boolean;
|
|
26
|
+
/** Collects every processor and integration that can invalidate incremental builds. */
|
|
27
|
+
export declare function collectBuildInputContributors(appConfig: EcoPagesAppConfig): BuildInputChangeContributor[];
|
|
28
|
+
export type { Processor, IntegrationPlugin };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { fileSystem } from "@ecopages/file-system";
|
|
2
|
+
function didBuildInputContributorChange(contributor) {
|
|
3
|
+
return contributor.didChange?.() === true;
|
|
4
|
+
}
|
|
5
|
+
function hashAppConfigFile(appConfig) {
|
|
6
|
+
const configPath = appConfig.absolutePaths?.config;
|
|
7
|
+
if (!configPath || !fileSystem.exists(configPath)) {
|
|
8
|
+
return "missing";
|
|
9
|
+
}
|
|
10
|
+
return fileSystem.hash(configPath);
|
|
11
|
+
}
|
|
12
|
+
function createBuildInputsFingerprint(appConfig) {
|
|
13
|
+
const changedContributors = [
|
|
14
|
+
...Array.from(appConfig.processors?.values() ?? []).filter((processor) => didBuildInputContributorChange(processor)).map((processor) => `processor:${processor.getName()}`),
|
|
15
|
+
...(appConfig.integrations ?? []).filter((integration) => didBuildInputContributorChange(integration)).map((integration) => `integration:${integration.name}`)
|
|
16
|
+
];
|
|
17
|
+
return changedContributors.length > 0 ? changedContributors.sort().join("|") : "stable";
|
|
18
|
+
}
|
|
19
|
+
function haveBuildInputsChanged(appConfig) {
|
|
20
|
+
return createBuildInputsFingerprint(appConfig) !== "stable";
|
|
21
|
+
}
|
|
22
|
+
function collectBuildInputContributors(appConfig) {
|
|
23
|
+
return [...Array.from(appConfig.processors?.values() ?? []), ...appConfig.integrations ?? []];
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
collectBuildInputContributors,
|
|
27
|
+
createBuildInputsFingerprint,
|
|
28
|
+
didBuildInputContributorChange,
|
|
29
|
+
hashAppConfigFile,
|
|
30
|
+
haveBuildInputsChanged
|
|
31
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EcoBuildPlugin } from './build-types.js';
|
|
2
|
+
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Foreign JSX override plugins for mixed-integration apps (Kita + React, etc.).
|
|
5
|
+
*
|
|
6
|
+
* Each plugin rewrites files for one `(integration, extension)` pair with a
|
|
7
|
+
* `@jsxImportSource` pragma so the bundler compiles against the owning runtime.
|
|
8
|
+
* Extensions are sorted longest-first so `.page.react.tsx` wins over `.tsx`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getJsxOwnershipPlugins(appConfig: EcoPagesAppConfig): EcoBuildPlugin[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createForeignJsxOverridePlugin } from "../plugins/foreign-jsx-override-plugin.js";
|
|
2
|
+
function getJsxOwnershipPlugins(appConfig) {
|
|
3
|
+
const jsxExtensions = (appConfig.integrations ?? []).filter((integration) => integration.jsxImportSource).flatMap(
|
|
4
|
+
(integration) => integration.extensions.filter((extension) => extension.endsWith(".tsx") || extension.endsWith(".jsx")).map((extension) => ({ integration, extension }))
|
|
5
|
+
).sort((left, right) => right.extension.length - left.extension.length);
|
|
6
|
+
return jsxExtensions.map(
|
|
7
|
+
({ integration, extension }) => createForeignJsxOverridePlugin({
|
|
8
|
+
hostJsxImportSource: integration.jsxImportSource,
|
|
9
|
+
foreignExtensions: [extension],
|
|
10
|
+
excludeExtensions: jsxExtensions.filter((candidate) => candidate.extension.length > extension.length).filter((candidate) => candidate.extension.endsWith(extension)).map((candidate) => candidate.extension),
|
|
11
|
+
name: `ecopages-jsx-ownership-${integration.name}-${extension.replace(/[^a-zA-Z0-9]+/g, "-")}`
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
getJsxOwnershipPlugins
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified pages Rolldown graph (Phase A build + Phase C import).
|
|
3
|
+
*
|
|
4
|
+
* Enabled by default in production builds. Opt out with
|
|
5
|
+
* `ECOPAGES_UNIFIED_PAGES_GRAPH=0`. Covers all static page template extensions
|
|
6
|
+
* registered on the app config.
|
|
7
|
+
*/
|
|
8
|
+
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
9
|
+
export declare const PAGES_UNIFIED_GRAPH_CACHE_DIR = ".server-pages-graph";
|
|
10
|
+
export declare const PAGES_UNIFIED_GRAPH_CACHE_FILENAME = ".build-cache.json";
|
|
11
|
+
export interface PagesUnifiedGraphCacheManifest {
|
|
12
|
+
invalidationVersion: string;
|
|
13
|
+
buildInputsFingerprint: string;
|
|
14
|
+
buildKey: string;
|
|
15
|
+
builtAt: number;
|
|
16
|
+
outputs: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function isPagesUnifiedGraphEnabled(): boolean;
|
|
19
|
+
/** True when the unified graph should build or serve prebuilt page modules. */
|
|
20
|
+
export declare function shouldBuildPagesUnifiedGraph(): boolean;
|
|
21
|
+
export declare function isPagesUnifiedGraphPage(filePath: string, appConfig: EcoPagesAppConfig): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Builds eligible static pages in one Rolldown invocation and seeds route-module disk cache.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ensurePagesUnifiedGraphBuilt(options: {
|
|
26
|
+
appConfig: EcoPagesAppConfig;
|
|
27
|
+
entryPaths: readonly string[];
|
|
28
|
+
outdir: string;
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}): Promise<PagesUnifiedGraphCacheManifest | undefined>;
|
|
31
|
+
export declare function importPagesUnifiedGraphModule<T>(appConfig: EcoPagesAppConfig, filePath: string): Promise<T | undefined>;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { fileSystem } from "@ecopages/file-system";
|
|
4
|
+
import { appLogger } from "../global/app-logger.js";
|
|
5
|
+
import { build, getAppRouteModuleBuildExecutor, getAppServerBuildPlugins } from "./build-adapter.js";
|
|
6
|
+
import { createBuildInputsFingerprint, hashAppConfigFile } from "./build-input-fingerprint.js";
|
|
7
|
+
import { getCorePackageVersion } from "../services/module-loading/route-module-build-manifest.js";
|
|
8
|
+
import { resolveInternalExecutionDir } from "../utils/resolve-work-dir.js";
|
|
9
|
+
import { getSharedRouteModuleBuildCache } from "../services/module-loading/route-module-build-cache-registry.js";
|
|
10
|
+
import { normalizeNodeRuntimeBuildOutputFile } from "./runtime-build-output-normalizer.js";
|
|
11
|
+
import { resolveRouteModuleDependencyPaths } from "../services/module-loading/route-module-dependency-hasher.js";
|
|
12
|
+
import { getJsxOwnershipPlugins } from "./jsx-ownership-plugins.js";
|
|
13
|
+
const PAGES_UNIFIED_GRAPH_CACHE_DIR = ".server-pages-graph";
|
|
14
|
+
const PAGES_UNIFIED_GRAPH_CACHE_FILENAME = ".build-cache.json";
|
|
15
|
+
function isPagesUnifiedGraphEnabled() {
|
|
16
|
+
if (process.env.ECOPAGES_UNIFIED_PAGES_GRAPH === "0") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (process.env.ECOPAGES_UNIFIED_PAGES_GRAPH === "1") {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return process.env.NODE_ENV === "production";
|
|
23
|
+
}
|
|
24
|
+
function shouldBuildPagesUnifiedGraph() {
|
|
25
|
+
return isPagesUnifiedGraphEnabled() && process.env.NODE_ENV === "production";
|
|
26
|
+
}
|
|
27
|
+
function isPagesUnifiedGraphPage(filePath, appConfig) {
|
|
28
|
+
const normalizedPath = path.normalize(filePath);
|
|
29
|
+
return appConfig.templatesExt.some((extension) => normalizedPath.endsWith(extension));
|
|
30
|
+
}
|
|
31
|
+
function getPagesUnifiedGraphCachePath(appConfig) {
|
|
32
|
+
return path.join(
|
|
33
|
+
resolveInternalExecutionDir(appConfig),
|
|
34
|
+
PAGES_UNIFIED_GRAPH_CACHE_DIR,
|
|
35
|
+
PAGES_UNIFIED_GRAPH_CACHE_FILENAME
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
function createPagesUnifiedGraphBuildKey(appConfig, outdir) {
|
|
39
|
+
const plugins = appConfig.runtime?.buildManifest ? getAppServerBuildPlugins(appConfig) : [];
|
|
40
|
+
const pluginNames = [...plugins, ...getJsxOwnershipPlugins(appConfig)].map((plugin) => plugin.name).sort().join("|");
|
|
41
|
+
const templateExtensions = [...appConfig.templatesExt].sort().join("|");
|
|
42
|
+
return [
|
|
43
|
+
path.resolve(outdir),
|
|
44
|
+
hashAppConfigFile(appConfig),
|
|
45
|
+
templateExtensions,
|
|
46
|
+
pluginNames,
|
|
47
|
+
"es2022",
|
|
48
|
+
"esm",
|
|
49
|
+
"external-packages"
|
|
50
|
+
].join("::");
|
|
51
|
+
}
|
|
52
|
+
function readPagesUnifiedGraphManifest(appConfig) {
|
|
53
|
+
const manifestPath = getPagesUnifiedGraphCachePath(appConfig);
|
|
54
|
+
if (!fileSystem.exists(manifestPath)) {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(fileSystem.readFileSync(manifestPath));
|
|
59
|
+
if (!parsed?.outputs) {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
} catch {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function writePagesUnifiedGraphManifest(appConfig, manifest) {
|
|
68
|
+
const manifestPath = getPagesUnifiedGraphCachePath(appConfig);
|
|
69
|
+
fileSystem.ensureDir(path.dirname(manifestPath));
|
|
70
|
+
fileSystem.write(manifestPath, `${JSON.stringify(manifest, null, " ")}
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
function createSafeGraphEntryKey(entryPath, rootDir) {
|
|
74
|
+
const relativePath = path.relative(rootDir, entryPath).replace(/\.[^.]+$/u, "");
|
|
75
|
+
const safeKey = relativePath.replace(/[^a-zA-Z0-9]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
76
|
+
return safeKey.length > 0 ? safeKey : "page";
|
|
77
|
+
}
|
|
78
|
+
function resolveOutputForEntrypoint(entryKey, buildResult) {
|
|
79
|
+
const outputs = buildResult.outputs.map((output) => output.path);
|
|
80
|
+
for (const outputPath of outputs) {
|
|
81
|
+
const outputBaseName = path.basename(outputPath);
|
|
82
|
+
if (outputBaseName.startsWith(`${entryKey}-`) && /\.(?:m?js)$/u.test(outputBaseName)) {
|
|
83
|
+
return outputPath;
|
|
84
|
+
}
|
|
85
|
+
if (outputBaseName === `${entryKey}.js` || outputBaseName === `${entryKey}.mjs`) {
|
|
86
|
+
return outputPath;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
function isManifestValidForEntries(manifest, appConfig, outdir, entryPaths) {
|
|
92
|
+
if (manifest.invalidationVersion !== getCorePackageVersion()) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (manifest.buildInputsFingerprint !== createBuildInputsFingerprint(appConfig)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (manifest.buildKey !== createPagesUnifiedGraphBuildKey(appConfig, outdir)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return entryPaths.every((entryPath) => {
|
|
102
|
+
const resolvedEntryPath = path.resolve(entryPath);
|
|
103
|
+
const outputPath = manifest.outputs[resolvedEntryPath];
|
|
104
|
+
return outputPath ? fileSystem.exists(outputPath) : false;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async function ensurePagesUnifiedGraphBuilt(options) {
|
|
108
|
+
if (!shouldBuildPagesUnifiedGraph()) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
const eligibleEntryPaths = options.entryPaths.map((entryPath) => path.resolve(entryPath)).filter((entryPath) => isPagesUnifiedGraphPage(entryPath, options.appConfig));
|
|
112
|
+
if (eligibleEntryPaths.length === 0) {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
const outdir = path.resolve(options.outdir);
|
|
116
|
+
const existingManifest = options.force ? void 0 : readPagesUnifiedGraphManifest(options.appConfig);
|
|
117
|
+
if (existingManifest && isManifestValidForEntries(existingManifest, options.appConfig, outdir, eligibleEntryPaths)) {
|
|
118
|
+
appLogger.debug("Reusing pages unified graph cache");
|
|
119
|
+
return existingManifest;
|
|
120
|
+
}
|
|
121
|
+
const plugins = options.appConfig.runtime?.buildManifest ? getAppServerBuildPlugins(options.appConfig) : [];
|
|
122
|
+
const mergedPlugins = [...plugins, ...getJsxOwnershipPlugins(options.appConfig)];
|
|
123
|
+
const routeModuleBuildCache = getSharedRouteModuleBuildCache(outdir, options.appConfig);
|
|
124
|
+
const entryRecord = {};
|
|
125
|
+
const entryKeysByPath = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const entryPath of eligibleEntryPaths) {
|
|
127
|
+
const entryKey = createSafeGraphEntryKey(entryPath, options.appConfig.rootDir);
|
|
128
|
+
entryRecord[entryKey] = entryPath;
|
|
129
|
+
entryKeysByPath.set(entryPath, entryKey);
|
|
130
|
+
}
|
|
131
|
+
appLogger.debugTime("pagesUnifiedGraphBuild");
|
|
132
|
+
const buildResult = await build(
|
|
133
|
+
{
|
|
134
|
+
entrypoints: entryRecord,
|
|
135
|
+
root: options.appConfig.rootDir,
|
|
136
|
+
outdir,
|
|
137
|
+
target: "es2022",
|
|
138
|
+
format: "esm",
|
|
139
|
+
sourcemap: "none",
|
|
140
|
+
splitting: true,
|
|
141
|
+
minify: false,
|
|
142
|
+
naming: "[name]-[hash].[ext]",
|
|
143
|
+
externalPackages: true,
|
|
144
|
+
plugins: mergedPlugins.length > 0 ? mergedPlugins : void 0
|
|
145
|
+
},
|
|
146
|
+
getAppRouteModuleBuildExecutor(options.appConfig)
|
|
147
|
+
);
|
|
148
|
+
appLogger.debugTimeEnd("pagesUnifiedGraphBuild");
|
|
149
|
+
if (!buildResult.success) {
|
|
150
|
+
const details = buildResult.logs.map((log) => log.message).join(" | ");
|
|
151
|
+
throw new Error(`Pages unified graph build failed: ${details}`);
|
|
152
|
+
}
|
|
153
|
+
const outputs = {};
|
|
154
|
+
for (const entryPath of eligibleEntryPaths) {
|
|
155
|
+
const fileHash = fileSystem.hash(entryPath);
|
|
156
|
+
const entryKey = entryKeysByPath.get(entryPath);
|
|
157
|
+
const compiledOutput = entryKey ? resolveOutputForEntrypoint(entryKey, buildResult) : void 0;
|
|
158
|
+
if (!compiledOutput) {
|
|
159
|
+
throw new Error(`Pages unified graph build produced no output for ${entryPath}`);
|
|
160
|
+
}
|
|
161
|
+
normalizeNodeRuntimeBuildOutputFile(compiledOutput, options.appConfig.rootDir);
|
|
162
|
+
outputs[entryPath] = compiledOutput;
|
|
163
|
+
routeModuleBuildCache.recordBuild({
|
|
164
|
+
filePath: entryPath,
|
|
165
|
+
rootDir: options.appConfig.rootDir,
|
|
166
|
+
outdir,
|
|
167
|
+
fileHash,
|
|
168
|
+
outputPath: compiledOutput,
|
|
169
|
+
dependencyModulePaths: resolveRouteModuleDependencyPaths(buildResult, entryPath, options.appConfig.rootDir),
|
|
170
|
+
externalPackages: true,
|
|
171
|
+
plugins: mergedPlugins
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const manifest = {
|
|
175
|
+
invalidationVersion: getCorePackageVersion(),
|
|
176
|
+
buildInputsFingerprint: createBuildInputsFingerprint(options.appConfig),
|
|
177
|
+
buildKey: createPagesUnifiedGraphBuildKey(options.appConfig, outdir),
|
|
178
|
+
builtAt: Date.now(),
|
|
179
|
+
outputs
|
|
180
|
+
};
|
|
181
|
+
writePagesUnifiedGraphManifest(options.appConfig, manifest);
|
|
182
|
+
appLogger.debug(`Built pages unified graph for ${eligibleEntryPaths.length} entries`);
|
|
183
|
+
return manifest;
|
|
184
|
+
}
|
|
185
|
+
async function importPagesUnifiedGraphModule(appConfig, filePath) {
|
|
186
|
+
const manifest = readPagesUnifiedGraphManifest(appConfig);
|
|
187
|
+
if (!manifest) {
|
|
188
|
+
return void 0;
|
|
189
|
+
}
|
|
190
|
+
const resolvedFilePath = path.resolve(filePath);
|
|
191
|
+
const outputPath = manifest.outputs[resolvedFilePath];
|
|
192
|
+
if (!outputPath || !fileSystem.exists(outputPath)) {
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
return await import(
|
|
196
|
+
/* @vite-ignore */
|
|
197
|
+
pathToFileURL(outputPath).href
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
PAGES_UNIFIED_GRAPH_CACHE_DIR,
|
|
202
|
+
PAGES_UNIFIED_GRAPH_CACHE_FILENAME,
|
|
203
|
+
ensurePagesUnifiedGraphBuilt,
|
|
204
|
+
importPagesUnifiedGraphModule,
|
|
205
|
+
isPagesUnifiedGraphEnabled,
|
|
206
|
+
isPagesUnifiedGraphPage,
|
|
207
|
+
shouldBuildPagesUnifiedGraph
|
|
208
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BuildExecutor, BuildOptions, BuildResult } from './build-adapter.js';
|
|
2
|
+
/**
|
|
3
|
+
* Concurrency-limited build executor wrapper.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Runs up to `limit` builds concurrently. Use for independent route-module
|
|
7
|
+
* and HMR browser builds where FIFO serialization is unnecessary.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ParallelBuildExecutor implements BuildExecutor {
|
|
10
|
+
private readonly inner;
|
|
11
|
+
private readonly limit;
|
|
12
|
+
private activeCount;
|
|
13
|
+
private queue;
|
|
14
|
+
constructor(inner: BuildExecutor, limit: number);
|
|
15
|
+
build(options: BuildOptions): Promise<BuildResult>;
|
|
16
|
+
private drainQueue;
|
|
17
|
+
unwrap(): BuildExecutor;
|
|
18
|
+
getConcurrencyLimitForTests(): number;
|
|
19
|
+
getActiveCountForTests(): number;
|
|
20
|
+
getQueuedCountForTests(): number;
|
|
21
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class ParallelBuildExecutor {
|
|
2
|
+
inner;
|
|
3
|
+
limit;
|
|
4
|
+
activeCount = 0;
|
|
5
|
+
queue = [];
|
|
6
|
+
constructor(inner, limit) {
|
|
7
|
+
this.inner = inner;
|
|
8
|
+
this.limit = Math.max(1, limit);
|
|
9
|
+
}
|
|
10
|
+
build(options) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
this.queue.push({
|
|
13
|
+
operation: () => this.inner.build(options),
|
|
14
|
+
resolve,
|
|
15
|
+
reject
|
|
16
|
+
});
|
|
17
|
+
this.drainQueue();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
drainQueue() {
|
|
21
|
+
while (this.activeCount < this.limit && this.queue.length > 0) {
|
|
22
|
+
const next = this.queue.shift();
|
|
23
|
+
if (!next) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.activeCount += 1;
|
|
27
|
+
void next.operation().then((result) => {
|
|
28
|
+
next.resolve(result);
|
|
29
|
+
}).catch((error) => {
|
|
30
|
+
next.reject(error);
|
|
31
|
+
}).finally(() => {
|
|
32
|
+
this.activeCount -= 1;
|
|
33
|
+
this.drainQueue();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
unwrap() {
|
|
38
|
+
return this.inner;
|
|
39
|
+
}
|
|
40
|
+
getConcurrencyLimitForTests() {
|
|
41
|
+
return this.limit;
|
|
42
|
+
}
|
|
43
|
+
getActiveCountForTests() {
|
|
44
|
+
return this.activeCount;
|
|
45
|
+
}
|
|
46
|
+
getQueuedCountForTests() {
|
|
47
|
+
return this.queue.length;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
ParallelBuildExecutor
|
|
52
|
+
};
|
|
@@ -211,6 +211,11 @@ function resolveRolldownOptions(options, contextRoot, outdir, appRootRequireCach
|
|
|
211
211
|
transform: Object.keys(transformOptions).length > 0 ? transformOptions : void 0,
|
|
212
212
|
resolve: options.conditions ? { conditionNames: options.conditions } : void 0,
|
|
213
213
|
treeshake: typeof options.treeshaking === "boolean" ? options.treeshaking : true,
|
|
214
|
+
...process.env.ECOPAGES_PROFILE_BUILD === "1" ? {} : {
|
|
215
|
+
checks: {
|
|
216
|
+
pluginTimings: false
|
|
217
|
+
}
|
|
218
|
+
},
|
|
214
219
|
experimental: {
|
|
215
220
|
nativeMagicString: true
|
|
216
221
|
},
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
toBuildLogs,
|
|
10
10
|
transpileProfileToOptions
|
|
11
11
|
} from "./rolldown-adapter-helpers.js";
|
|
12
|
+
import { recordRolldownBuildInvocation } from "./rolldown-build-invocation-metrics.js";
|
|
12
13
|
const moduleRequire = createRequire(import.meta.url);
|
|
13
14
|
class RolldownBuildAdapter {
|
|
14
15
|
ownership = "rolldown";
|
|
@@ -20,6 +21,7 @@ class RolldownBuildAdapter {
|
|
|
20
21
|
* entrypoints, use {@link RolldownDevBuildAdapter}.
|
|
21
22
|
*/
|
|
22
23
|
async buildOrThrow(options) {
|
|
24
|
+
recordRolldownBuildInvocation("rolldown");
|
|
23
25
|
const contextRoot = options.root ? path.resolve(options.root) : process.cwd();
|
|
24
26
|
const outdir = path.resolve(options.outdir ?? "dist/assets");
|
|
25
27
|
const plugins = options.plugins ?? [];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide Rolldown build invocation counters for benchmarks and spikes.
|
|
3
|
+
*
|
|
4
|
+
* Gated behind {@link isRolldownBuildMetricsEnabled} so production paths stay quiet.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isRolldownBuildMetricsEnabled(): boolean;
|
|
7
|
+
export declare function recordRolldownBuildInvocation(adapter?: 'rolldown' | 'rolldown-dev'): void;
|
|
8
|
+
export declare function getRolldownBuildInvocationCounts(): Readonly<Record<string, number>>;
|
|
9
|
+
export declare function resetRolldownBuildInvocationCounts(): void;
|
|
10
|
+
export declare function recordPageModuleBuildInvocation(): void;
|
|
11
|
+
export declare function getPageModuleRolldownBuildInvocations(): number;
|
|
12
|
+
export declare function getTotalRolldownBuildInvocations(): number;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const invocationCounts = /* @__PURE__ */ new Map();
|
|
2
|
+
let pageModuleBuildInvocations = 0;
|
|
3
|
+
function isRolldownBuildMetricsEnabled() {
|
|
4
|
+
return process.env.ECOPAGES_ROLLDOWN_BUILD_METRICS === "1" || process.env.ECOPAGES_LOGGER_DEBUG === "true";
|
|
5
|
+
}
|
|
6
|
+
function recordRolldownBuildInvocation(adapter = "rolldown") {
|
|
7
|
+
if (!isRolldownBuildMetricsEnabled()) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
invocationCounts.set(adapter, (invocationCounts.get(adapter) ?? 0) + 1);
|
|
11
|
+
}
|
|
12
|
+
function getRolldownBuildInvocationCounts() {
|
|
13
|
+
return Object.fromEntries(invocationCounts);
|
|
14
|
+
}
|
|
15
|
+
function resetRolldownBuildInvocationCounts() {
|
|
16
|
+
invocationCounts.clear();
|
|
17
|
+
pageModuleBuildInvocations = 0;
|
|
18
|
+
}
|
|
19
|
+
function recordPageModuleBuildInvocation() {
|
|
20
|
+
if (!isRolldownBuildMetricsEnabled()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
pageModuleBuildInvocations += 1;
|
|
24
|
+
}
|
|
25
|
+
function getPageModuleRolldownBuildInvocations() {
|
|
26
|
+
return pageModuleBuildInvocations;
|
|
27
|
+
}
|
|
28
|
+
function getTotalRolldownBuildInvocations() {
|
|
29
|
+
let total = 0;
|
|
30
|
+
for (const count of invocationCounts.values()) {
|
|
31
|
+
total += count;
|
|
32
|
+
}
|
|
33
|
+
return total;
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
getPageModuleRolldownBuildInvocations,
|
|
37
|
+
getRolldownBuildInvocationCounts,
|
|
38
|
+
getTotalRolldownBuildInvocations,
|
|
39
|
+
isRolldownBuildMetricsEnabled,
|
|
40
|
+
recordPageModuleBuildInvocation,
|
|
41
|
+
recordRolldownBuildInvocation,
|
|
42
|
+
resetRolldownBuildInvocationCounts
|
|
43
|
+
};
|
|
@@ -39,8 +39,8 @@ export declare class RolldownDevBuildAdapter implements BuildAdapter {
|
|
|
39
39
|
private readonly appRootRequireCache;
|
|
40
40
|
/**
|
|
41
41
|
* Stable key over the inputs that affect the engine configuration.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* Output paths and entrypoints must participate — HMR registers many
|
|
43
|
+
* distinct script entrypoints with literal naming under the same outdir.
|
|
44
44
|
*/
|
|
45
45
|
private getCacheKey;
|
|
46
46
|
/**
|