@ecopages/core 0.2.0-beta.38 → 0.2.0-beta.39
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 +1 -0
- package/package.json +27 -3
- package/src/adapters/bun/server-adapter.js +1 -0
- package/src/adapters/node/server-adapter.js +1 -0
- package/src/adapters/shared/http/explicit-static-render-preparation.js +1 -1
- package/src/adapters/shared/http/explicit-static-route-matcher.js +1 -1
- package/src/adapters/shared/http/fs-server-response-matcher.js +15 -7
- package/src/adapters/shared/runtime/collect-dev-prewarm-plan.d.ts +11 -0
- package/src/adapters/shared/runtime/collect-dev-prewarm-plan.js +29 -0
- package/src/adapters/shared/runtime/dev-static-route-prewarm.d.ts +16 -0
- package/src/adapters/shared/runtime/dev-static-route-prewarm.js +66 -0
- package/src/adapters/shared/runtime/render-context.js +1 -1
- package/src/adapters/shared/runtime/server-adapter.d.ts +7 -1
- package/src/adapters/shared/runtime/server-adapter.js +64 -5
- package/src/build/README.md +1 -1
- package/src/build/browser/browser-runtime-plugin.js +2 -5
- package/src/build/cache/production-build-cache.d.ts +3 -2
- package/src/build/cache/production-build-cache.js +2 -2
- package/src/build/contracts/content-virtual-modules.d.ts +12 -0
- package/src/build/contracts/content-virtual-modules.js +29 -0
- package/src/build/rolldown/rolldown-adapter-helpers.js +1 -1
- package/src/build/runtime/build-request-policy.d.ts +3 -4
- package/src/build/runtime/build-request-policy.js +4 -4
- package/src/cache/index.d.ts +2 -1
- package/src/cache/index.js +2 -1
- package/src/cache/module-parse-cache.d.ts +5 -0
- package/src/cache/module-parse-cache.js +27 -3
- package/src/cache/module-transform-profiler.d.ts +8 -0
- package/src/cache/module-transform-profiler.js +8 -0
- package/src/client/view-transitions.d.ts +17 -1
- package/src/client/view-transitions.js +45 -1
- package/src/client/view-transitions.test.browser.d.ts +1 -0
- package/src/client/view-transitions.test.browser.js +56 -0
- package/src/config/config-builder.d.ts +16 -9
- package/src/config/config-builder.js +24 -14
- package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +1 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.js +15 -2
- package/src/diagnostics/request-pipeline-metrics.d.ts +38 -0
- package/src/diagnostics/request-pipeline-metrics.js +121 -0
- package/src/eco/component-identity.d.ts +13 -0
- package/src/eco/component-identity.js +18 -0
- package/src/eco/eco-declared-component.d.ts +3 -2
- package/src/eco/eco-declared-component.js +6 -4
- package/src/eco/eco.browser.js +2 -2
- package/src/eco/eco.js +7 -6
- package/src/eco/eco.types.d.ts +6 -5
- package/src/errors/http-error.d.ts +8 -0
- package/src/errors/http-error.js +16 -0
- package/src/errors/undeclared-component-dependency-error.d.ts +1 -1
- package/src/errors/undeclared-component-dependency-error.js +2 -2
- package/src/index.browser.d.ts +1 -0
- package/src/index.browser.js +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/plugins/README.md +2 -1
- package/src/plugins/eco-component-meta-plugin.d.ts +6 -102
- package/src/plugins/eco-component-meta-plugin.js +110 -391
- package/src/plugins/processor.d.ts +10 -0
- package/src/plugins/processor.js +9 -0
- package/src/route-renderer/README.md +10 -8
- package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +1 -1
- package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +1 -1
- package/src/route-renderer/orchestration/integration-renderer.d.ts +0 -8
- package/src/route-renderer/orchestration/integration-renderer.js +6 -19
- package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +5 -4
- package/src/route-renderer/orchestration/ownership-graph/component-graph.js +6 -4
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.d.ts +0 -1
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +10 -19
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +1 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +12 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.d.ts +9 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +7 -0
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +65 -33
- package/src/route-renderer/page-loading/component-dependency-collection.js +3 -2
- package/src/route-renderer/page-loading/ecopages-virtual-imports.d.ts +0 -5
- package/src/route-renderer/page-loading/ecopages-virtual-imports.js +4 -10
- package/src/route-renderer/page-loading/file-scoped-dependency-components.d.ts +2 -2
- package/src/route-renderer/page-loading/file-scoped-dependency-components.js +12 -11
- package/src/route-renderer/page-loading/lazy-trigger-planning.js +2 -1
- package/src/route-renderer/page-loading/page-module-loader.d.ts +1 -1
- package/src/route-renderer/page-loading/page-module-loader.js +8 -3
- package/src/router/README.md +8 -0
- package/src/services/README.md +5 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.d.ts +1 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.js +22 -5
- package/src/services/cache/cache.types.d.ts +4 -2
- package/src/services/cache/html-page-cache-dependency-index.d.ts +34 -0
- package/src/services/cache/html-page-cache-dependency-index.js +88 -0
- package/src/services/cache/index.d.ts +2 -0
- package/src/services/cache/index.js +2 -0
- package/src/services/cache/page-cache-service.d.ts +31 -1
- package/src/services/cache/page-cache-service.js +79 -3
- package/src/services/cache/page-request-cache-coordinator.service.js +4 -0
- package/src/services/module-loading/README.md +9 -6
- package/src/services/module-loading/app-server-module-transpiler.service.js +0 -3
- package/src/services/module-loading/collection-server-module-build.service.d.ts +19 -0
- package/src/services/module-loading/collection-server-module-build.service.js +83 -0
- package/src/services/module-loading/page-module-import.service.d.ts +5 -11
- package/src/services/module-loading/page-module-import.service.js +56 -73
- package/src/services/module-loading/route-module-build-cache.store.js +6 -6
- package/src/services/module-loading/route-module-build-manifest.d.ts +8 -5
- package/src/services/module-loading/route-module-build-manifest.js +20 -32
- package/src/services/module-loading/server-module-transpiler.service.d.ts +0 -2
- package/src/services/module-loading/server-module-transpiler.service.js +0 -3
- package/src/static-site-generator/static-site-generator.js +2 -2
- package/src/types/internal-types.d.ts +4 -0
- package/src/types/public-types.d.ts +18 -18
- package/src/watchers/project-watcher.js +14 -0
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { build, type BuildExecutor, type BuildResult } from '../../build/build-adapter.js';
|
|
1
|
+
import { build, type BuildExecutor, type BuildOptions, type BuildResult } from '../../build/build-adapter.js';
|
|
2
2
|
import type { EcoBuildPlugin } from '../../build/contracts/build-types.js';
|
|
3
3
|
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
4
4
|
import type { SourceModuleLoaderFactory } from './module-loading-types.js';
|
|
5
5
|
interface PageModuleImportBaseOptions {
|
|
6
6
|
filePath: string;
|
|
7
7
|
bypassCache?: boolean;
|
|
8
|
-
cacheScope?: string;
|
|
9
|
-
invalidationVersion?: number;
|
|
10
8
|
}
|
|
11
9
|
/**
|
|
12
10
|
* Options for imports that must pass through the Ecopages build pipeline.
|
|
@@ -21,6 +19,7 @@ export interface PageModuleBuildImportOptions extends PageModuleImportBaseOption
|
|
|
21
19
|
buildExecutor?: BuildExecutor;
|
|
22
20
|
splitting?: boolean;
|
|
23
21
|
externalPackages?: boolean;
|
|
22
|
+
sourceTransforms?: BuildOptions['sourceTransforms'];
|
|
24
23
|
jsx?: {
|
|
25
24
|
development?: boolean;
|
|
26
25
|
factory?: string;
|
|
@@ -64,20 +63,15 @@ export declare class PageModuleImportService {
|
|
|
64
63
|
private readonly dependencies;
|
|
65
64
|
private readonly dependencyHasher;
|
|
66
65
|
private readonly importCache;
|
|
67
|
-
private
|
|
66
|
+
private developmentImportGeneration;
|
|
68
67
|
constructor(appConfig?: EcoPagesAppConfig, dependencies?: Partial<PageModuleImportDependencies>);
|
|
69
68
|
/**
|
|
70
69
|
* Clears the shared import cache used by framework-owned page-module loads.
|
|
71
70
|
*/
|
|
72
71
|
clearImportCache(): void;
|
|
73
72
|
/**
|
|
74
|
-
*
|
|
75
|
-
* import
|
|
76
|
-
*
|
|
77
|
-
* This forces all modules to be reloaded on the next import, even if their
|
|
78
|
-
* source content hasn't changed. This is necessary to ensure that changes to
|
|
79
|
-
* non-content aspects of modules (e.g. dependencies, transpilation output)
|
|
80
|
-
* are picked up during development.
|
|
73
|
+
* Clears in-memory import promises and dependency-hash memoization so the next
|
|
74
|
+
* import revalidates against current source and transform identity.
|
|
81
75
|
*/
|
|
82
76
|
invalidateDevelopmentGraph(): void;
|
|
83
77
|
/**
|
|
@@ -6,8 +6,8 @@ import { createServerBuildRequest } from '../../build/runtime/build-request-poli
|
|
|
6
6
|
import { resolveBuildProfileOptions } from '../../build/runtime/build-profile-options.js';
|
|
7
7
|
import { importPagesUnifiedGraphModule, isPagesUnifiedGraphPage, shouldBuildPagesUnifiedGraph, } from '../../build/cache/pages-unified-graph-build.js';
|
|
8
8
|
import { recordPageModuleBuildInvocation } from '../../build/rolldown/rolldown-build-invocation-metrics.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { recordPageModuleLoad } from '../../diagnostics/request-pipeline-metrics.js';
|
|
10
|
+
import { createRouteModuleReuseIdentity, resolvePageModuleOutputFileName } from './route-module-build-manifest.js';
|
|
11
11
|
import { getSharedRouteModuleBuildCache } from './route-module-build-cache-registry.js';
|
|
12
12
|
import { RouteModuleDependencyHasher, resolveRouteModuleDependencyPaths, } from './route-module-dependency-hasher.js';
|
|
13
13
|
import { supportsSourceModuleLoading } from './source-module-support.js';
|
|
@@ -28,7 +28,7 @@ export class PageModuleImportService {
|
|
|
28
28
|
dependencies;
|
|
29
29
|
dependencyHasher;
|
|
30
30
|
importCache = new Map();
|
|
31
|
-
|
|
31
|
+
developmentImportGeneration = 0;
|
|
32
32
|
constructor(appConfig, dependencies) {
|
|
33
33
|
this.appConfig = appConfig;
|
|
34
34
|
this.dependencies = {
|
|
@@ -49,18 +49,13 @@ export class PageModuleImportService {
|
|
|
49
49
|
this.importCache.clear();
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
* import
|
|
54
|
-
*
|
|
55
|
-
* This forces all modules to be reloaded on the next import, even if their
|
|
56
|
-
* source content hasn't changed. This is necessary to ensure that changes to
|
|
57
|
-
* non-content aspects of modules (e.g. dependencies, transpilation output)
|
|
58
|
-
* are picked up during development.
|
|
52
|
+
* Clears in-memory import promises and dependency-hash memoization so the next
|
|
53
|
+
* import revalidates against current source and transform identity.
|
|
59
54
|
*/
|
|
60
55
|
invalidateDevelopmentGraph() {
|
|
56
|
+
this.developmentImportGeneration += 1;
|
|
61
57
|
this.clearImportCache();
|
|
62
58
|
this.dependencyHasher.clearMemo();
|
|
63
|
-
this.developmentInvalidationVersion += 1;
|
|
64
59
|
}
|
|
65
60
|
/**
|
|
66
61
|
* Imports a page-like module from source.
|
|
@@ -76,8 +71,6 @@ export class PageModuleImportService {
|
|
|
76
71
|
*/
|
|
77
72
|
async importModule(options) {
|
|
78
73
|
const { filePath } = options;
|
|
79
|
-
const invalidationVersion = options.invalidationVersion ?? this.developmentInvalidationVersion;
|
|
80
|
-
const { externalPackages, splitting } = options;
|
|
81
74
|
const fileHash = this.dependencies.hashFile(filePath);
|
|
82
75
|
const hostModuleLoader = typeof Bun === 'undefined' &&
|
|
83
76
|
process.env.NODE_ENV === 'development' &&
|
|
@@ -85,63 +78,58 @@ export class PageModuleImportService {
|
|
|
85
78
|
? this.dependencies.getHostModuleLoader()
|
|
86
79
|
: undefined;
|
|
87
80
|
if (hostModuleLoader) {
|
|
88
|
-
const sourceModuleUrl = createRuntimeModuleUrl(filePath, fileHash,
|
|
89
|
-
|
|
81
|
+
const sourceModuleUrl = createRuntimeModuleUrl(filePath, fileHash, this.developmentImportGeneration);
|
|
82
|
+
const loadedModule = (await hostModuleLoader(sourceModuleUrl.href));
|
|
83
|
+
recordPageModuleLoad('host-loader');
|
|
84
|
+
return loadedModule;
|
|
90
85
|
}
|
|
91
|
-
if (options.bypassCache) {
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
if (!options.bypassCache) {
|
|
87
|
+
const runtime = typeof Bun !== 'undefined' ? 'bun' : 'node-build';
|
|
88
|
+
const cacheKey = [
|
|
89
|
+
runtime,
|
|
90
|
+
filePath,
|
|
91
|
+
createRouteModuleReuseIdentity(options, options.sourceTransforms),
|
|
92
|
+
fileHash,
|
|
93
|
+
].join('::');
|
|
94
|
+
const cachedModule = this.importCache.get(cacheKey);
|
|
95
|
+
if (cachedModule) {
|
|
96
|
+
if (!cachedModule.dependencyHashes) {
|
|
97
|
+
const loadedModule = (await cachedModule.promise);
|
|
98
|
+
recordPageModuleLoad('import-cache-hit');
|
|
99
|
+
return loadedModule;
|
|
100
|
+
}
|
|
101
|
+
if (this.dependencyHasher.matchesStoredHashes(cachedModule.dependencyHashes, filePath, fileHash)) {
|
|
102
|
+
const loadedModule = (await cachedModule.promise);
|
|
103
|
+
recordPageModuleLoad('import-cache-hit');
|
|
104
|
+
return loadedModule;
|
|
105
|
+
}
|
|
106
|
+
this.importCache.delete(cacheKey);
|
|
107
|
+
}
|
|
108
|
+
const importPromise = this.loadModule({
|
|
94
109
|
...options,
|
|
95
|
-
invalidationVersion: this.developmentInvalidationVersion,
|
|
96
110
|
fileHash,
|
|
111
|
+
importCacheKey: cacheKey,
|
|
97
112
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
runtime,
|
|
102
|
-
filePath,
|
|
103
|
-
options.rootDir,
|
|
104
|
-
path.resolve(options.outdir),
|
|
105
|
-
splitting ?? 'default',
|
|
106
|
-
externalPackages ?? 'default',
|
|
107
|
-
options.cacheScope ?? 'default',
|
|
108
|
-
createJsxCacheKey(options.jsx),
|
|
109
|
-
createPluginCacheKey(options.plugins),
|
|
110
|
-
fileHash,
|
|
111
|
-
invalidationVersion,
|
|
112
|
-
].join('::');
|
|
113
|
-
const cachedModule = this.importCache.get(cacheKey);
|
|
114
|
-
if (cachedModule) {
|
|
115
|
-
if (!cachedModule.dependencyHashes) {
|
|
116
|
-
return (await cachedModule.promise);
|
|
113
|
+
this.importCache.set(cacheKey, { promise: importPromise });
|
|
114
|
+
try {
|
|
115
|
+
return await importPromise;
|
|
117
116
|
}
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
catch (error) {
|
|
118
|
+
this.importCache.delete(cacheKey);
|
|
119
|
+
throw error;
|
|
120
120
|
}
|
|
121
|
-
this.importCache.delete(cacheKey);
|
|
122
121
|
}
|
|
123
|
-
|
|
122
|
+
return await this.loadModule({
|
|
124
123
|
...options,
|
|
125
124
|
fileHash,
|
|
126
|
-
importCacheKey: cacheKey,
|
|
127
125
|
});
|
|
128
|
-
this.importCache.set(cacheKey, { promise: importPromise });
|
|
129
|
-
try {
|
|
130
|
-
return await importPromise;
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
this.importCache.delete(cacheKey);
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
126
|
}
|
|
137
127
|
async loadModule(options) {
|
|
138
|
-
const { filePath,
|
|
128
|
+
const { filePath, fileHash, importCacheKey } = options;
|
|
139
129
|
const { rootDir, outdir, splitting, externalPackages, transpileErrorMessage = (details) => `Error transpiling page module: ${details}`, noOutputMessage = (targetFilePath) => `No transpiled output generated for page module: ${targetFilePath}`, } = options;
|
|
140
130
|
const outputFileName = resolvePageModuleOutputFileName({
|
|
141
131
|
filePath,
|
|
142
132
|
fileHash,
|
|
143
|
-
cacheScope,
|
|
144
|
-
invalidationVersion,
|
|
145
133
|
});
|
|
146
134
|
const outputNamingTemplate = outputFileName.replace(/\.mjs$/u, '.[ext]');
|
|
147
135
|
const preferredOutputPath = path.join(outdir, outputFileName);
|
|
@@ -178,24 +166,26 @@ export class PageModuleImportService {
|
|
|
178
166
|
externalPackages: buildOptions.externalPackages,
|
|
179
167
|
jsx: buildOptions.jsx,
|
|
180
168
|
plugins: buildOptions.plugins,
|
|
169
|
+
sourceTransforms: buildOptions.sourceTransforms,
|
|
181
170
|
fileHash,
|
|
182
171
|
};
|
|
183
172
|
const routeModuleBuildCache = this.getRouteModuleBuildCache(outdir);
|
|
184
173
|
const cachedBuild = routeModuleBuildCache.lookup(cacheBuildOptions);
|
|
185
174
|
if (cachedBuild) {
|
|
186
|
-
|
|
175
|
+
const loadedModule = (await import(/* @vite-ignore */ pathToFileURL(cachedBuild.outputPath).href));
|
|
176
|
+
recordPageModuleLoad('disk-cache-hit');
|
|
177
|
+
return loadedModule;
|
|
187
178
|
}
|
|
188
|
-
if (
|
|
189
|
-
shouldBuildPagesUnifiedGraph() &&
|
|
190
|
-
this.appConfig &&
|
|
191
|
-
isPagesUnifiedGraphPage(filePath, this.appConfig)) {
|
|
179
|
+
if (shouldBuildPagesUnifiedGraph() && this.appConfig && isPagesUnifiedGraphPage(filePath, this.appConfig)) {
|
|
192
180
|
const graphModule = await importPagesUnifiedGraphModule(this.appConfig, filePath);
|
|
193
181
|
if (graphModule !== undefined) {
|
|
182
|
+
recordPageModuleLoad('unified-graph');
|
|
194
183
|
return graphModule;
|
|
195
184
|
}
|
|
196
185
|
}
|
|
197
186
|
recordPageModuleBuildInvocation();
|
|
198
187
|
const buildResult = await this.dependencies.buildModule(buildOptions, options.buildExecutor);
|
|
188
|
+
recordPageModuleLoad('cold-build', buildResult.outputs.length);
|
|
199
189
|
if (!buildResult.success) {
|
|
200
190
|
const details = buildResult.logs.map((log) => log.message).join(' | ');
|
|
201
191
|
throw new Error(transpileErrorMessage(details));
|
|
@@ -220,10 +210,8 @@ export class PageModuleImportService {
|
|
|
220
210
|
dependencyModulePaths,
|
|
221
211
|
});
|
|
222
212
|
const compiledOutputUrl = pathToFileURL(compiledOutput);
|
|
223
|
-
if (shouldAddRuntimeUpdateQuery(
|
|
224
|
-
compiledOutputUrl.searchParams.set('update',
|
|
225
|
-
.filter((value) => value !== undefined)
|
|
226
|
-
.join('-'));
|
|
213
|
+
if (shouldAddRuntimeUpdateQuery()) {
|
|
214
|
+
compiledOutputUrl.searchParams.set('update', `${fileHash}-${this.developmentImportGeneration}`);
|
|
227
215
|
}
|
|
228
216
|
return (await import(/* @vite-ignore */ compiledOutputUrl.href));
|
|
229
217
|
}
|
|
@@ -231,18 +219,13 @@ export class PageModuleImportService {
|
|
|
231
219
|
return getSharedRouteModuleBuildCache(outdir, this.appConfig);
|
|
232
220
|
}
|
|
233
221
|
}
|
|
234
|
-
function createRuntimeModuleUrl(filePath, fileHash,
|
|
222
|
+
function createRuntimeModuleUrl(filePath, fileHash, developmentImportGeneration) {
|
|
235
223
|
const moduleUrl = pathToFileURL(filePath);
|
|
236
|
-
if (shouldAddRuntimeUpdateQuery(
|
|
237
|
-
moduleUrl.searchParams.set('update',
|
|
238
|
-
.filter((value) => value !== undefined)
|
|
239
|
-
.join('-'));
|
|
224
|
+
if (shouldAddRuntimeUpdateQuery()) {
|
|
225
|
+
moduleUrl.searchParams.set('update', `${fileHash}-${developmentImportGeneration}`);
|
|
240
226
|
}
|
|
241
227
|
return moduleUrl;
|
|
242
228
|
}
|
|
243
|
-
function shouldAddRuntimeUpdateQuery(
|
|
244
|
-
return process.env.NODE_ENV === 'development'
|
|
245
|
-
}
|
|
246
|
-
function sanitizeCacheScope(cacheScope) {
|
|
247
|
-
return cacheScope.replace(/[^a-zA-Z0-9_-]+/g, '-');
|
|
229
|
+
function shouldAddRuntimeUpdateQuery() {
|
|
230
|
+
return process.env.NODE_ENV === 'development';
|
|
248
231
|
}
|
|
@@ -28,10 +28,10 @@ export class RouteModuleBuildCache {
|
|
|
28
28
|
return undefined;
|
|
29
29
|
}
|
|
30
30
|
const manifest = this.loadManifest();
|
|
31
|
-
if (manifest.
|
|
31
|
+
if (manifest.corePackageVersion !== this.dependencies.getCorePackageVersion()) {
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
|
-
const buildKey = createPersistedRouteModuleBuildKey(options);
|
|
34
|
+
const buildKey = createPersistedRouteModuleBuildKey(options, options.sourceTransforms);
|
|
35
35
|
const cacheFilePath = normalizeRouteModuleCachePath(options.filePath);
|
|
36
36
|
const entry = manifest.entries[cacheFilePath];
|
|
37
37
|
if (!entry || entry.sourceHash !== options.fileHash || entry.buildKey !== buildKey) {
|
|
@@ -60,12 +60,12 @@ export class RouteModuleBuildCache {
|
|
|
60
60
|
dependencyHashes[cacheFilePath] = options.fileHash;
|
|
61
61
|
const manifest = this.loadManifest();
|
|
62
62
|
const existingEntry = manifest.entries[cacheFilePath];
|
|
63
|
-
manifest.
|
|
63
|
+
manifest.corePackageVersion = this.dependencies.getCorePackageVersion();
|
|
64
64
|
manifest.entries[cacheFilePath] = {
|
|
65
65
|
sourceHash: options.fileHash,
|
|
66
66
|
outputPath: options.outputPath,
|
|
67
67
|
builtAt: Date.now(),
|
|
68
|
-
buildKey: createPersistedRouteModuleBuildKey(options),
|
|
68
|
+
buildKey: createPersistedRouteModuleBuildKey(options, options.sourceTransforms),
|
|
69
69
|
dependencyHashes,
|
|
70
70
|
renderedOutputs: existingEntry?.renderedOutputs,
|
|
71
71
|
};
|
|
@@ -87,7 +87,7 @@ export class RouteModuleBuildCache {
|
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
const manifest = this.loadManifest();
|
|
90
|
-
manifest.
|
|
90
|
+
manifest.corePackageVersion = this.dependencies.getCorePackageVersion();
|
|
91
91
|
manifest.configHash = context.configHash;
|
|
92
92
|
manifest.buildInputsFingerprint = context.buildInputsFingerprint;
|
|
93
93
|
this.persistManifest(manifest);
|
|
@@ -140,7 +140,7 @@ export class RouteModuleBuildCache {
|
|
|
140
140
|
const cacheFilePath = normalizeRouteModuleCachePath(options.filePath);
|
|
141
141
|
const manifest = this.loadManifest();
|
|
142
142
|
const existingEntry = manifest.entries[cacheFilePath];
|
|
143
|
-
manifest.
|
|
143
|
+
manifest.corePackageVersion = this.dependencies.getCorePackageVersion();
|
|
144
144
|
manifest.configHash = options.context.configHash;
|
|
145
145
|
manifest.buildInputsFingerprint = options.context.buildInputsFingerprint;
|
|
146
146
|
manifest.entries[cacheFilePath] = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BuildOptions } from '../../build/contracts/build-contracts.js';
|
|
1
2
|
import type { PageModuleBuildImportOptions } from './page-module-import.service.js';
|
|
2
3
|
import type { RouteModuleDependencyHashes } from './route-module-dependency-hasher.js';
|
|
3
4
|
export { createJsxCacheKey, createPluginCacheKey, getCorePackageVersion, hashPluginSetup, } from '../../build/cache/cache-keys.js';
|
|
@@ -19,7 +20,7 @@ export interface RouteModuleStaticRenderCacheEntry {
|
|
|
19
20
|
}
|
|
20
21
|
/** On-disk manifest describing all cached route-module builds for one server outdir. */
|
|
21
22
|
export interface RouteModuleBuildCacheManifest {
|
|
22
|
-
|
|
23
|
+
corePackageVersion: string;
|
|
23
24
|
configHash?: string;
|
|
24
25
|
buildInputsFingerprint?: string;
|
|
25
26
|
entries: Record<string, RouteModuleBuildCacheEntry>;
|
|
@@ -38,17 +39,19 @@ export type RouteModuleBuildCacheManifestReader = (manifestPath: string) => Rout
|
|
|
38
39
|
export type RouteModuleBuildCacheManifestWriter = (manifestPath: string, manifest: RouteModuleBuildCacheManifest) => void;
|
|
39
40
|
/** Normalizes filesystem paths used as manifest keys and build-key inputs. */
|
|
40
41
|
export declare function normalizeRouteModuleCachePath(filePath: string): string;
|
|
41
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Returns whether route-module disk caching should run for the current import.
|
|
44
|
+
*/
|
|
42
45
|
export declare function shouldPersistRouteModuleBuildCache(options: PageModuleBuildImportOptions): boolean;
|
|
46
|
+
/** Canonical reuse identity for route-module memory and disk caches. */
|
|
47
|
+
export declare function createRouteModuleReuseIdentity(options: PageModuleBuildImportOptions, sourceTransforms?: BuildOptions['sourceTransforms']): string;
|
|
43
48
|
/** Derives the deterministic on-disk filename for one transpiled route module. */
|
|
44
49
|
export declare function resolvePageModuleOutputFileName(options: {
|
|
45
50
|
filePath: string;
|
|
46
51
|
fileHash: string;
|
|
47
|
-
cacheScope?: string;
|
|
48
|
-
invalidationVersion?: number;
|
|
49
52
|
}): string;
|
|
50
53
|
/** Builds the cache key for persisted production route-module builds. */
|
|
51
|
-
export declare function createPersistedRouteModuleBuildKey(options: PageModuleBuildImportOptions): string;
|
|
54
|
+
export declare function createPersistedRouteModuleBuildKey(options: PageModuleBuildImportOptions, sourceTransforms?: BuildOptions['sourceTransforms']): string;
|
|
52
55
|
export declare function createEmptyRouteModuleBuildCacheManifest(): RouteModuleBuildCacheManifest;
|
|
53
56
|
export declare function readRouteModuleBuildCacheManifest(manifestPath: string): RouteModuleBuildCacheManifest | undefined;
|
|
54
57
|
export declare function writeRouteModuleBuildCacheManifest(manifestPath: string, manifest: RouteModuleBuildCacheManifest): void;
|
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { readProductionCacheManifest, writeProductionCacheManifest } from '../../build/cache/production-build-cache.js';
|
|
3
|
-
import { createJsxCacheKey, createPluginCacheKey, getCorePackageVersion } from '../../build/cache/cache-keys.js';
|
|
3
|
+
import { createJsxCacheKey, createPluginCacheKey, createSourceTransformCacheKey, getCorePackageVersion, } from '../../build/cache/cache-keys.js';
|
|
4
4
|
export { createJsxCacheKey, createPluginCacheKey, getCorePackageVersion, hashPluginSetup, } from '../../build/cache/cache-keys.js';
|
|
5
5
|
export { ROUTE_MODULE_BUILD_CACHE_FILENAME } from '../../build/cache/cache-constants.js';
|
|
6
6
|
/** Normalizes filesystem paths used as manifest keys and build-key inputs. */
|
|
7
7
|
export function normalizeRouteModuleCachePath(filePath) {
|
|
8
8
|
return path.resolve(filePath);
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Returns whether route-module disk caching should run for the current import.
|
|
12
|
+
*/
|
|
11
13
|
export function shouldPersistRouteModuleBuildCache(options) {
|
|
12
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
14
|
+
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'development') {
|
|
13
15
|
return false;
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
if ((options.invalidationVersion ?? 0) > 0) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
if (options.cacheScope) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
/** Derives the deterministic on-disk filename for one transpiled route module. */
|
|
27
|
-
export function resolvePageModuleOutputFileName(options) {
|
|
28
|
-
const fileBaseName = path.basename(options.filePath, path.extname(options.filePath));
|
|
29
|
-
const cacheScopeSuffix = options.cacheScope ? `-${sanitizeCacheScope(options.cacheScope)}` : '';
|
|
30
|
-
const invalidationSuffix = shouldVersionBuildOutputPath(options.invalidationVersion ?? 0)
|
|
31
|
-
? `-v${options.invalidationVersion}`
|
|
32
|
-
: '';
|
|
33
|
-
return `${fileBaseName}-${options.fileHash}${cacheScopeSuffix}${invalidationSuffix}.mjs`;
|
|
17
|
+
return !options.bypassCache;
|
|
34
18
|
}
|
|
35
|
-
/**
|
|
36
|
-
export function
|
|
19
|
+
/** Canonical reuse identity for route-module memory and disk caches. */
|
|
20
|
+
export function createRouteModuleReuseIdentity(options, sourceTransforms) {
|
|
37
21
|
return [
|
|
38
22
|
path.resolve(options.rootDir),
|
|
39
23
|
path.resolve(options.outdir),
|
|
@@ -41,11 +25,21 @@ export function createPersistedRouteModuleBuildKey(options) {
|
|
|
41
25
|
options.externalPackages ?? 'default',
|
|
42
26
|
createJsxCacheKey(options.jsx),
|
|
43
27
|
createPluginCacheKey(options.plugins),
|
|
28
|
+
createSourceTransformCacheKey(sourceTransforms),
|
|
44
29
|
].join('::');
|
|
45
30
|
}
|
|
31
|
+
/** Derives the deterministic on-disk filename for one transpiled route module. */
|
|
32
|
+
export function resolvePageModuleOutputFileName(options) {
|
|
33
|
+
const fileBaseName = path.basename(options.filePath, path.extname(options.filePath));
|
|
34
|
+
return `${fileBaseName}-${options.fileHash}.mjs`;
|
|
35
|
+
}
|
|
36
|
+
/** Builds the cache key for persisted production route-module builds. */
|
|
37
|
+
export function createPersistedRouteModuleBuildKey(options, sourceTransforms) {
|
|
38
|
+
return createRouteModuleReuseIdentity(options, sourceTransforms);
|
|
39
|
+
}
|
|
46
40
|
export function createEmptyRouteModuleBuildCacheManifest() {
|
|
47
41
|
return {
|
|
48
|
-
|
|
42
|
+
corePackageVersion: getCorePackageVersion(),
|
|
49
43
|
entries: {},
|
|
50
44
|
};
|
|
51
45
|
}
|
|
@@ -55,7 +49,7 @@ export function readRouteModuleBuildCacheManifest(manifestPath) {
|
|
|
55
49
|
return undefined;
|
|
56
50
|
}
|
|
57
51
|
return {
|
|
58
|
-
|
|
52
|
+
corePackageVersion: parsed.corePackageVersion ?? parsed.invalidationVersion ?? '',
|
|
59
53
|
configHash: parsed.configHash,
|
|
60
54
|
buildInputsFingerprint: parsed.buildInputsFingerprint,
|
|
61
55
|
entries: parsed.entries,
|
|
@@ -64,9 +58,3 @@ export function readRouteModuleBuildCacheManifest(manifestPath) {
|
|
|
64
58
|
export function writeRouteModuleBuildCacheManifest(manifestPath, manifest) {
|
|
65
59
|
writeProductionCacheManifest(manifestPath, manifest);
|
|
66
60
|
}
|
|
67
|
-
function shouldVersionBuildOutputPath(invalidationVersion) {
|
|
68
|
-
return typeof Bun !== 'undefined' && invalidationVersion > 0;
|
|
69
|
-
}
|
|
70
|
-
function sanitizeCacheScope(cacheScope) {
|
|
71
|
-
return cacheScope.replace(/[^a-zA-Z0-9_-]+/g, '-');
|
|
72
|
-
}
|
|
@@ -18,7 +18,6 @@ export type ServerModuleTranspilerBootstrapArgs = {
|
|
|
18
18
|
getBuildExecutor?: () => BuildExecutor | undefined;
|
|
19
19
|
canLoadSourceModuleFromHost?: (filePath: string) => boolean;
|
|
20
20
|
getHostModuleLoader?: SourceModuleLoaderFactory;
|
|
21
|
-
getInvalidationVersion?: () => number;
|
|
22
21
|
invalidateModules?: (changedFiles?: string[]) => void;
|
|
23
22
|
pageModuleImportService?: ServerModuleImportDependency;
|
|
24
23
|
/** Factory evaluated on each importModule call to produce plugins applied to every load. */
|
|
@@ -35,7 +34,6 @@ export declare class ServerModuleTranspiler {
|
|
|
35
34
|
private readonly pageModuleImportService;
|
|
36
35
|
private readonly getRootDir;
|
|
37
36
|
private readonly getBuildExecutor;
|
|
38
|
-
private readonly getInvalidationVersion;
|
|
39
37
|
private readonly invalidateModules;
|
|
40
38
|
private readonly getDefaultPlugins;
|
|
41
39
|
/**
|
|
@@ -10,7 +10,6 @@ export class ServerModuleTranspiler {
|
|
|
10
10
|
pageModuleImportService;
|
|
11
11
|
getRootDir;
|
|
12
12
|
getBuildExecutor;
|
|
13
|
-
getInvalidationVersion;
|
|
14
13
|
invalidateModules;
|
|
15
14
|
getDefaultPlugins;
|
|
16
15
|
/**
|
|
@@ -26,7 +25,6 @@ export class ServerModuleTranspiler {
|
|
|
26
25
|
});
|
|
27
26
|
this.getRootDir = () => args.rootDir;
|
|
28
27
|
this.getBuildExecutor = args.getBuildExecutor ?? (() => undefined);
|
|
29
|
-
this.getInvalidationVersion = () => args.getInvalidationVersion?.();
|
|
30
28
|
this.getDefaultPlugins = args.getDefaultPlugins ?? (() => []);
|
|
31
29
|
this.invalidateModules = (changedFiles) => {
|
|
32
30
|
if (args.invalidateModules) {
|
|
@@ -48,7 +46,6 @@ export class ServerModuleTranspiler {
|
|
|
48
46
|
...(mergedPlugins.length > 0 ? { plugins: mergedPlugins } : {}),
|
|
49
47
|
rootDir: this.getRootDir(),
|
|
50
48
|
buildExecutor: this.getBuildExecutor(),
|
|
51
|
-
invalidationVersion: this.getInvalidationVersion(),
|
|
52
49
|
});
|
|
53
50
|
}
|
|
54
51
|
/**
|
|
@@ -15,7 +15,7 @@ import { normalizePathname } from '../utils/path-pattern.js';
|
|
|
15
15
|
export const STATIC_SITE_GENERATOR_ERRORS = {
|
|
16
16
|
ROUTE_RENDERER_FACTORY_REQUIRED: 'RouteRendererFactory is required for render strategy',
|
|
17
17
|
unsupportedBodyType: (bodyType) => `Unsupported body type for static generation: ${bodyType}`,
|
|
18
|
-
missingIntegration: (routePath) => `View at ${routePath} is missing
|
|
18
|
+
missingIntegration: (routePath) => `View at ${routePath} is missing component identity integration. Ensure it's defined with eco.page().`,
|
|
19
19
|
noRendererForIntegration: (integrationName) => `No renderer found for integration: ${integrationName}`,
|
|
20
20
|
dynamicRouteRequiresStaticPaths: (routePath) => `Dynamic route ${routePath} requires staticPaths to be defined on the view.`,
|
|
21
21
|
};
|
|
@@ -430,7 +430,7 @@ export class StaticSiteGenerator {
|
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
resolveExplicitViewSourceFile(view) {
|
|
433
|
-
const sourceFile = view.config?.
|
|
433
|
+
const sourceFile = view.config?.identity?.file;
|
|
434
434
|
if (!sourceFile) {
|
|
435
435
|
return undefined;
|
|
436
436
|
}
|
|
@@ -107,6 +107,10 @@ export type EcoPagesAppConfig = {
|
|
|
107
107
|
sitemap: SitemapConfig;
|
|
108
108
|
/** Additional paths to watch. Use this to monitor extra files. It is relative to the rootDir */
|
|
109
109
|
additionalWatchPaths: string[];
|
|
110
|
+
/** Static paths to SSR-prewarm in development before user navigation. */
|
|
111
|
+
devPrewarmPaths?: string[];
|
|
112
|
+
/** Static paths to SSR-prewarm before the development server reports ready. */
|
|
113
|
+
devPrewarmBeforeReadyPaths?: string[];
|
|
110
114
|
/**
|
|
111
115
|
* @default { title: 'Ecopages', description: 'Ecopages' }
|
|
112
116
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { ComponentIdentity } from '../eco/component-identity.js';
|
|
2
3
|
import type { ApiResponseBuilder } from '../adapters/shared/http/api-response.js';
|
|
3
4
|
import type { ForeignChildRuntime } from '../route-renderer/orchestration/foreign-child/component-render-context.js';
|
|
4
5
|
import type { EcoPageComponent } from '../eco/eco.types.js';
|
|
@@ -339,7 +340,7 @@ export type EcoComponentDependencies = {
|
|
|
339
340
|
/**
|
|
340
341
|
* Child components whose assets and foreign-child graph are collected transitively.
|
|
341
342
|
* Each entry must be an `eco.component()`, `eco.layout()`, or `eco.html()` result
|
|
342
|
-
* with
|
|
343
|
+
* with bound component identity.
|
|
343
344
|
*/
|
|
344
345
|
components?: EcoDeclaredComponent[];
|
|
345
346
|
};
|
|
@@ -347,8 +348,8 @@ export type EcoComponentDependencies = {
|
|
|
347
348
|
* Component returned from `eco.component()`, `eco.layout()`, or `eco.html()`.
|
|
348
349
|
*
|
|
349
350
|
* @remarks
|
|
350
|
-
* Used for `dependencies.components` and eco factory return types.
|
|
351
|
-
*
|
|
351
|
+
* Used for `dependencies.components` and eco factory return types. Bound
|
|
352
|
+
* component identity is enforced at runtime via `isEcoDeclaredComponent()`, not by this alias.
|
|
352
353
|
*/
|
|
353
354
|
export type EcoDeclaredComponent<P = any, R = any> = EcoComponent<P, R>;
|
|
354
355
|
export type EcoPagesElement = string | Promise<string>;
|
|
@@ -370,22 +371,9 @@ export type EcoChildren = string | Promise<string> | number | bigint | boolean |
|
|
|
370
371
|
* Represents the input configuration for EcoPages.
|
|
371
372
|
*/
|
|
372
373
|
export type EcoPagesConfig = Omit<Partial<EcoPagesAppConfig>, 'baseUrl' | 'derivedPaths' | 'templatesExt' | 'integrationsDependencies'> & Pick<EcoPagesAppConfig, 'baseUrl' | 'rootDir'>;
|
|
373
|
-
/**
|
|
374
|
-
* Internal metadata injected by eco-component-meta-plugin.
|
|
375
|
-
* Contains component file path and integration info for dependency resolution.
|
|
376
|
-
* @internal
|
|
377
|
-
*/
|
|
378
|
-
export interface EcoInjectedMeta {
|
|
379
|
-
/** Hashed identifier for client-side use (doesn't expose file paths) */
|
|
380
|
-
id: string;
|
|
381
|
-
/** Full file path of the component (use path.dirname() to get directory) */
|
|
382
|
-
file: string;
|
|
383
|
-
/** The integration identifier (e.g., 'react', 'kitajs', 'lit', 'ghtml', 'mdx') */
|
|
384
|
-
integration: string;
|
|
385
|
-
}
|
|
386
374
|
export type EcoComponentConfig = {
|
|
387
|
-
/**
|
|
388
|
-
|
|
375
|
+
/** Canonical integration, module, and stable component attribution. */
|
|
376
|
+
identity?: ComponentIdentity;
|
|
389
377
|
/**
|
|
390
378
|
* Explicit integration override for this component.
|
|
391
379
|
* When provided, this takes precedence over auto-detected integration metadata.
|
|
@@ -799,6 +787,14 @@ export type RouteRendererOptions = {
|
|
|
799
787
|
params?: PageParams;
|
|
800
788
|
query?: PageQuery;
|
|
801
789
|
locals?: RequestLocals;
|
|
790
|
+
/**
|
|
791
|
+
* Preloaded page module from request-time inspection.
|
|
792
|
+
*
|
|
793
|
+
* @remarks
|
|
794
|
+
* When present, render preparation reuses this module instead of importing the
|
|
795
|
+
* page file again for the same request.
|
|
796
|
+
*/
|
|
797
|
+
pageModule?: EcoPageFile;
|
|
802
798
|
/**
|
|
803
799
|
* Extra props merged into the page props for this render.
|
|
804
800
|
* @remarks Used by semantic error pages (for example development `message` /
|
|
@@ -817,6 +813,8 @@ export type RouteRenderResult = {
|
|
|
817
813
|
body: RouteRendererBody;
|
|
818
814
|
/** Cache strategy from page component's eco.page({ cache }) option */
|
|
819
815
|
cacheStrategy?: CacheStrategy;
|
|
816
|
+
/** Source paths observed during render preparation for HTML cache invalidation. */
|
|
817
|
+
sourceDependencyPaths?: readonly string[];
|
|
820
818
|
};
|
|
821
819
|
/**
|
|
822
820
|
* Represents the dependencies required for an integration plugin.
|
|
@@ -888,6 +886,8 @@ export type IntegrationRendererRenderOptions<C = EcoPagesElement> = RouteRendere
|
|
|
888
886
|
pageProps?: Record<string, unknown>;
|
|
889
887
|
cacheStrategy?: CacheStrategy;
|
|
890
888
|
pageLocals?: RequestLocals;
|
|
889
|
+
/** @internal Source paths from render preparation for HTML cache dependency registration. */
|
|
890
|
+
sourceDependencyPaths?: readonly string[];
|
|
891
891
|
};
|
|
892
892
|
/**
|
|
893
893
|
* Structured page asset package produced after dependency resolution.
|
|
@@ -4,6 +4,7 @@ import { fileSystem } from '@ecopages/file-system';
|
|
|
4
4
|
import { appLogger } from '../global/app-logger.js';
|
|
5
5
|
import { DevelopmentInvalidationService, } from '../services/invalidation/development-invalidation.service.js';
|
|
6
6
|
import { prepareHmrFileChange } from '../hmr/hmr-file-change-prep.js';
|
|
7
|
+
import { invalidateAppPageCacheBySourcePaths, clearAppPageCache } from '../services/cache/page-cache-service.js';
|
|
7
8
|
import { getAppPageBrowserGraphSession } from '../route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js';
|
|
8
9
|
import { isRegisteredDevTransformEntrypoint } from '../hmr/hmr-entrypoint-output.js';
|
|
9
10
|
import { resolveInternalExecutionDir } from '../utils/resolve-work-dir.js';
|
|
@@ -163,6 +164,12 @@ export class ProjectWatcher {
|
|
|
163
164
|
}
|
|
164
165
|
this.uncacheModules();
|
|
165
166
|
const resolvedFilePath = path.resolve(filePath);
|
|
167
|
+
if (shouldClearAllPageHtmlCache(plan.category)) {
|
|
168
|
+
await clearAppPageCache(this.appConfig);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
await invalidateAppPageCacheBySourcePaths(this.appConfig, [resolvedFilePath]);
|
|
172
|
+
}
|
|
166
173
|
const graphPreparation = this.hmrManager.isEnabled()
|
|
167
174
|
? prepareHmrFileChange(this.appConfig, resolvedFilePath)
|
|
168
175
|
: undefined;
|
|
@@ -404,3 +411,10 @@ export class ProjectWatcher {
|
|
|
404
411
|
await this.changeQueue.catch(() => undefined);
|
|
405
412
|
}
|
|
406
413
|
}
|
|
414
|
+
function shouldClearAllPageHtmlCache(category) {
|
|
415
|
+
return (category === 'other' ||
|
|
416
|
+
category === 'server-source' ||
|
|
417
|
+
category === 'additional-watch' ||
|
|
418
|
+
category === 'include-source' ||
|
|
419
|
+
category === 'explicit-server-view');
|
|
420
|
+
}
|