@ecopages/core 0.2.0-beta.6 → 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 -7
- package/src/adapters/shared/server-static-builder.js +89 -49
- 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 +47 -3
- 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/runtime-build-output-normalizer.d.ts +7 -0
- package/src/build/runtime-build-output-normalizer.js +85 -1
- 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 -20
- package/src/static-site-generator/static-site-generator.js +188 -85
- 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
|
@@ -188,8 +188,8 @@ class HtmlTransformerService {
|
|
|
188
188
|
* string-based fallback remains in place for runtimes that cannot provide one
|
|
189
189
|
* of those rewriter implementations.
|
|
190
190
|
*/
|
|
191
|
-
async transform(res, contributions = []) {
|
|
192
|
-
const { head, body } = this.groupDependenciesByPosition();
|
|
191
|
+
async transform(res, contributions = [], pagePackage) {
|
|
192
|
+
const { head, body } = this.groupDependenciesByPosition(pagePackage);
|
|
193
193
|
const { headPrepend, headAppend, bodyPrepend, bodyAppend } = this.groupContributionsByPlacement(contributions);
|
|
194
194
|
const htmlRewriter = await this.htmlRewriterProvider.createHtmlRewriter();
|
|
195
195
|
if (htmlRewriter) {
|
|
@@ -239,8 +239,8 @@ class HtmlTransformerService {
|
|
|
239
239
|
/**
|
|
240
240
|
* Splits processed assets into head and body injection groups.
|
|
241
241
|
*/
|
|
242
|
-
groupDependenciesByPosition() {
|
|
243
|
-
const dependencies = this.pagePackage ? this.resolvePagePackageHtmlDependencies(this.pagePackage) : this.processedDependencies;
|
|
242
|
+
groupDependenciesByPosition(pagePackageOverride) {
|
|
243
|
+
const dependencies = pagePackageOverride ? this.resolvePagePackageHtmlDependencies(pagePackageOverride) : this.pagePackage ? this.resolvePagePackageHtmlDependencies(this.pagePackage) : this.processedDependencies;
|
|
244
244
|
return dependencies.reduce(
|
|
245
245
|
(acc, dep) => {
|
|
246
246
|
if (dep.kind === "script") {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
2
|
-
export type DevelopmentInvalidationCategory = 'public-asset' | 'additional-watch' | 'include-source' | 'route-source' | 'processor-owned-asset' | 'server-source' | 'other';
|
|
2
|
+
export type DevelopmentInvalidationCategory = 'public-asset' | 'additional-watch' | 'include-source' | 'explicit-server-view' | 'route-source' | 'processor-owned-asset' | 'server-source' | 'other';
|
|
3
3
|
/**
|
|
4
4
|
* Framework-owned invalidation plan for one changed file.
|
|
5
5
|
*
|
|
@@ -62,6 +62,15 @@ export declare class DevelopmentInvalidationService {
|
|
|
62
62
|
* Returns whether the file is an include/template source file.
|
|
63
63
|
*/
|
|
64
64
|
isIncludeSourceFile(filePath: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Returns whether the file is an explicit server-rendered view module.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* These modules are typically registered through `renderServerModule` in
|
|
70
|
+
* `app.ts` rather than the filesystem router. They need a full browser reload
|
|
71
|
+
* because their HTML is produced on the server, not through client HMR entrypoints.
|
|
72
|
+
*/
|
|
73
|
+
isExplicitServerViewFile(filePath: string): boolean;
|
|
65
74
|
/**
|
|
66
75
|
* Returns whether the file is a server-executed source module outside the
|
|
67
76
|
* special route/include buckets.
|
|
@@ -60,6 +60,16 @@ class DevelopmentInvalidationService {
|
|
|
60
60
|
processorHandledAsset: false
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
if (this.isExplicitServerViewFile(filePath)) {
|
|
64
|
+
return {
|
|
65
|
+
category: "explicit-server-view",
|
|
66
|
+
invalidateServerModules: true,
|
|
67
|
+
refreshRoutes: false,
|
|
68
|
+
reloadBrowser: true,
|
|
69
|
+
delegateToHmr: false,
|
|
70
|
+
processorHandledAsset: false
|
|
71
|
+
};
|
|
72
|
+
}
|
|
63
73
|
if (this.isRouteSourceFile(filePath)) {
|
|
64
74
|
return {
|
|
65
75
|
category: "route-source",
|
|
@@ -148,6 +158,25 @@ class DevelopmentInvalidationService {
|
|
|
148
158
|
}
|
|
149
159
|
return /\.(?:[cm]?ts|[jt]sx?|mdx)$/u.test(resolvedPath);
|
|
150
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Returns whether the file is an explicit server-rendered view module.
|
|
163
|
+
*
|
|
164
|
+
* @remarks
|
|
165
|
+
* These modules are typically registered through `renderServerModule` in
|
|
166
|
+
* `app.ts` rather than the filesystem router. They need a full browser reload
|
|
167
|
+
* because their HTML is produced on the server, not through client HMR entrypoints.
|
|
168
|
+
*/
|
|
169
|
+
isExplicitServerViewFile(filePath) {
|
|
170
|
+
const resolvedPath = path.resolve(filePath);
|
|
171
|
+
const viewsDir = path.join(this.appConfig.absolutePaths.srcDir, "views");
|
|
172
|
+
if (resolvedPath !== viewsDir && !resolvedPath.startsWith(`${viewsDir}${path.sep}`)) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (this.appConfig.templatesExt.some((extension) => resolvedPath.endsWith(extension))) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return /\.(?:[cm]?ts|[jt]sx?|mdx)$/u.test(resolvedPath);
|
|
179
|
+
}
|
|
151
180
|
/**
|
|
152
181
|
* Returns whether the file is a server-executed source module outside the
|
|
153
182
|
* special route/include buckets.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Module Loading
|
|
2
|
+
|
|
3
|
+
Server-side source loading for framework-owned page modules, includes, and related templates.
|
|
4
|
+
|
|
5
|
+
## Import stack
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Call site (route scan, renderer, SSG, API)
|
|
9
|
+
└─ AppModuleLoader.importModule() app-server-module-transpiler.service.ts
|
|
10
|
+
├─ merges server build plugins + JSX ownership plugins
|
|
11
|
+
├─ resolves route-module BuildExecutor
|
|
12
|
+
└─ PageModuleImportService.importModule()
|
|
13
|
+
├─ dev + host loader → host runtime (Node only, when configured)
|
|
14
|
+
├─ production disk cache → route-module build cache
|
|
15
|
+
├─ unified pages graph → prebuilt chunk import
|
|
16
|
+
└─ Rolldown per-page build → dynamic import of transpiled output
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`ServerModuleTranspiler` is a thin wrapper that injects `rootDir`, optional default plugins, and invalidation version into the same `PageModuleImportService` path. App code should prefer `getAppModuleLoader(appConfig)` or `getAppServerModuleTranspiler(appConfig)`.
|
|
20
|
+
|
|
21
|
+
## Host vs app ownership
|
|
22
|
+
|
|
23
|
+
| Path | When |
|
|
24
|
+
| ----------------------- | -------------------------------------------------------------------------------------------- |
|
|
25
|
+
| Host loader | `appConfig.runtime.hostModuleLoader` set (Vite host) and file supports direct source loading |
|
|
26
|
+
| Ecopages build pipeline | Framework-owned template files under `pages/`, `includes/`, etc. |
|
|
27
|
+
|
|
28
|
+
`shouldAppUseHostModuleLoader()` encodes the directory + extension rules. Bun always uses the build pipeline today.
|
|
29
|
+
|
|
30
|
+
## Caching layers
|
|
31
|
+
|
|
32
|
+
1. **In-memory promise cache** (`PageModuleImportService.importCache`) — keyed by file path, resolved `outdir`, JSX/plugin inputs, file hash, and invalidation version. Cleared by `invalidateDevelopmentGraph()`.
|
|
33
|
+
2. **Disk transpile cache** (`.eco/.server-modules/.build-cache.json`) — production only; see route-module build cache slice.
|
|
34
|
+
3. **Unified graph manifest** — production static export fast path; see build layer docs.
|
|
35
|
+
|
|
36
|
+
## Files
|
|
37
|
+
|
|
38
|
+
| File | Role |
|
|
39
|
+
| ----------------------------------------- | --------------------------------------------------------- |
|
|
40
|
+
| `page-module-import.service.ts` | Core import/cache/build orchestration |
|
|
41
|
+
| `app-module-loader.service.ts` | App-facing loader interface |
|
|
42
|
+
| `app-server-module-transpiler.service.ts` | Factory for app-scoped loader + transpiler |
|
|
43
|
+
| `server-module-transpiler.service.ts` | Injectable transpiler boundary for tests and bootstrap |
|
|
44
|
+
| `host-module-loader-registry.ts` | Process-global host loader fallback for embedded runtimes |
|
|
45
|
+
| `source-module-support.ts` | Extension allowlist for host direct loading |
|
|
46
|
+
|
|
47
|
+
## Development invalidation
|
|
48
|
+
|
|
49
|
+
`DevelopmentInvalidationService.invalidateServerModules()` bumps the app-owned invalidation version and calls `appModuleLoader.invalidateDevelopmentGraph()`, which clears the in-memory import cache and increments the per-service invalidation counter used in cache keys and dev `?update=` query params.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { getAppRouteModuleBuildExecutor, getAppServerBuildPlugins } from "../../build/build-adapter.js";
|
|
2
|
+
import { getJsxOwnershipPlugins } from "../../build/jsx-ownership-plugins.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { DevelopmentInvalidationService } from "../invalidation/development-invalidation.service.js";
|
|
5
5
|
import {} from "./app-module-loader.service.js";
|
|
@@ -37,22 +37,9 @@ function setAppHostModuleLoader(appConfig, hostModuleLoader) {
|
|
|
37
37
|
hostModuleLoader
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
function getJsxOwnershipPlugins(appConfig) {
|
|
41
|
-
const jsxExtensions = (appConfig.integrations ?? []).filter((integration) => integration.jsxImportSource).flatMap(
|
|
42
|
-
(integration) => integration.extensions.filter((extension) => extension.endsWith(".tsx") || extension.endsWith(".jsx")).map((extension) => ({ integration, extension }))
|
|
43
|
-
).sort((left, right) => right.extension.length - left.extension.length);
|
|
44
|
-
return jsxExtensions.map(
|
|
45
|
-
({ integration, extension }) => createForeignJsxOverridePlugin({
|
|
46
|
-
hostJsxImportSource: integration.jsxImportSource,
|
|
47
|
-
foreignExtensions: [extension],
|
|
48
|
-
excludeExtensions: jsxExtensions.filter((candidate) => candidate.extension.length > extension.length).filter((candidate) => candidate.extension.endsWith(extension)).map((candidate) => candidate.extension),
|
|
49
|
-
name: `ecopages-jsx-ownership-${integration.name}-${extension.replace(/[^a-zA-Z0-9]+/g, "-")}`
|
|
50
|
-
})
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
40
|
function createAppModuleLoader(appConfig) {
|
|
54
41
|
const invalidationService = new DevelopmentInvalidationService(appConfig);
|
|
55
|
-
const pageModuleImportService = new PageModuleImportService({
|
|
42
|
+
const pageModuleImportService = new PageModuleImportService(appConfig, {
|
|
56
43
|
canLoadSourceModuleFromHost: (filePath) => shouldAppUseHostModuleLoader(appConfig, filePath),
|
|
57
44
|
getHostModuleLoader: () => getAppHostModuleLoader(appConfig)
|
|
58
45
|
});
|
|
@@ -69,7 +56,7 @@ function createAppModuleLoader(appConfig) {
|
|
|
69
56
|
return await pageModuleImportService.importModule({
|
|
70
57
|
...options,
|
|
71
58
|
...mergedPlugins.length > 0 ? { plugins: mergedPlugins } : {},
|
|
72
|
-
buildExecutor: options.buildExecutor ??
|
|
59
|
+
buildExecutor: options.buildExecutor ?? getAppRouteModuleBuildExecutor(appConfig),
|
|
73
60
|
invalidationVersion
|
|
74
61
|
});
|
|
75
62
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { build, type BuildExecutor, type BuildResult } from '../../build/build-adapter.js';
|
|
2
2
|
import type { EcoBuildPlugin } from '../../build/build-types.js';
|
|
3
|
+
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
3
4
|
import type { SourceModuleLoaderFactory } from './module-loading-types.js';
|
|
4
5
|
interface PageModuleImportBaseOptions {
|
|
5
6
|
filePath: string;
|
|
@@ -59,10 +60,11 @@ export interface PageModuleImportDependencies {
|
|
|
59
60
|
* transpilation settings, and error semantics across the different callers.
|
|
60
61
|
*/
|
|
61
62
|
export declare class PageModuleImportService {
|
|
63
|
+
private readonly appConfig?;
|
|
62
64
|
private readonly dependencies;
|
|
63
65
|
private readonly importCache;
|
|
64
66
|
private developmentInvalidationVersion;
|
|
65
|
-
constructor(dependencies?: Partial<PageModuleImportDependencies>);
|
|
67
|
+
constructor(appConfig?: EcoPagesAppConfig, dependencies?: Partial<PageModuleImportDependencies>);
|
|
66
68
|
/**
|
|
67
69
|
* Clears the shared import cache used by framework-owned page-module loads.
|
|
68
70
|
*/
|
|
@@ -91,5 +93,6 @@ export declare class PageModuleImportService {
|
|
|
91
93
|
*/
|
|
92
94
|
importModule<T = unknown>(options: PageModuleBuildImportOptions): Promise<T>;
|
|
93
95
|
private loadModule;
|
|
96
|
+
private getRouteModuleBuildCache;
|
|
94
97
|
}
|
|
95
98
|
export {};
|
|
@@ -3,12 +3,27 @@ import { pathToFileURL } from "node:url";
|
|
|
3
3
|
import { fileSystem } from "@ecopages/file-system";
|
|
4
4
|
import { build } from "../../build/build-adapter.js";
|
|
5
5
|
import { normalizeNodeRuntimeBuildOutputFile } from "../../build/runtime-build-output-normalizer.js";
|
|
6
|
+
import {
|
|
7
|
+
importPagesUnifiedGraphModule,
|
|
8
|
+
isPagesUnifiedGraphPage,
|
|
9
|
+
shouldBuildPagesUnifiedGraph
|
|
10
|
+
} from "../../build/pages-unified-graph-build.js";
|
|
11
|
+
import { recordPageModuleBuildInvocation } from "../../build/rolldown-build-invocation-metrics.js";
|
|
12
|
+
import {
|
|
13
|
+
resolvePageModuleOutputFileName,
|
|
14
|
+
createPluginCacheKey,
|
|
15
|
+
createJsxCacheKey
|
|
16
|
+
} from "./route-module-build-cache.js";
|
|
17
|
+
import { getSharedRouteModuleBuildCache } from "./route-module-build-cache-registry.js";
|
|
18
|
+
import { resolveRouteModuleDependencyPaths } from "./route-module-dependency-hasher.js";
|
|
6
19
|
import { supportsSourceModuleLoading } from "./source-module-support.js";
|
|
7
20
|
class PageModuleImportService {
|
|
21
|
+
appConfig;
|
|
8
22
|
dependencies;
|
|
9
23
|
importCache = /* @__PURE__ */ new Map();
|
|
10
24
|
developmentInvalidationVersion = 0;
|
|
11
|
-
constructor(dependencies) {
|
|
25
|
+
constructor(appConfig, dependencies) {
|
|
26
|
+
this.appConfig = appConfig;
|
|
12
27
|
this.dependencies = {
|
|
13
28
|
hashFile: dependencies?.hashFile ?? ((filePath) => fileSystem.hash(filePath)),
|
|
14
29
|
buildModule: dependencies?.buildModule ?? ((options, buildExecutor) => build(options, buildExecutor)),
|
|
@@ -70,6 +85,7 @@ class PageModuleImportService {
|
|
|
70
85
|
runtime,
|
|
71
86
|
filePath,
|
|
72
87
|
options.rootDir,
|
|
88
|
+
path.resolve(options.outdir),
|
|
73
89
|
splitting ?? "default",
|
|
74
90
|
externalPackages ?? "default",
|
|
75
91
|
options.cacheScope ?? "default",
|
|
@@ -104,11 +120,32 @@ class PageModuleImportService {
|
|
|
104
120
|
transpileErrorMessage = (details) => `Error transpiling page module: ${details}`,
|
|
105
121
|
noOutputMessage = (targetFilePath) => `No transpiled output generated for page module: ${targetFilePath}`
|
|
106
122
|
} = options;
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
const outputFileName = resolvePageModuleOutputFileName({
|
|
124
|
+
filePath,
|
|
125
|
+
fileHash,
|
|
126
|
+
cacheScope,
|
|
127
|
+
invalidationVersion
|
|
128
|
+
});
|
|
129
|
+
const outputNamingTemplate = outputFileName.replace(/\.mjs$/u, ".[ext]");
|
|
130
|
+
const preferredOutputPath = path.join(outdir, outputFileName);
|
|
131
|
+
const routeModuleBuildCache = this.getRouteModuleBuildCache(outdir);
|
|
132
|
+
const cachedBuild = routeModuleBuildCache.lookup({
|
|
133
|
+
...options,
|
|
134
|
+
fileHash
|
|
135
|
+
});
|
|
136
|
+
if (cachedBuild) {
|
|
137
|
+
return await import(
|
|
138
|
+
/* @vite-ignore */
|
|
139
|
+
pathToFileURL(cachedBuild.outputPath).href
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (!cacheScope && shouldBuildPagesUnifiedGraph() && this.appConfig && isPagesUnifiedGraphPage(filePath, this.appConfig)) {
|
|
143
|
+
const graphModule = await importPagesUnifiedGraphModule(this.appConfig, filePath);
|
|
144
|
+
if (graphModule !== void 0) {
|
|
145
|
+
return graphModule;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
recordPageModuleBuildInvocation();
|
|
112
149
|
const buildResult = await this.dependencies.buildModule(
|
|
113
150
|
{
|
|
114
151
|
entrypoints: [filePath],
|
|
@@ -131,12 +168,17 @@ class PageModuleImportService {
|
|
|
131
168
|
const details = buildResult.logs.map((log) => log.message).join(" | ");
|
|
132
169
|
throw new Error(transpileErrorMessage(details));
|
|
133
170
|
}
|
|
134
|
-
const preferredOutputPath = path.join(outdir, outputFileName);
|
|
135
171
|
const compiledOutput = buildResult.outputs.find((output) => output.path === preferredOutputPath)?.path ?? buildResult.outputs.find((output) => /\.(?:[cm]?js)$/u.test(output.path))?.path;
|
|
136
172
|
if (!compiledOutput) {
|
|
137
173
|
throw new Error(noOutputMessage(filePath));
|
|
138
174
|
}
|
|
139
175
|
normalizeNodeRuntimeBuildOutputFile(compiledOutput, rootDir);
|
|
176
|
+
routeModuleBuildCache.recordBuild({
|
|
177
|
+
...options,
|
|
178
|
+
fileHash,
|
|
179
|
+
outputPath: compiledOutput,
|
|
180
|
+
dependencyModulePaths: resolveRouteModuleDependencyPaths(buildResult, filePath, rootDir)
|
|
181
|
+
});
|
|
140
182
|
const compiledOutputUrl = pathToFileURL(compiledOutput);
|
|
141
183
|
if (shouldAddRuntimeUpdateQuery(invalidationVersion, cacheScope)) {
|
|
142
184
|
compiledOutputUrl.searchParams.set(
|
|
@@ -149,6 +191,9 @@ class PageModuleImportService {
|
|
|
149
191
|
compiledOutputUrl.href
|
|
150
192
|
);
|
|
151
193
|
}
|
|
194
|
+
getRouteModuleBuildCache(outdir) {
|
|
195
|
+
return getSharedRouteModuleBuildCache(outdir, this.appConfig);
|
|
196
|
+
}
|
|
152
197
|
}
|
|
153
198
|
function createRuntimeModuleUrl(filePath, fileHash, invalidationVersion, cacheScope) {
|
|
154
199
|
const moduleUrl = pathToFileURL(filePath);
|
|
@@ -163,31 +208,9 @@ function createRuntimeModuleUrl(filePath, fileHash, invalidationVersion, cacheSc
|
|
|
163
208
|
function shouldAddRuntimeUpdateQuery(invalidationVersion, cacheScope) {
|
|
164
209
|
return process.env.NODE_ENV === "development" || invalidationVersion > 0 || !!cacheScope;
|
|
165
210
|
}
|
|
166
|
-
function shouldVersionBuildOutputPath(invalidationVersion) {
|
|
167
|
-
return typeof Bun !== "undefined" && invalidationVersion > 0;
|
|
168
|
-
}
|
|
169
211
|
function sanitizeCacheScope(cacheScope) {
|
|
170
212
|
return cacheScope.replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
171
213
|
}
|
|
172
|
-
function createJsxCacheKey(jsx) {
|
|
173
|
-
if (!jsx) {
|
|
174
|
-
return "jsx:default";
|
|
175
|
-
}
|
|
176
|
-
return JSON.stringify({
|
|
177
|
-
development: jsx.development ?? false,
|
|
178
|
-
factory: jsx.factory ?? null,
|
|
179
|
-
fragment: jsx.fragment ?? null,
|
|
180
|
-
importSource: jsx.importSource ?? null,
|
|
181
|
-
runtime: jsx.runtime ?? null,
|
|
182
|
-
sideEffects: jsx.sideEffects ?? null
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
function createPluginCacheKey(plugins) {
|
|
186
|
-
if (!plugins || plugins.length === 0) {
|
|
187
|
-
return "plugins:default";
|
|
188
|
-
}
|
|
189
|
-
return `plugins:${plugins.map((plugin) => plugin.name).join(",")}`;
|
|
190
|
-
}
|
|
191
214
|
export {
|
|
192
215
|
PageModuleImportService
|
|
193
216
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
2
|
+
import { RouteModuleBuildCache } from './route-module-build-cache.store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the outdir whose transpile cache backs incremental static generation.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Static pages render through `.eco/.server-modules`. The manifest there carries
|
|
8
|
+
* import-graph hashes from transpile, so static-render reuse must read the same
|
|
9
|
+
* directory rather than `.server-route-modules`, which only serves route-registry
|
|
10
|
+
* scanning.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getServerModuleBuildCacheOutdir(appConfig: EcoPagesAppConfig): string;
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated Use {@link getServerModuleBuildCacheOutdir}.
|
|
15
|
+
*/
|
|
16
|
+
export declare const getRouteModuleBuildCacheOutdir: typeof getServerModuleBuildCacheOutdir;
|
|
17
|
+
/**
|
|
18
|
+
* Returns the shared production build cache for one server-module outdir.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* One app-scoped instance prevents manifest drift between page imports and static
|
|
22
|
+
* generation during the same build process.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getSharedRouteModuleBuildCache(outdir: string, appConfig?: EcoPagesAppConfig): RouteModuleBuildCache;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolveInternalExecutionDir } from "../../utils/resolve-work-dir.js";
|
|
3
|
+
import { RouteModuleBuildCache } from "./route-module-build-cache.store.js";
|
|
4
|
+
function getServerModuleBuildCacheOutdir(appConfig) {
|
|
5
|
+
return path.join(resolveInternalExecutionDir(appConfig), ".server-modules");
|
|
6
|
+
}
|
|
7
|
+
const getRouteModuleBuildCacheOutdir = getServerModuleBuildCacheOutdir;
|
|
8
|
+
function getRouteModuleBuildCacheMap(appConfig) {
|
|
9
|
+
const existing = appConfig.runtime?.routeModuleBuildCaches;
|
|
10
|
+
if (existing) {
|
|
11
|
+
return existing;
|
|
12
|
+
}
|
|
13
|
+
const routeModuleBuildCaches = /* @__PURE__ */ new Map();
|
|
14
|
+
appConfig.runtime = {
|
|
15
|
+
...appConfig.runtime ?? {},
|
|
16
|
+
routeModuleBuildCaches
|
|
17
|
+
};
|
|
18
|
+
return routeModuleBuildCaches;
|
|
19
|
+
}
|
|
20
|
+
function getSharedRouteModuleBuildCache(outdir, appConfig) {
|
|
21
|
+
if (!appConfig) {
|
|
22
|
+
return new RouteModuleBuildCache(outdir);
|
|
23
|
+
}
|
|
24
|
+
const caches = getRouteModuleBuildCacheMap(appConfig);
|
|
25
|
+
const existingCache = caches.get(outdir);
|
|
26
|
+
if (existingCache) {
|
|
27
|
+
return existingCache;
|
|
28
|
+
}
|
|
29
|
+
const routeModuleBuildCache = new RouteModuleBuildCache(outdir);
|
|
30
|
+
caches.set(outdir, routeModuleBuildCache);
|
|
31
|
+
return routeModuleBuildCache;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
getRouteModuleBuildCacheOutdir,
|
|
35
|
+
getServerModuleBuildCacheOutdir,
|
|
36
|
+
getSharedRouteModuleBuildCache
|
|
37
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { RouteModuleDependencyHashes } from './route-module-dependency-hasher.js';
|
|
2
|
+
export { ROUTE_MODULE_BUILD_CACHE_FILENAME, createEmptyRouteModuleBuildCacheManifest, createPersistedRouteModuleBuildKey, createPluginCacheKey, createJsxCacheKey, hashPluginSetup, getCorePackageVersion, normalizeRouteModuleCachePath, readRouteModuleBuildCacheManifest, resolvePageModuleOutputFileName, shouldPersistRouteModuleBuildCache, writeRouteModuleBuildCacheManifest, type RouteModuleBuildCacheEntry, type RouteModuleBuildCacheLookup, type RouteModuleBuildCacheManifest, type RouteModuleStaticRenderCacheContext, type RouteModuleStaticRenderCacheEntry, } from './route-module-build-manifest.js';
|
|
3
|
+
export { RouteModuleBuildCache } from './route-module-build-cache.store.js';
|
|
4
|
+
export { getRouteModuleBuildCacheOutdir, getServerModuleBuildCacheOutdir, getSharedRouteModuleBuildCache, } from './route-module-build-cache-registry.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ROUTE_MODULE_BUILD_CACHE_FILENAME,
|
|
3
|
+
createEmptyRouteModuleBuildCacheManifest,
|
|
4
|
+
createPersistedRouteModuleBuildKey,
|
|
5
|
+
createPluginCacheKey,
|
|
6
|
+
createJsxCacheKey,
|
|
7
|
+
hashPluginSetup,
|
|
8
|
+
getCorePackageVersion,
|
|
9
|
+
normalizeRouteModuleCachePath,
|
|
10
|
+
readRouteModuleBuildCacheManifest,
|
|
11
|
+
resolvePageModuleOutputFileName,
|
|
12
|
+
shouldPersistRouteModuleBuildCache,
|
|
13
|
+
writeRouteModuleBuildCacheManifest
|
|
14
|
+
} from "./route-module-build-manifest.js";
|
|
15
|
+
import { RouteModuleBuildCache } from "./route-module-build-cache.store.js";
|
|
16
|
+
import {
|
|
17
|
+
getRouteModuleBuildCacheOutdir,
|
|
18
|
+
getServerModuleBuildCacheOutdir,
|
|
19
|
+
getSharedRouteModuleBuildCache
|
|
20
|
+
} from "./route-module-build-cache-registry.js";
|
|
21
|
+
export {
|
|
22
|
+
ROUTE_MODULE_BUILD_CACHE_FILENAME,
|
|
23
|
+
RouteModuleBuildCache,
|
|
24
|
+
createEmptyRouteModuleBuildCacheManifest,
|
|
25
|
+
createJsxCacheKey,
|
|
26
|
+
createPersistedRouteModuleBuildKey,
|
|
27
|
+
createPluginCacheKey,
|
|
28
|
+
getCorePackageVersion,
|
|
29
|
+
getRouteModuleBuildCacheOutdir,
|
|
30
|
+
getServerModuleBuildCacheOutdir,
|
|
31
|
+
getSharedRouteModuleBuildCache,
|
|
32
|
+
hashPluginSetup,
|
|
33
|
+
normalizeRouteModuleCachePath,
|
|
34
|
+
readRouteModuleBuildCacheManifest,
|
|
35
|
+
resolvePageModuleOutputFileName,
|
|
36
|
+
shouldPersistRouteModuleBuildCache,
|
|
37
|
+
writeRouteModuleBuildCacheManifest
|
|
38
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { PageModuleBuildImportOptions } from './page-module-import.service.js';
|
|
2
|
+
import { RouteModuleDependencyHasher, type RouteModuleDependencyHashes } from './route-module-dependency-hasher.js';
|
|
3
|
+
import { type RouteModuleBuildCacheLookup, type RouteModuleBuildCacheManifest, type RouteModuleStaticRenderCacheContext } from './route-module-build-manifest.js';
|
|
4
|
+
export type { RouteModuleDependencyHashes };
|
|
5
|
+
type RouteModuleBuildCacheDependencies = {
|
|
6
|
+
readManifest: (manifestPath: string) => RouteModuleBuildCacheManifest | undefined;
|
|
7
|
+
writeManifest: (manifestPath: string, manifest: RouteModuleBuildCacheManifest) => void;
|
|
8
|
+
exists: (filePath: string) => boolean;
|
|
9
|
+
getCorePackageVersion: () => string;
|
|
10
|
+
createDependencyHasher: () => RouteModuleDependencyHasher;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Persists route-module transpile and static-render metadata under one server outdir.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RouteModuleBuildCache {
|
|
16
|
+
private readonly manifestPath;
|
|
17
|
+
private readonly dependencies;
|
|
18
|
+
private readonly dependencyHasher;
|
|
19
|
+
private manifest;
|
|
20
|
+
private manifestLoaded;
|
|
21
|
+
constructor(outdir: string, dependencies?: Partial<RouteModuleBuildCacheDependencies>);
|
|
22
|
+
lookup(options: PageModuleBuildImportOptions & {
|
|
23
|
+
fileHash: string;
|
|
24
|
+
}): RouteModuleBuildCacheLookup | undefined;
|
|
25
|
+
recordBuild(options: PageModuleBuildImportOptions & {
|
|
26
|
+
fileHash: string;
|
|
27
|
+
outputPath: string;
|
|
28
|
+
dependencyModulePaths?: readonly string[];
|
|
29
|
+
}): void;
|
|
30
|
+
isIncrementalStaticGenerationAvailable(context: RouteModuleStaticRenderCacheContext): boolean;
|
|
31
|
+
ensureIncrementalStaticGenerationContext(context: RouteModuleStaticRenderCacheContext): void;
|
|
32
|
+
canReuseStaticRender(options: {
|
|
33
|
+
sourceFile: string;
|
|
34
|
+
pathname: string;
|
|
35
|
+
renderedOutputPath: string;
|
|
36
|
+
context: RouteModuleStaticRenderCacheContext;
|
|
37
|
+
force?: boolean;
|
|
38
|
+
}): boolean;
|
|
39
|
+
lookupStaticRender(options: {
|
|
40
|
+
filePath: string;
|
|
41
|
+
pathname: string;
|
|
42
|
+
sourceHash: string;
|
|
43
|
+
renderedOutputPath: string;
|
|
44
|
+
context: RouteModuleStaticRenderCacheContext;
|
|
45
|
+
}): boolean;
|
|
46
|
+
recordStaticRender(options: {
|
|
47
|
+
filePath: string;
|
|
48
|
+
pathname: string;
|
|
49
|
+
sourceHash: string;
|
|
50
|
+
renderedOutputPath: string;
|
|
51
|
+
context: RouteModuleStaticRenderCacheContext;
|
|
52
|
+
}): void;
|
|
53
|
+
resetMemory(): void;
|
|
54
|
+
pruneStaleRenderedOutputs(activePathnames: ReadonlySet<string>): string[];
|
|
55
|
+
private loadManifest;
|
|
56
|
+
private persistManifest;
|
|
57
|
+
}
|