@ecopages/core 0.2.0-beta.2 → 0.2.0-beta.20
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 -1
- package/package.json +163 -3
- package/src/adapters/abstract/application-adapter.d.ts +104 -5
- package/src/adapters/abstract/application-adapter.js +125 -1
- package/src/adapters/abstract/server-adapter.d.ts +6 -2
- package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
- package/src/adapters/abstract/ws-pattern-matcher.js +61 -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 +6 -4
- package/src/adapters/bun/create-app.js +65 -43
- package/src/adapters/bun/hmr-manager.d.ts +2 -1
- package/src/adapters/bun/hmr-manager.js +2 -2
- package/src/adapters/bun/server-adapter.d.ts +90 -22
- package/src/adapters/bun/server-adapter.js +265 -84
- package/src/adapters/bun/static-preview-host.js +25 -20
- package/src/adapters/create-app.d.ts +13 -11
- package/src/adapters/create-app.js +2 -51
- package/src/adapters/index.d.ts +1 -1
- package/src/adapters/index.js +1 -2
- package/src/adapters/node/create-app.d.ts +5 -1
- package/src/adapters/node/create-app.js +51 -12
- 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/node-hmr-manager.d.ts +2 -1
- package/src/adapters/node/node-hmr-manager.js +2 -2
- package/src/adapters/node/server-adapter-dependencies.js +4 -0
- package/src/adapters/node/server-adapter.d.ts +62 -20
- package/src/adapters/node/server-adapter.js +161 -131
- package/src/adapters/node/static-content-server.d.ts +1 -0
- package/src/adapters/node/static-content-server.js +10 -2
- package/src/adapters/node/static-preview-host.js +27 -12
- package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
- package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -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/fs-server-response-matcher.d.ts +2 -0
- package/src/adapters/shared/fs-server-response-matcher.js +40 -18
- package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +0 -2
- package/src/adapters/shared/hmr-entrypoint-registrar.js +3 -26
- package/src/adapters/shared/hmr-html-response.d.ts +1 -1
- package/src/adapters/shared/hmr-html-response.js +4 -1
- package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
- package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
- package/src/adapters/shared/port-manager.d.ts +116 -0
- package/src/adapters/shared/port-manager.js +196 -0
- package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
- package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
- package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
- package/src/adapters/shared/runtime-server-lifecycle.js +43 -0
- package/src/adapters/shared/server-adapter.d.ts +2 -1
- package/src/adapters/shared/server-adapter.js +13 -6
- package/src/adapters/shared/server-route-handler.d.ts +4 -1
- package/src/adapters/shared/server-route-handler.js +5 -2
- package/src/adapters/shared/server-static-builder.d.ts +23 -32
- package/src/adapters/shared/server-static-builder.js +97 -73
- package/src/adapters/shared/shared-hmr-manager.d.ts +28 -0
- package/src/adapters/shared/shared-hmr-manager.js +119 -9
- package/src/adapters/shared/static-preview-host.d.ts +5 -0
- package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
- package/src/adapters/shared/websocket-lifecycle.js +55 -0
- package/src/build/README.md +77 -14
- package/src/build/app-build-manifest-runtime.d.ts +10 -0
- package/src/build/app-build-manifest-runtime.js +90 -0
- package/src/build/build-adapter.d.ts +17 -319
- package/src/build/build-adapter.js +28 -94
- package/src/build/build-contracts.d.ts +97 -0
- package/src/build/build-contracts.js +0 -0
- package/src/build/build-input-fingerprint.d.ts +28 -0
- package/src/build/build-input-fingerprint.js +31 -0
- package/src/build/build-profile-options.d.ts +7 -0
- package/src/build/build-profile-options.js +37 -0
- package/src/build/build-runtime.d.ts +24 -0
- package/src/build/build-runtime.js +65 -0
- package/src/build/deduping-build-executor.d.ts +28 -0
- package/src/build/deduping-build-executor.js +56 -0
- package/src/build/jsx-ownership-plugins.d.ts +24 -0
- package/src/build/jsx-ownership-plugins.js +41 -0
- package/src/build/pages-unified-graph-build.d.ts +31 -0
- package/src/build/pages-unified-graph-build.js +206 -0
- package/src/build/parallel-build-executor.d.ts +21 -0
- package/src/build/parallel-build-executor.js +52 -0
- package/src/build/production-build-cache.d.ts +20 -0
- package/src/build/production-build-cache.js +63 -0
- package/src/build/rolldown-adapter-helpers.d.ts +3 -6
- package/src/build/rolldown-adapter-helpers.js +71 -6
- package/src/build/rolldown-build-adapter.d.ts +2 -6
- package/src/build/rolldown-build-adapter.js +3 -3
- 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-plugin-bridge.d.ts +15 -1
- package/src/build/rolldown-plugin-bridge.js +20 -5
- package/src/build/rolldown-source-transform-pass.d.ts +15 -0
- package/src/build/rolldown-source-transform-pass.js +58 -0
- package/src/build/runtime-build-executor.d.ts +8 -15
- package/src/build/runtime-build-executor.js +12 -15
- package/src/build/runtime-build-output-normalizer.d.ts +7 -0
- package/src/build/runtime-build-output-normalizer.js +94 -2
- package/src/build/server-entry-build-cache.d.ts +44 -0
- package/src/build/server-entry-build-cache.js +161 -0
- package/src/client/navigation-scripts/classification.d.ts +16 -0
- package/src/client/navigation-scripts/classification.js +41 -0
- package/src/client/navigation-scripts/index.d.ts +3 -0
- package/src/client/navigation-scripts/index.js +23 -0
- package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
- package/src/client/navigation-scripts/registry.d.ts +11 -0
- package/src/client/navigation-scripts/registry.js +12 -0
- package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
- package/src/client/navigation-scripts/rerun-queue.js +73 -0
- package/src/client/scroll.d.ts +17 -0
- package/src/client/scroll.js +25 -0
- package/src/client/scroll.test.browser.js +37 -0
- package/src/client/view-transitions.d.ts +16 -0
- package/src/client/view-transitions.js +63 -0
- package/src/config/config-builder.d.ts +12 -7
- package/src/config/config-builder.js +12 -9
- package/src/dev/client-bridge-registry.d.ts +5 -0
- package/src/dev/client-bridge-registry.js +15 -0
- package/src/dev/dev-client-ownership.d.ts +7 -0
- package/src/dev/dev-client-ownership.js +6 -0
- package/src/dev/hmr-manager-registry.d.ts +8 -0
- package/src/dev/hmr-manager-registry.js +15 -0
- package/src/dev/host-runtime.d.ts +3 -0
- package/src/dev/host-runtime.js +7 -0
- package/src/dev/runtime-server-started-message.d.ts +2 -0
- package/src/dev/runtime-server-started-message.js +6 -0
- package/src/eco/README.md +11 -0
- package/src/eco/eco-declared-component.d.ts +14 -0
- package/src/eco/eco-declared-component.js +16 -0
- package/src/eco/eco.browser.js +5 -6
- package/src/eco/eco.js +5 -6
- package/src/eco/eco.types.d.ts +2 -2
- package/src/eco/page-layout-normalization.d.ts +21 -0
- package/src/eco/page-layout-normalization.js +59 -0
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +7 -1
- package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
- package/src/errors/undeclared-component-dependency-error.js +17 -0
- package/src/hmr/client/hmr-runtime.js +5 -2
- package/src/hmr/hmr-entrypoint-output.d.ts +28 -0
- package/src/hmr/hmr-entrypoint-output.js +58 -0
- package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -0
- package/src/hmr/strategies/js-hmr-strategy.js +68 -18
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
- package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
- package/src/integrations/ghtml/ghtml-renderer.js +2 -51
- package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
- package/src/integrations/ghtml/ghtml.plugin.js +7 -14
- package/src/plugins/define-integration.d.ts +26 -0
- package/src/plugins/define-integration.js +19 -0
- package/src/plugins/eco-component-meta-plugin.js +5 -8
- package/src/plugins/foreign-jsx-override-plugin.js +4 -6
- package/src/plugins/integration-plugin.d.ts +19 -9
- package/src/plugins/integration-plugin.js +7 -4
- package/src/plugins/jsx-import-source.utils.d.ts +8 -0
- package/src/plugins/jsx-import-source.utils.js +26 -0
- package/src/plugins/processor.d.ts +19 -4
- package/src/plugins/processor.js +22 -4
- package/src/plugins/source-transform.d.ts +31 -0
- package/src/plugins/source-transform.js +23 -1
- package/src/route-renderer/GRAPH.md +8 -8
- package/src/route-renderer/README.md +30 -28
- package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
- package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
- package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
- package/src/route-renderer/orchestration/component-graph.js +98 -0
- package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
- package/src/route-renderer/orchestration/component-render-context.js +3 -1
- package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
- package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
- package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
- package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
- package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
- package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
- package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
- package/src/route-renderer/orchestration/integration-renderer.js +113 -195
- package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
- package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
- package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
- package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
- package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
- package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
- package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
- package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
- package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
- package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
- 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-html-finalization.service.d.ts +17 -0
- package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
- package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
- package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
- package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
- package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
- package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
- package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
- package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
- package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
- package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
- package/src/router/client/link-intent.d.ts +11 -21
- package/src/router/client/link-intent.js +10 -0
- package/src/router/client/link-intent.test.browser.js +12 -1
- package/src/router/client/link-navigation-policy.d.ts +45 -0
- package/src/router/client/link-navigation-policy.js +101 -0
- package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
- package/src/router/client/link-navigation-policy.test.browser.js +92 -0
- package/src/router/client/navigation-coordinator.d.ts +1 -1
- package/src/router/client/navigation-coordinator.js +12 -2
- 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/file-script.processor.js +36 -14
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
- package/src/services/assets/browser-bundle.service.d.ts +3 -1
- package/src/services/assets/browser-bundle.service.js +17 -5
- 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 +20 -2
- package/src/services/invalidation/development-invalidation.service.js +56 -2
- package/src/services/module-loading/README.md +49 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
- package/src/services/module-loading/page-module-import.service.d.ts +5 -1
- package/src/services/module-loading/page-module-import.service.js +97 -45
- 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 +207 -0
- package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
- package/src/services/module-loading/route-module-build-manifest.js +116 -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 +59 -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 +16 -9
- package/src/types/public-types.d.ts +138 -26
- package/src/utils/parse-cli-args.d.ts +2 -0
- package/src/utils/parse-cli-args.js +6 -0
- package/src/utils/server-utils.module.d.ts +3 -2
- package/src/utils/server-utils.module.js +5 -6
- package/src/utils/static-file-extensions.d.ts +12 -0
- package/src/utils/static-file-extensions.js +48 -0
- package/src/watchers/project-watcher-ignore.d.ts +11 -0
- package/src/watchers/project-watcher-ignore.js +16 -0
- package/src/watchers/project-watcher.d.ts +25 -2
- package/src/watchers/project-watcher.js +130 -23
- package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
- package/src/build/rolldown-dev-build-adapter.js +0 -166
- package/src/hmr/hmr.postcss.test.e2e.js +0 -31
- package/src/hmr/hmr.test.e2e.js +0 -43
- package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
- package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
- package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
- package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
- /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
- /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
|
@@ -4,6 +4,8 @@ import path from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
const corePackageRequire = createRequire(new URL("../../package.json", import.meta.url));
|
|
6
6
|
const appDeclaredPackageCache = /* @__PURE__ */ new Map();
|
|
7
|
+
const appWorkspacePackageCache = /* @__PURE__ */ new Map();
|
|
8
|
+
const CORE_RUNTIME_BARE_SPECIFIER_PACKAGES = /* @__PURE__ */ new Set(["ws"]);
|
|
7
9
|
function tryResolveRuntimeImport(specifier, resolver) {
|
|
8
10
|
try {
|
|
9
11
|
return resolver.resolve(specifier);
|
|
@@ -14,6 +16,24 @@ function tryResolveRuntimeImport(specifier, resolver) {
|
|
|
14
16
|
function isBareRuntimeImport(specifier) {
|
|
15
17
|
return !specifier.startsWith(".") && !path.isAbsolute(specifier) && !specifier.startsWith("/") && !specifier.startsWith("node:") && !specifier.startsWith("@/") && !specifier.startsWith("~/") && !specifier.startsWith("#") && !specifier.includes(":");
|
|
16
18
|
}
|
|
19
|
+
function extractPackageNameFromFileUrl(specifier) {
|
|
20
|
+
if (!specifier.startsWith("file://")) {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
const lastNodeModulesIndex = specifier.lastIndexOf("/node_modules/");
|
|
24
|
+
if (lastNodeModulesIndex === -1) {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
const afterNodeModules = specifier.slice(lastNodeModulesIndex + "/node_modules/".length);
|
|
28
|
+
const parts = afterNodeModules.split("/");
|
|
29
|
+
if (parts.length === 0) {
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
if (parts[0]?.startsWith("@") && parts.length > 1) {
|
|
33
|
+
return `${parts[0]}/${parts[1]}`;
|
|
34
|
+
}
|
|
35
|
+
return parts[0];
|
|
36
|
+
}
|
|
17
37
|
function getPackageNameFromSpecifier(specifier) {
|
|
18
38
|
if (specifier.startsWith("@")) {
|
|
19
39
|
return specifier.split("/").slice(0, 2).join("/");
|
|
@@ -46,14 +66,52 @@ function getDeclaredAppPackages(rootDir) {
|
|
|
46
66
|
appDeclaredPackageCache.set(cacheKey, declaredPackages);
|
|
47
67
|
return declaredPackages;
|
|
48
68
|
}
|
|
69
|
+
function getWorkspacePackages(rootDir) {
|
|
70
|
+
const cacheKey = path.resolve(rootDir);
|
|
71
|
+
const cached = appWorkspacePackageCache.get(cacheKey);
|
|
72
|
+
if (cached) {
|
|
73
|
+
return cached;
|
|
74
|
+
}
|
|
75
|
+
const packageJsonPath = path.resolve(rootDir, "package.json");
|
|
76
|
+
const workspacePackages = /* @__PURE__ */ new Set();
|
|
77
|
+
try {
|
|
78
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
79
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
80
|
+
const entries = packageJson[field];
|
|
81
|
+
if (!entries || typeof entries !== "object") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
for (const [packageName, version] of Object.entries(entries)) {
|
|
85
|
+
if (typeof version === "string" && version.startsWith("workspace:")) {
|
|
86
|
+
workspacePackages.add(packageName);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
appWorkspacePackageCache.set(cacheKey, workspacePackages);
|
|
92
|
+
return workspacePackages;
|
|
93
|
+
}
|
|
94
|
+
appWorkspacePackageCache.set(cacheKey, workspacePackages);
|
|
95
|
+
return workspacePackages;
|
|
96
|
+
}
|
|
97
|
+
function isWorkspacePackageImport(specifier, rootDir) {
|
|
98
|
+
return getWorkspacePackages(rootDir).has(getPackageNameFromSpecifier(specifier));
|
|
99
|
+
}
|
|
49
100
|
function isDeclaredAppPackageImport(specifier, rootDir) {
|
|
50
101
|
return getDeclaredAppPackages(rootDir).has(getPackageNameFromSpecifier(specifier));
|
|
51
102
|
}
|
|
103
|
+
function isDeclaredInResolutionChain(specifier, rootDir) {
|
|
104
|
+
const packageName = getPackageNameFromSpecifier(specifier);
|
|
105
|
+
if (CORE_RUNTIME_BARE_SPECIFIER_PACKAGES.has(packageName)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return isDeclaredAppPackageImport(specifier, rootDir);
|
|
109
|
+
}
|
|
52
110
|
function rewriteRuntimeImportSpecifier(specifier, quote, rootDir) {
|
|
53
111
|
if (!isBareRuntimeImport(specifier)) {
|
|
54
112
|
return void 0;
|
|
55
113
|
}
|
|
56
|
-
if (
|
|
114
|
+
if (isDeclaredInResolutionChain(specifier, rootDir)) {
|
|
57
115
|
return void 0;
|
|
58
116
|
}
|
|
59
117
|
const resolvedPath = tryResolveRuntimeImport(specifier, corePackageRequire);
|
|
@@ -67,6 +125,23 @@ function rewriteRuntimeBuildOutputImports(code, rootDir) {
|
|
|
67
125
|
const rewrittenSpecifier = rewriteRuntimeImportSpecifier(specifier, quote, rootDir);
|
|
68
126
|
return `${prefix}${rewrittenSpecifier ?? `${quote}${specifier}${quote}`}`;
|
|
69
127
|
};
|
|
128
|
+
const rewriteFileUrl = (prefix, quote, specifier) => {
|
|
129
|
+
const packageName = extractPackageNameFromFileUrl(specifier);
|
|
130
|
+
if (!packageName) {
|
|
131
|
+
return `${prefix}${quote}${specifier}${quote}`;
|
|
132
|
+
}
|
|
133
|
+
if (!isDeclaredInResolutionChain(packageName, rootDir)) {
|
|
134
|
+
return `${prefix}${quote}${specifier}${quote}`;
|
|
135
|
+
}
|
|
136
|
+
const lastNodeModulesIndex = specifier.lastIndexOf("/node_modules/");
|
|
137
|
+
if (lastNodeModulesIndex === -1) {
|
|
138
|
+
return `${prefix}${quote}${specifier}${quote}`;
|
|
139
|
+
}
|
|
140
|
+
const afterNodeModules = specifier.slice(lastNodeModulesIndex + "/node_modules/".length);
|
|
141
|
+
const afterPackage = afterNodeModules.slice(packageName.length);
|
|
142
|
+
const bareSpecifier = packageName + afterPackage;
|
|
143
|
+
return `${prefix}${quote}${bareSpecifier}${quote}`;
|
|
144
|
+
};
|
|
70
145
|
const withResolvedPackages = code.replace(
|
|
71
146
|
/(\bfrom\s+)(['"])([^'"\\]+)\2/g,
|
|
72
147
|
(_match, prefix, quote, specifier) => rewriteSpecifier(prefix, quote, specifier)
|
|
@@ -77,7 +152,17 @@ function rewriteRuntimeBuildOutputImports(code, rootDir) {
|
|
|
77
152
|
/(\bimport\s*\(\s*)(['"])([^'"\\]+)\2/g,
|
|
78
153
|
(_match, prefix, quote, specifier) => rewriteSpecifier(prefix, quote, specifier)
|
|
79
154
|
);
|
|
80
|
-
|
|
155
|
+
const withBareSpecifiers = withResolvedPackages.replace(
|
|
156
|
+
/(\bfrom\s+)(['"])(file:\/\/[^'"\\]+)\2/g,
|
|
157
|
+
(_match, prefix, quote, specifier) => rewriteFileUrl(prefix, quote, specifier)
|
|
158
|
+
).replace(
|
|
159
|
+
/(\bimport\s+)(['"])(file:\/\/[^'"\\]+)\2/g,
|
|
160
|
+
(_match, prefix, quote, specifier) => rewriteFileUrl(prefix, quote, specifier)
|
|
161
|
+
).replace(
|
|
162
|
+
/(\bimport\s*\(\s*)(['"])(file:\/\/[^'"\\]+)\2/g,
|
|
163
|
+
(_match, prefix, quote, specifier) => rewriteFileUrl(prefix, quote, specifier)
|
|
164
|
+
);
|
|
165
|
+
return withBareSpecifiers.replace(
|
|
81
166
|
/(['"])(@oxc-project\/runtime\/(?:helpers(?:\/esm)?\/[^'"\\]+))\1/g,
|
|
82
167
|
(match, quote, specifier) => {
|
|
83
168
|
const resolvedPath = tryResolveRuntimeImport(specifier, corePackageRequire);
|
|
@@ -86,6 +171,9 @@ function rewriteRuntimeBuildOutputImports(code, rootDir) {
|
|
|
86
171
|
);
|
|
87
172
|
}
|
|
88
173
|
function normalizeNodeRuntimeBuildOutputFile(filePath, rootDir) {
|
|
174
|
+
if (process.env.ECOPAGES_LOGGER_DEBUG === "true") {
|
|
175
|
+
console.log(`[normalizeNodeRuntimeBuildOutputFile] Checking ${filePath}`);
|
|
176
|
+
}
|
|
89
177
|
if (!/\.(?:[cm]?js)$/u.test(filePath)) {
|
|
90
178
|
return;
|
|
91
179
|
}
|
|
@@ -96,6 +184,9 @@ function normalizeNodeRuntimeBuildOutputFile(filePath, rootDir) {
|
|
|
96
184
|
const code = fileSystem.readFileSync(filePath, "utf-8");
|
|
97
185
|
const rewritten = rewriteRuntimeBuildOutputImports(code, rootDir);
|
|
98
186
|
if (rewritten !== code) {
|
|
187
|
+
if (process.env.ECOPAGES_LOGGER_DEBUG === "true") {
|
|
188
|
+
console.log(`[normalizeNodeRuntimeBuildOutputFile] Rewriting ${filePath}`);
|
|
189
|
+
}
|
|
99
190
|
fileSystem.writeFileSync(filePath, rewritten);
|
|
100
191
|
}
|
|
101
192
|
}
|
|
@@ -106,6 +197,7 @@ function normalizeNodeRuntimeBuildOutputs(outputPaths, rootDir) {
|
|
|
106
197
|
}
|
|
107
198
|
export {
|
|
108
199
|
isDeclaredAppPackageImport,
|
|
200
|
+
isWorkspacePackageImport,
|
|
109
201
|
normalizeNodeRuntimeBuildOutputFile,
|
|
110
202
|
normalizeNodeRuntimeBuildOutputs
|
|
111
203
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BuildResult } from './build-adapter.js';
|
|
2
|
+
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
3
|
+
/** @deprecated Use {@link ROUTE_MODULE_BUILD_CACHE_FILENAME}; both caches share the same manifest filename. */
|
|
4
|
+
export declare const SERVER_ENTRY_BUILD_CACHE_FILENAME = ".build-cache.json";
|
|
5
|
+
export declare const SERVER_BUNDLE_MANIFEST_FILENAME = "manifest.json";
|
|
6
|
+
export interface ServerBundleDeployManifest {
|
|
7
|
+
serverEntry: string;
|
|
8
|
+
distDir: string;
|
|
9
|
+
builtAt: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ServerEntryBuildCacheManifest {
|
|
12
|
+
invalidationVersion: string;
|
|
13
|
+
entryPath: string;
|
|
14
|
+
entryHash: string;
|
|
15
|
+
buildInputsFingerprint: string;
|
|
16
|
+
buildKey: string;
|
|
17
|
+
outputPaths: string[];
|
|
18
|
+
dependencyHashes: Record<string, string>;
|
|
19
|
+
builtAt: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ServerEntryBuildCacheLookup {
|
|
22
|
+
manifest: ServerEntryBuildCacheManifest;
|
|
23
|
+
outputPaths: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare function getServerBundleOutputPaths(appConfig: EcoPagesAppConfig): {
|
|
26
|
+
distDir: string;
|
|
27
|
+
serverOutdir: string;
|
|
28
|
+
serverEntryPath: string;
|
|
29
|
+
manifestPath: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function readServerEntryBuildCacheManifest(appConfig: EcoPagesAppConfig): ServerEntryBuildCacheManifest | undefined;
|
|
32
|
+
export declare function writeServerBundleDeployManifest(appConfig: EcoPagesAppConfig, serverEntryPath: string): void;
|
|
33
|
+
export declare function lookupServerEntryBuildCache(options: {
|
|
34
|
+
appConfig: EcoPagesAppConfig;
|
|
35
|
+
entryPath: string;
|
|
36
|
+
force?: boolean;
|
|
37
|
+
}): ServerEntryBuildCacheLookup | undefined;
|
|
38
|
+
export declare function recordServerEntryBuildCache(options: {
|
|
39
|
+
appConfig: EcoPagesAppConfig;
|
|
40
|
+
entryPath: string;
|
|
41
|
+
buildResult: BuildResult;
|
|
42
|
+
outputPaths: string[];
|
|
43
|
+
}): void;
|
|
44
|
+
export declare function resolveProductionServerEntry(cwd?: string): string | undefined;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileSystem } from "@ecopages/file-system";
|
|
3
|
+
import { getAppServerBuildPlugins } from "./build-adapter.js";
|
|
4
|
+
import { createBuildInputsFingerprint } from "./build-input-fingerprint.js";
|
|
5
|
+
import {
|
|
6
|
+
isProductionCacheManifestCurrent,
|
|
7
|
+
matchesProductionCacheBuildKey,
|
|
8
|
+
matchesProductionCacheFingerprint,
|
|
9
|
+
readProductionCacheManifest,
|
|
10
|
+
writeProductionCacheManifest
|
|
11
|
+
} from "./production-build-cache.js";
|
|
12
|
+
import { getCorePackageVersion } from "../services/module-loading/route-module-build-manifest.js";
|
|
13
|
+
import {
|
|
14
|
+
RouteModuleDependencyHasher,
|
|
15
|
+
createRouteModuleDependencyHashes
|
|
16
|
+
} from "../services/module-loading/route-module-dependency-hasher.js";
|
|
17
|
+
import { resolveInternalExecutionDir } from "../utils/resolve-work-dir.js";
|
|
18
|
+
import { SERVER_BUNDLE_DIR, SERVER_BUNDLE_FILENAME } from "../utils/resolve-entry-file.js";
|
|
19
|
+
import { ROUTE_MODULE_BUILD_CACHE_FILENAME } from "../services/module-loading/route-module-build-manifest.js";
|
|
20
|
+
const SERVER_ENTRY_BUILD_CACHE_FILENAME = ROUTE_MODULE_BUILD_CACHE_FILENAME;
|
|
21
|
+
const SERVER_BUNDLE_MANIFEST_FILENAME = "manifest.json";
|
|
22
|
+
function getServerEntryCacheDir(appConfig) {
|
|
23
|
+
return path.join(resolveInternalExecutionDir(appConfig), ".server-entry");
|
|
24
|
+
}
|
|
25
|
+
function getServerEntryCacheManifestPath(appConfig) {
|
|
26
|
+
return path.join(getServerEntryCacheDir(appConfig), SERVER_ENTRY_BUILD_CACHE_FILENAME);
|
|
27
|
+
}
|
|
28
|
+
function hashDependencyGraph(entryPath, dependencyGraph, hasher, rootDir) {
|
|
29
|
+
if (!dependencyGraph) {
|
|
30
|
+
return { [path.resolve(entryPath)]: fileSystem.hash(entryPath) };
|
|
31
|
+
}
|
|
32
|
+
return createRouteModuleDependencyHashes(hasher, { dependencyGraph }, entryPath, rootDir);
|
|
33
|
+
}
|
|
34
|
+
function createServerEntryBuildKey(appConfig) {
|
|
35
|
+
try {
|
|
36
|
+
const plugins = getAppServerBuildPlugins(appConfig);
|
|
37
|
+
const pluginNames = plugins.map((plugin) => plugin.name).sort().join("|");
|
|
38
|
+
return ["node", "esm", "external-packages", pluginNames].join("::");
|
|
39
|
+
} catch {
|
|
40
|
+
return "node::esm::external-packages";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function getServerBundleOutputPaths(appConfig) {
|
|
44
|
+
const distDir = appConfig.absolutePaths?.distDir ?? path.join(appConfig.rootDir, appConfig.distDir);
|
|
45
|
+
const serverOutdir = path.join(distDir, SERVER_BUNDLE_DIR);
|
|
46
|
+
const serverEntryPath = path.join(serverOutdir, SERVER_BUNDLE_FILENAME);
|
|
47
|
+
const manifestPath = path.join(serverOutdir, SERVER_BUNDLE_MANIFEST_FILENAME);
|
|
48
|
+
return { distDir, serverOutdir, serverEntryPath, manifestPath };
|
|
49
|
+
}
|
|
50
|
+
function readServerEntryBuildCacheManifest(appConfig) {
|
|
51
|
+
return readProductionCacheManifest(getServerEntryCacheManifestPath(appConfig));
|
|
52
|
+
}
|
|
53
|
+
function writeServerEntryBuildCacheManifest(appConfig, manifest) {
|
|
54
|
+
writeProductionCacheManifest(
|
|
55
|
+
path.join(getServerEntryCacheDir(appConfig), SERVER_ENTRY_BUILD_CACHE_FILENAME),
|
|
56
|
+
manifest
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function writeServerBundleDeployManifest(appConfig, serverEntryPath) {
|
|
60
|
+
const { distDir, manifestPath } = getServerBundleOutputPaths(appConfig);
|
|
61
|
+
fileSystem.ensureDir(path.dirname(manifestPath));
|
|
62
|
+
const payload = {
|
|
63
|
+
serverEntry: path.relative(path.dirname(manifestPath), serverEntryPath) || SERVER_BUNDLE_FILENAME,
|
|
64
|
+
distDir,
|
|
65
|
+
builtAt: Date.now()
|
|
66
|
+
};
|
|
67
|
+
fileSystem.write(manifestPath, `${JSON.stringify(payload, null, " ")}
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
function lookupServerEntryBuildCache(options) {
|
|
71
|
+
if (options.force) {
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
if (process.env.NODE_ENV !== "production") {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
const entryPath = path.resolve(options.entryPath);
|
|
78
|
+
const entryHash = fileSystem.hash(entryPath);
|
|
79
|
+
const buildInputsFingerprint = createBuildInputsFingerprint(options.appConfig);
|
|
80
|
+
const buildKey = createServerEntryBuildKey(options.appConfig);
|
|
81
|
+
const manifest = readServerEntryBuildCacheManifest(options.appConfig);
|
|
82
|
+
if (!manifest) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
if (!isProductionCacheManifestCurrent(manifest, getCorePackageVersion())) {
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
if (manifest.entryPath !== entryPath || manifest.entryHash !== entryHash || !matchesProductionCacheFingerprint(manifest, buildInputsFingerprint) || !matchesProductionCacheBuildKey(manifest, buildKey)) {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
const hasher = new RouteModuleDependencyHasher();
|
|
92
|
+
if (!hasher.matchesStoredHashes(manifest.dependencyHashes, entryPath, entryHash)) {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
const existingOutputs = manifest.outputPaths.filter((outputPath) => fileSystem.exists(outputPath));
|
|
96
|
+
if (existingOutputs.length === 0) {
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
manifest,
|
|
101
|
+
outputPaths: existingOutputs
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function recordServerEntryBuildCache(options) {
|
|
105
|
+
if (process.env.NODE_ENV !== "production") {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const entryPath = path.resolve(options.entryPath);
|
|
109
|
+
const entryHash = fileSystem.hash(entryPath);
|
|
110
|
+
const hasher = new RouteModuleDependencyHasher();
|
|
111
|
+
const dependencyHashes = hashDependencyGraph(
|
|
112
|
+
entryPath,
|
|
113
|
+
options.buildResult.dependencyGraph,
|
|
114
|
+
hasher,
|
|
115
|
+
options.appConfig.rootDir
|
|
116
|
+
);
|
|
117
|
+
const manifest = {
|
|
118
|
+
invalidationVersion: getCorePackageVersion(),
|
|
119
|
+
entryPath,
|
|
120
|
+
entryHash,
|
|
121
|
+
buildInputsFingerprint: createBuildInputsFingerprint(options.appConfig),
|
|
122
|
+
buildKey: createServerEntryBuildKey(options.appConfig),
|
|
123
|
+
outputPaths: options.outputPaths,
|
|
124
|
+
dependencyHashes,
|
|
125
|
+
builtAt: Date.now()
|
|
126
|
+
};
|
|
127
|
+
writeServerEntryBuildCacheManifest(options.appConfig, manifest);
|
|
128
|
+
}
|
|
129
|
+
function resolveProductionServerEntry(cwd = process.cwd()) {
|
|
130
|
+
const manifestPath = path.join(cwd, "dist", SERVER_BUNDLE_DIR, SERVER_BUNDLE_MANIFEST_FILENAME);
|
|
131
|
+
if (fileSystem.exists(manifestPath)) {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(fileSystem.readFileSync(manifestPath));
|
|
134
|
+
if (parsed?.serverEntry) {
|
|
135
|
+
const fromManifest = path.isAbsolute(parsed.serverEntry) ? parsed.serverEntry : path.join(path.dirname(manifestPath), parsed.serverEntry);
|
|
136
|
+
if (fileSystem.exists(fromManifest)) {
|
|
137
|
+
return fromManifest;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (parsed?.distDir) {
|
|
141
|
+
const fromDistDir = path.join(parsed.distDir, SERVER_BUNDLE_DIR, SERVER_BUNDLE_FILENAME);
|
|
142
|
+
if (fileSystem.exists(fromDistDir)) {
|
|
143
|
+
return fromDistDir;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const legacyPath = path.join(cwd, "dist", SERVER_BUNDLE_DIR, SERVER_BUNDLE_FILENAME);
|
|
150
|
+
return fileSystem.exists(legacyPath) ? legacyPath : void 0;
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
SERVER_BUNDLE_MANIFEST_FILENAME,
|
|
154
|
+
SERVER_ENTRY_BUILD_CACHE_FILENAME,
|
|
155
|
+
getServerBundleOutputPaths,
|
|
156
|
+
lookupServerEntryBuildCache,
|
|
157
|
+
readServerEntryBuildCacheManifest,
|
|
158
|
+
recordServerEntryBuildCache,
|
|
159
|
+
resolveProductionServerEntry,
|
|
160
|
+
writeServerBundleDeployManifest
|
|
161
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classifies head scripts for navigation-time persistence and execution.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Returns whether a script tag carries non-executable data (for example JSON-LD).
|
|
7
|
+
*/
|
|
8
|
+
export declare function isNonExecutableHeadScript(element: Element): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Returns whether an identified inline head script should survive head diffing.
|
|
11
|
+
*/
|
|
12
|
+
export declare function shouldPersistExecutableInlineHeadScript(element: Element): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Returns whether a script is queued for rerun after navigation commit.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isRerunScript(element: Element): element is HTMLScriptElement;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const EXECUTABLE_SCRIPT_TYPES = /* @__PURE__ */ new Set([
|
|
2
|
+
"application/javascript",
|
|
3
|
+
"application/ecmascript",
|
|
4
|
+
"module",
|
|
5
|
+
"text/ecmascript",
|
|
6
|
+
"text/javascript"
|
|
7
|
+
]);
|
|
8
|
+
function isNonExecutableHeadScript(element) {
|
|
9
|
+
if (element.tagName !== "SCRIPT") {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const type = (element.getAttribute("type") ?? "").trim().toLowerCase();
|
|
13
|
+
if (!type) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return !EXECUTABLE_SCRIPT_TYPES.has(type);
|
|
17
|
+
}
|
|
18
|
+
function shouldPersistExecutableInlineHeadScript(element) {
|
|
19
|
+
if (element.tagName !== "SCRIPT") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const scriptId = element.getAttribute("data-eco-script-id") || element.getAttribute("id");
|
|
23
|
+
if (!scriptId) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (element.hasAttribute("data-eco-rerun")) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (element.src) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return !isNonExecutableHeadScript(element);
|
|
33
|
+
}
|
|
34
|
+
function isRerunScript(element) {
|
|
35
|
+
return element.tagName === "SCRIPT" && element.hasAttribute("data-eco-rerun");
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
isNonExecutableHeadScript,
|
|
39
|
+
isRerunScript,
|
|
40
|
+
shouldPersistExecutableInlineHeadScript
|
|
41
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { isNonExecutableHeadScript, isRerunScript, shouldPersistExecutableInlineHeadScript } from './classification.js';
|
|
2
|
+
export { RERUN_SRC_ATTR, getRegisteredRerunScript, type RerunScriptCallback } from './registry.js';
|
|
3
|
+
export { collectRerunScripts, createRerunScriptUrl, findExistingRerunScript, flushPendingRerunScripts, isExternalModuleRerunScript, resetRerunNonceForTests, type PendingRerunScript, } from './rerun-queue.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isNonExecutableHeadScript, isRerunScript, shouldPersistExecutableInlineHeadScript } from "./classification.js";
|
|
2
|
+
import { RERUN_SRC_ATTR, getRegisteredRerunScript } from "./registry.js";
|
|
3
|
+
import {
|
|
4
|
+
collectRerunScripts,
|
|
5
|
+
createRerunScriptUrl,
|
|
6
|
+
findExistingRerunScript,
|
|
7
|
+
flushPendingRerunScripts,
|
|
8
|
+
isExternalModuleRerunScript,
|
|
9
|
+
resetRerunNonceForTests
|
|
10
|
+
} from "./rerun-queue.js";
|
|
11
|
+
export {
|
|
12
|
+
RERUN_SRC_ATTR,
|
|
13
|
+
collectRerunScripts,
|
|
14
|
+
createRerunScriptUrl,
|
|
15
|
+
findExistingRerunScript,
|
|
16
|
+
flushPendingRerunScripts,
|
|
17
|
+
getRegisteredRerunScript,
|
|
18
|
+
isExternalModuleRerunScript,
|
|
19
|
+
isNonExecutableHeadScript,
|
|
20
|
+
isRerunScript,
|
|
21
|
+
resetRerunNonceForTests,
|
|
22
|
+
shouldPersistExecutableInlineHeadScript
|
|
23
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { isNonExecutableHeadScript } from "./classification.js";
|
|
3
|
+
import {
|
|
4
|
+
collectRerunScripts,
|
|
5
|
+
createRerunScriptUrl,
|
|
6
|
+
flushPendingRerunScripts,
|
|
7
|
+
resetRerunNonceForTests
|
|
8
|
+
} from "./rerun-queue.js";
|
|
9
|
+
import { RERUN_SRC_ATTR } from "./registry.js";
|
|
10
|
+
describe("isNonExecutableHeadScript", () => {
|
|
11
|
+
it("treats JSON-LD scripts as non-executable", () => {
|
|
12
|
+
const script = document.createElement("script");
|
|
13
|
+
script.type = "application/ld+json";
|
|
14
|
+
expect(isNonExecutableHeadScript(script)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it("treats untyped scripts as executable", () => {
|
|
17
|
+
const script = document.createElement("script");
|
|
18
|
+
expect(isNonExecutableHeadScript(script)).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("collectRerunScripts and flushPendingRerunScripts", () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.head.replaceChildren();
|
|
24
|
+
document.body.replaceChildren();
|
|
25
|
+
resetRerunNonceForTests();
|
|
26
|
+
});
|
|
27
|
+
it("replays registered rerun callbacks without duplicating script tags", () => {
|
|
28
|
+
const callback = vi.fn();
|
|
29
|
+
window.__ECO_PAGES__ = {
|
|
30
|
+
rerunScripts: { "eco-test": callback }
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
flushPendingRerunScripts([
|
|
34
|
+
{
|
|
35
|
+
parent: "head",
|
|
36
|
+
attributes: [
|
|
37
|
+
["data-eco-rerun", ""],
|
|
38
|
+
["data-eco-script-id", "eco-test"]
|
|
39
|
+
],
|
|
40
|
+
textContent: "",
|
|
41
|
+
src: null,
|
|
42
|
+
scriptId: "eco-test"
|
|
43
|
+
}
|
|
44
|
+
]);
|
|
45
|
+
expect(callback).toHaveBeenCalledOnce();
|
|
46
|
+
expect(document.head.querySelector('script[data-eco-script-id="eco-test"]')).toBeNull();
|
|
47
|
+
} finally {
|
|
48
|
+
delete window.__ECO_PAGES__;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
it("cache-busts external module rerun scripts", () => {
|
|
52
|
+
const html = '<html><head><script data-eco-rerun src="/app.js" type="module"><\/script></head></html>';
|
|
53
|
+
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
54
|
+
const scripts = collectRerunScripts(parsed);
|
|
55
|
+
flushPendingRerunScripts(scripts);
|
|
56
|
+
const inserted = document.head.querySelector(`script[${RERUN_SRC_ATTR}]`);
|
|
57
|
+
expect(inserted?.getAttribute("src")).toMatch(/__eco_rerun=1/);
|
|
58
|
+
expect(inserted?.getAttribute(RERUN_SRC_ATTR)).toBe("/app.js");
|
|
59
|
+
});
|
|
60
|
+
it("appends a unique nonce on each module rerun url", () => {
|
|
61
|
+
const first = createRerunScriptUrl("/app.js");
|
|
62
|
+
const second = createRerunScriptUrl("/app.js");
|
|
63
|
+
expect(first).not.toBe(second);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `__ECO_PAGES__.rerunScripts` registry access for navigation reruns.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
export type RerunScriptCallback = () => void;
|
|
6
|
+
/** Attribute storing the original module `src` after cache-busting. */
|
|
7
|
+
export declare const RERUN_SRC_ATTR = "data-eco-rerun-src";
|
|
8
|
+
/**
|
|
9
|
+
* Returns a registered in-memory rerun callback for the given script id.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getRegisteredRerunScript(scriptId: string | null): RerunScriptCallback | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const RERUN_SRC_ATTR = "data-eco-rerun-src";
|
|
2
|
+
function getRegisteredRerunScript(scriptId) {
|
|
3
|
+
if (!scriptId) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const runtimeWindow = window;
|
|
7
|
+
return runtimeWindow.__ECO_PAGES__?.rerunScripts?.[scriptId] ?? null;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
RERUN_SRC_ATTR,
|
|
11
|
+
getRegisteredRerunScript
|
|
12
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queueing and replay of `data-eco-rerun` scripts after navigation.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
export type PendingRerunScript = {
|
|
6
|
+
parent: 'head' | 'body';
|
|
7
|
+
attributes: Array<[string, string]>;
|
|
8
|
+
textContent: string;
|
|
9
|
+
src: string | null;
|
|
10
|
+
scriptId: string | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Collects rerun scripts from an incoming document.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectRerunScripts(document: Document, filter?: (script: HTMLScriptElement) => boolean): PendingRerunScript[];
|
|
16
|
+
/**
|
|
17
|
+
* Returns whether a rerun script is an external ES module that needs cache busting.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isExternalModuleRerunScript(script: PendingRerunScript): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Finds an existing rerun script in `root` by id or by src and inline content.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findExistingRerunScript(root: ParentNode, script: PendingRerunScript): HTMLScriptElement | null;
|
|
24
|
+
/**
|
|
25
|
+
* Appends a nonce query parameter so module scripts re-execute after navigation.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createRerunScriptUrl(src: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Replays queued rerun scripts after the incoming page body is in place.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Flushed elements keep `data-eco-rerun` so head cleanup does not treat them
|
|
33
|
+
* as persistable inline scripts on the next navigation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function flushPendingRerunScripts(scripts: readonly PendingRerunScript[]): void;
|
|
36
|
+
/** @remarks Test-only reset for nonce sequencing. */
|
|
37
|
+
export declare function resetRerunNonceForTests(): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { RERUN_SRC_ATTR, getRegisteredRerunScript } from "./registry.js";
|
|
2
|
+
let rerunNonce = 0;
|
|
3
|
+
function collectRerunScripts(document2, filter) {
|
|
4
|
+
return Array.from(document2.querySelectorAll("script[data-eco-rerun]")).filter((script) => filter ? filter(script) : true).map((script) => ({
|
|
5
|
+
parent: script.closest("body") ? "body" : "head",
|
|
6
|
+
attributes: Array.from(script.attributes).map((attribute) => [attribute.name, attribute.value]),
|
|
7
|
+
textContent: script.textContent ?? "",
|
|
8
|
+
src: script.getAttribute("src"),
|
|
9
|
+
scriptId: script.getAttribute("data-eco-script-id")
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
function isExternalModuleRerunScript(script) {
|
|
13
|
+
if (!script.src) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return script.attributes.some(([name, value]) => name === "type" && value === "module");
|
|
17
|
+
}
|
|
18
|
+
function findExistingRerunScript(root, script) {
|
|
19
|
+
const scripts = Array.from(root.querySelectorAll("script"));
|
|
20
|
+
if (script.scriptId) {
|
|
21
|
+
return scripts.find((candidate) => candidate.getAttribute("data-eco-script-id") === script.scriptId) ?? null;
|
|
22
|
+
}
|
|
23
|
+
return scripts.find(
|
|
24
|
+
(candidate) => (candidate.getAttribute(RERUN_SRC_ATTR) ?? candidate.getAttribute("src")) === script.src && (candidate.textContent ?? "") === script.textContent
|
|
25
|
+
) ?? null;
|
|
26
|
+
}
|
|
27
|
+
function createRerunScriptUrl(src) {
|
|
28
|
+
const url = new URL(src, document.baseURI);
|
|
29
|
+
url.searchParams.set("__eco_rerun", String(++rerunNonce));
|
|
30
|
+
return url.toString();
|
|
31
|
+
}
|
|
32
|
+
function flushPendingRerunScripts(scripts) {
|
|
33
|
+
for (const script of scripts) {
|
|
34
|
+
const targetParent = script.parent === "body" ? document.body : document.head;
|
|
35
|
+
const registeredRerun = getRegisteredRerunScript(script.scriptId);
|
|
36
|
+
const replacement = document.createElement("script");
|
|
37
|
+
const shouldBustModuleSrc = isExternalModuleRerunScript(script) && !registeredRerun;
|
|
38
|
+
for (const [name, value] of script.attributes) {
|
|
39
|
+
if (name === "src" && shouldBustModuleSrc) {
|
|
40
|
+
replacement.setAttribute(RERUN_SRC_ATTR, value);
|
|
41
|
+
replacement.setAttribute("src", createRerunScriptUrl(value));
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
replacement.setAttribute(name, value);
|
|
45
|
+
}
|
|
46
|
+
replacement.textContent = script.textContent;
|
|
47
|
+
const existingScript = findExistingRerunScript(targetParent, script);
|
|
48
|
+
if (registeredRerun) {
|
|
49
|
+
const needsScriptElement = isExternalModuleRerunScript(script);
|
|
50
|
+
if (!existingScript && needsScriptElement) {
|
|
51
|
+
targetParent.appendChild(replacement);
|
|
52
|
+
}
|
|
53
|
+
registeredRerun();
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (existingScript) {
|
|
57
|
+
existingScript.replaceWith(replacement);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
targetParent.appendChild(replacement);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function resetRerunNonceForTests() {
|
|
64
|
+
rerunNonce = 0;
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
collectRerunScripts,
|
|
68
|
+
createRerunScriptUrl,
|
|
69
|
+
findExistingRerunScript,
|
|
70
|
+
flushPendingRerunScripts,
|
|
71
|
+
isExternalModuleRerunScript,
|
|
72
|
+
resetRerunNonceForTests
|
|
73
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window scroll behavior during client-side navigations.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
export type WindowScrollBehavior = 'top' | 'preserve' | 'auto';
|
|
6
|
+
export type ManageWindowScrollOptions = {
|
|
7
|
+
scrollBehavior: WindowScrollBehavior;
|
|
8
|
+
smoothScroll: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Updates window scroll position after a navigation commit.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Hash navigations always scroll to the target element and ignore `scrollBehavior`.
|
|
15
|
+
* Fragment targets are resolved by element id (`#section` → `getElementById('section')`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function manageWindowScroll(newUrl: URL, previousUrl: URL, options: ManageWindowScrollOptions): void;
|