@ecopages/core 0.2.0-beta.7 → 0.2.0-beta.9
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 +27 -3
- package/src/adapters/abstract/application-adapter.d.ts +12 -1
- package/src/adapters/abstract/application-adapter.js +13 -0
- package/src/adapters/abstract/server-adapter.d.ts +3 -0
- package/src/adapters/bun/client-bridge.d.ts +1 -0
- package/src/adapters/bun/client-bridge.js +3 -0
- package/src/adapters/bun/create-app.d.ts +2 -0
- package/src/adapters/bun/create-app.js +28 -9
- package/src/adapters/bun/server-adapter.d.ts +13 -0
- package/src/adapters/bun/server-adapter.js +29 -4
- package/src/adapters/create-app.d.ts +13 -0
- package/src/adapters/create-app.js +10 -1
- package/src/adapters/node/create-app.d.ts +1 -0
- package/src/adapters/node/create-app.js +17 -7
- package/src/adapters/node/node-client-bridge.d.ts +1 -0
- package/src/adapters/node/node-client-bridge.js +3 -0
- package/src/adapters/node/server-adapter.d.ts +10 -13
- package/src/adapters/node/server-adapter.js +37 -82
- package/src/adapters/node/static-content-server.d.ts +1 -0
- package/src/adapters/node/static-content-server.js +3 -0
- package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
- package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
- package/src/adapters/shared/runtime-app-bootstrap.d.ts +0 -1
- package/src/adapters/shared/runtime-app-bootstrap.js +1 -6
- package/src/adapters/shared/server-static-builder.d.ts +4 -6
- package/src/adapters/shared/server-static-builder.js +90 -34
- package/src/adapters/shared/shared-hmr-manager.d.ts +1 -0
- package/src/adapters/shared/shared-hmr-manager.js +15 -2
- package/src/build/README.md +44 -4
- package/src/build/build-adapter.d.ts +27 -2
- package/src/build/build-adapter.js +75 -32
- 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 +7 -1
- 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/rolldown-plugin-bridge.d.ts +10 -1
- package/src/build/rolldown-plugin-bridge.js +17 -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 +7 -13
- package/src/build/runtime-build-executor.js +31 -7
- package/src/build/server-entry-build-cache.d.ts +44 -0
- package/src/build/server-entry-build-cache.js +169 -0
- package/src/config/config-builder.d.ts +11 -1
- package/src/config/config-builder.js +11 -1
- 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/plugins/source-transform.d.ts +31 -0
- package/src/plugins/source-transform.js +23 -1
- 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 +3 -1
- package/src/services/assets/browser-bundle.service.js +14 -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 +10 -1
- package/src/services/invalidation/development-invalidation.service.js +29 -0
- package/src/services/module-loading/README.md +49 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +4 -17
- package/src/services/module-loading/page-module-import.service.d.ts +4 -1
- package/src/services/module-loading/page-module-import.service.js +52 -29
- package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
- package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
- package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
- package/src/services/module-loading/route-module-build-cache.js +38 -0
- package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
- package/src/services/module-loading/route-module-build-cache.store.js +203 -0
- package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
- package/src/services/module-loading/route-module-build-manifest.js +124 -0
- package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
- package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
- package/src/services/module-loading/server-module-transpiler.service.js +1 -1
- package/src/static-site-generator/README.md +33 -0
- package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
- package/src/static-site-generator/static-build-invalidation.js +78 -0
- package/src/static-site-generator/static-export-context.d.ts +22 -0
- package/src/static-site-generator/static-export-context.js +0 -0
- package/src/static-site-generator/static-site-generator.d.ts +17 -8
- package/src/static-site-generator/static-site-generator.js +176 -62
- package/src/types/internal-types.d.ts +8 -0
- package/src/utils/parse-cli-args.d.ts +1 -0
- package/src/utils/parse-cli-args.js +3 -0
- package/src/watchers/project-watcher.d.ts +8 -0
- package/src/watchers/project-watcher.js +32 -12
|
@@ -6,10 +6,12 @@ import type { EcoPagesElement } from '../types/public-types.js';
|
|
|
6
6
|
import type { IntegrationRenderer } from '../route-renderer/orchestration/integration-renderer.js';
|
|
7
7
|
import { AssetProcessingService } from '../services/assets/asset-processing-service/asset-processing.service.js';
|
|
8
8
|
import type { AssetDefinition, ProcessedAsset } from '../services/assets/asset-processing-service/assets.types.js';
|
|
9
|
+
import type { StaticExportContext } from '../static-site-generator/static-export-context.js';
|
|
9
10
|
import type { RuntimeCapabilityDeclaration } from './runtime-capability.js';
|
|
10
11
|
export type { RuntimeCapabilityDeclaration, RuntimeCapabilityTag } from './runtime-capability.js';
|
|
11
12
|
export type { EcoBuildLoader, EcoBuildOnLoadArgs, EcoBuildOnLoadResult, EcoBuildOnResolveArgs, EcoBuildOnResolveResult, EcoBuildPlugin, EcoBuildPluginBuilder, } from '../build/build-types.js';
|
|
12
13
|
export type { PageBrowserGraphContribution, PageBrowserGraphContributionContext } from '../types/public-types.js';
|
|
14
|
+
export type { StaticExportContext } from '../static-site-generator/static-export-context.js';
|
|
13
15
|
export type { HtmlDocumentContribution, HtmlDocumentContributionContext, } from '../route-renderer/orchestration/integration-renderer.js';
|
|
14
16
|
/**
|
|
15
17
|
* Type-erased integration plugin stored in app-level registries.
|
|
@@ -47,13 +49,6 @@ export interface IntegrationPluginConfig {
|
|
|
47
49
|
* They are not specific to any particular page or component.
|
|
48
50
|
*/
|
|
49
51
|
integrationDependencies?: AssetDefinition[];
|
|
50
|
-
/**
|
|
51
|
-
* The strategy to use for static building.
|
|
52
|
-
* - 'render': Execute component function directly (faster, efficient).
|
|
53
|
-
* - 'fetch': Start server and fetch URL (slower, needed for some SSR like Lit).
|
|
54
|
-
* @default 'render'
|
|
55
|
-
*/
|
|
56
|
-
staticBuildStep?: 'render' | 'fetch';
|
|
57
52
|
/**
|
|
58
53
|
* Declares runtime-specific requirements that must be satisfied before the
|
|
59
54
|
* app can start with this integration enabled.
|
|
@@ -96,7 +91,6 @@ export declare abstract class IntegrationPlugin<C = EcoPagesElement> {
|
|
|
96
91
|
readonly name: string;
|
|
97
92
|
readonly extensions: string[];
|
|
98
93
|
abstract renderer: RendererClass<C>;
|
|
99
|
-
readonly staticBuildStep: 'render' | 'fetch';
|
|
100
94
|
readonly runtimeCapability?: RuntimeCapabilityDeclaration;
|
|
101
95
|
readonly jsxImportSource?: string;
|
|
102
96
|
protected integrationDependencies: AssetDefinition[];
|
|
@@ -213,6 +207,23 @@ export declare abstract class IntegrationPlugin<C = EcoPagesElement> {
|
|
|
213
207
|
* `setup()`.
|
|
214
208
|
*/
|
|
215
209
|
prepareBuildContributions(): Promise<void>;
|
|
210
|
+
/**
|
|
211
|
+
* Reports whether this integration's build inputs changed since the last
|
|
212
|
+
* incremental static build.
|
|
213
|
+
*/
|
|
214
|
+
didChange(): boolean;
|
|
215
|
+
/**
|
|
216
|
+
* Runs integration-specific setup before static page generation begins.
|
|
217
|
+
*
|
|
218
|
+
* @remarks
|
|
219
|
+
* Integrations that need build-scoped SSR preload or worker sessions should
|
|
220
|
+
* start them here rather than per-page inside the renderer.
|
|
221
|
+
*/
|
|
222
|
+
beforeStaticExport?(_context: StaticExportContext): Promise<void>;
|
|
223
|
+
/**
|
|
224
|
+
* Releases resources started in {@link beforeStaticExport}.
|
|
225
|
+
*/
|
|
226
|
+
afterStaticExport?(_context: StaticExportContext): Promise<void>;
|
|
216
227
|
/**
|
|
217
228
|
* Performs runtime-only integration setup after config build has already
|
|
218
229
|
* sealed manifest contributions.
|
|
@@ -15,7 +15,6 @@ function assertIntegrationInvariant(condition, message) {
|
|
|
15
15
|
class IntegrationPlugin {
|
|
16
16
|
name;
|
|
17
17
|
extensions;
|
|
18
|
-
staticBuildStep;
|
|
19
18
|
runtimeCapability;
|
|
20
19
|
jsxImportSource;
|
|
21
20
|
integrationDependencies;
|
|
@@ -60,7 +59,6 @@ class IntegrationPlugin {
|
|
|
60
59
|
this.name = config.name;
|
|
61
60
|
this.extensions = config.extensions;
|
|
62
61
|
this.integrationDependencies = config.integrationDependencies || [];
|
|
63
|
-
this.staticBuildStep = config.staticBuildStep || "render";
|
|
64
62
|
this.runtimeCapability = config.runtimeCapability;
|
|
65
63
|
this.jsxImportSource = config.jsxImportSource;
|
|
66
64
|
}
|
|
@@ -169,6 +167,13 @@ class IntegrationPlugin {
|
|
|
169
167
|
*/
|
|
170
168
|
async prepareBuildContributions() {
|
|
171
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Reports whether this integration's build inputs changed since the last
|
|
172
|
+
* incremental static build.
|
|
173
|
+
*/
|
|
174
|
+
didChange() {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
172
177
|
/**
|
|
173
178
|
* Performs runtime-only integration setup after config build has already
|
|
174
179
|
* sealed manifest contributions.
|
|
@@ -80,6 +80,11 @@ export declare abstract class Processor<TOptions = Record<string, unknown>> {
|
|
|
80
80
|
* warming or watcher registration should stay in `setup()`.
|
|
81
81
|
*/
|
|
82
82
|
prepareBuildContributions(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Reports whether this processor's build inputs changed since the last
|
|
85
|
+
* incremental static build.
|
|
86
|
+
*/
|
|
87
|
+
didChange(): boolean;
|
|
83
88
|
abstract setup(): Promise<void>;
|
|
84
89
|
abstract teardown(): Promise<void>;
|
|
85
90
|
abstract process(input: unknown, filePath?: string): Promise<unknown>;
|
package/src/plugins/processor.js
CHANGED
|
@@ -54,6 +54,13 @@ class Processor {
|
|
|
54
54
|
*/
|
|
55
55
|
async prepareBuildContributions() {
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Reports whether this processor's build inputs changed since the last
|
|
59
|
+
* incremental static build.
|
|
60
|
+
*/
|
|
61
|
+
didChange() {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
57
64
|
getCachePath(key) {
|
|
58
65
|
return `${this.context?.cache}/${key}`;
|
|
59
66
|
}
|
|
@@ -4,9 +4,21 @@ export interface EcoSourceTransformResult {
|
|
|
4
4
|
code: string;
|
|
5
5
|
map?: unknown;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Bundler-neutral source transform registered on {@link EcoPagesAppConfig.sourceTransforms}.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Prefer this shape over a competing {@link EcoBuildPlugin} `onLoad` handler when
|
|
12
|
+
* the transform only rewrites module source. Browser/HMR builds run source
|
|
13
|
+
* transforms after first-wins `onLoad` plugins, so metadata injection and similar
|
|
14
|
+
* passes still run on rewritten output from boundary/runtime plugins.
|
|
15
|
+
*/
|
|
7
16
|
export interface EcoSourceTransform {
|
|
17
|
+
/** Stable transform name. Also used to dedupe loader plugins in browser builds. */
|
|
8
18
|
name: string;
|
|
19
|
+
/** File-path filter tested against {@link normalizeTransformId | normalized ids}. */
|
|
9
20
|
filter: RegExp;
|
|
21
|
+
/** Runs before default transforms, or after them when set to `post`. */
|
|
10
22
|
enforce?: 'pre' | 'post';
|
|
11
23
|
transform(code: string, id: string): EcoSourceTransformResult | string | undefined;
|
|
12
24
|
}
|
|
@@ -24,8 +36,27 @@ export declare function normalizeTransformId(id: string): string;
|
|
|
24
36
|
* Applies one source transform if the normalized id matches its filter.
|
|
25
37
|
*/
|
|
26
38
|
export declare function applySourceTransform(transform: EcoSourceTransform, code: string, id: string): EcoSourceTransformResult | string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Applies app-owned source transforms in deterministic `pre` → default → `post` order.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* Used by the Rolldown plugin bridge after `onLoad` plugins produce final module
|
|
44
|
+
* contents. Transforms that do not match `filter` are skipped; matching transforms
|
|
45
|
+
* are chained left-to-right on the current source string.
|
|
46
|
+
*
|
|
47
|
+
* @param transforms - App-owned transforms, usually from {@link getAppSourceTransforms}.
|
|
48
|
+
* @param code - Current module source.
|
|
49
|
+
* @param id - Module id forwarded to each transform after query/hash normalization.
|
|
50
|
+
* @returns The transformed source, or the original `code` when no transform matches.
|
|
51
|
+
*/
|
|
52
|
+
export declare function applySourceTransforms(transforms: readonly EcoSourceTransform[], code: string, id: string): string;
|
|
27
53
|
/**
|
|
28
54
|
* Adapts a source transform into the existing Ecopages build-plugin contract.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* Server-oriented builds and loader registration still use this adapter.
|
|
58
|
+
* Browser/HMR builds should register the transform in `appConfig.sourceTransforms`
|
|
59
|
+
* instead so the Rolldown bridge can run it after competing `onLoad` plugins.
|
|
29
60
|
*/
|
|
30
61
|
export declare function createEcoBuildPluginFromSourceTransform(transform: EcoSourceTransform): EcoBuildPlugin;
|
|
31
62
|
/**
|
|
@@ -13,6 +13,27 @@ function applySourceTransform(transform, code, id) {
|
|
|
13
13
|
}
|
|
14
14
|
return transform.transform(code, normalizedId);
|
|
15
15
|
}
|
|
16
|
+
const SOURCE_TRANSFORM_ENFORCE_ORDER = {
|
|
17
|
+
pre: 0,
|
|
18
|
+
default: 1,
|
|
19
|
+
post: 2
|
|
20
|
+
};
|
|
21
|
+
function getSourceTransformEnforceOrder(transform) {
|
|
22
|
+
return SOURCE_TRANSFORM_ENFORCE_ORDER[transform.enforce ?? "default"];
|
|
23
|
+
}
|
|
24
|
+
function applySourceTransforms(transforms, code, id) {
|
|
25
|
+
let current = code;
|
|
26
|
+
for (const transform of [...transforms].sort(
|
|
27
|
+
(left, right) => getSourceTransformEnforceOrder(left) - getSourceTransformEnforceOrder(right)
|
|
28
|
+
)) {
|
|
29
|
+
const result = applySourceTransform(transform, current, id);
|
|
30
|
+
if (!result) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
current = typeof result === "string" ? result : result.code;
|
|
34
|
+
}
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
16
37
|
function inferLoaderFromPath(filePath) {
|
|
17
38
|
const extension = path.extname(filePath).toLowerCase();
|
|
18
39
|
switch (extension) {
|
|
@@ -56,13 +77,14 @@ function createVitePluginFromSourceTransform(transform) {
|
|
|
56
77
|
};
|
|
57
78
|
}
|
|
58
79
|
function getAppSourceTransforms(appConfig) {
|
|
59
|
-
return Array.from(appConfig.sourceTransforms.values());
|
|
80
|
+
return appConfig.sourceTransforms ? Array.from(appConfig.sourceTransforms.values()) : [];
|
|
60
81
|
}
|
|
61
82
|
function createVitePluginsFromAppSourceTransforms(appConfig) {
|
|
62
83
|
return getAppSourceTransforms(appConfig).map((transform) => createVitePluginFromSourceTransform(transform));
|
|
63
84
|
}
|
|
64
85
|
export {
|
|
65
86
|
applySourceTransform,
|
|
87
|
+
applySourceTransforms,
|
|
66
88
|
createEcoBuildPluginFromSourceTransform,
|
|
67
89
|
createVitePluginFromSourceTransform,
|
|
68
90
|
createVitePluginsFromAppSourceTransforms,
|
|
@@ -33,11 +33,11 @@ flowchart TD
|
|
|
33
33
|
|
|
34
34
|
## 2) Route Render Flow
|
|
35
35
|
|
|
36
|
-
`IntegrationRenderer.execute()` delegates shared route orchestration to `
|
|
36
|
+
`IntegrationRenderer.execute()` delegates shared route orchestration to `RouteRenderOrchestrator`.
|
|
37
37
|
|
|
38
38
|
```mermaid
|
|
39
39
|
flowchart TD
|
|
40
|
-
A[IntegrationRenderer.execute] --> B[
|
|
40
|
+
A[IntegrationRenderer.execute] --> B[RouteRenderOrchestrator.prepareRenderOptions]
|
|
41
41
|
B --> C[resolvePageModule]
|
|
42
42
|
C --> D[ownershipValidationService.validate]
|
|
43
43
|
D --> E[resolvePageData]
|
|
@@ -121,7 +121,7 @@ flowchart TD
|
|
|
121
121
|
The most useful reading order is:
|
|
122
122
|
|
|
123
123
|
1. `route-renderer.ts`
|
|
124
|
-
2. `orchestration/route-render-
|
|
124
|
+
2. `orchestration/route-render-orchestrator.ts`
|
|
125
125
|
3. `orchestration/integration-renderer.ts`
|
|
126
126
|
4. `orchestration/ownership-validation.service.ts`
|
|
127
127
|
5. `orchestration/ownership-planning.service.ts`
|
|
@@ -134,7 +134,7 @@ The most useful reading order is:
|
|
|
134
134
|
## 7) Key Files
|
|
135
135
|
|
|
136
136
|
- `packages/core/src/route-renderer/route-renderer.ts`
|
|
137
|
-
- `packages/core/src/route-renderer/orchestration/route-render-
|
|
137
|
+
- `packages/core/src/route-renderer/orchestration/route-render-orchestrator.ts`
|
|
138
138
|
- `packages/core/src/route-renderer/orchestration/integration-renderer.ts`
|
|
139
139
|
- `packages/core/src/route-renderer/orchestration/ownership-validation.service.ts`
|
|
140
140
|
- `packages/core/src/route-renderer/orchestration/ownership-planning.service.ts`
|
|
@@ -27,7 +27,7 @@ These concepts intentionally live in different places:
|
|
|
27
27
|
- `component-render-context.ts` intercepts foreign children during active component render
|
|
28
28
|
- `queued-foreign-subtree-resolution.service.ts` resolves queued foreign subtrees for renderers that cannot hand them off inline
|
|
29
29
|
- `integration-renderer.ts` owns renderer-to-renderer delegation and shared shell composition
|
|
30
|
-
- `route-render-
|
|
30
|
+
- `route-render-orchestrator.ts` owns route preparation, final response capture, and unresolved artifact enforcement
|
|
31
31
|
|
|
32
32
|
## Main Files
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ These concepts intentionally live in different places:
|
|
|
38
38
|
|
|
39
39
|
### `orchestration/`
|
|
40
40
|
|
|
41
|
-
- `route-render-
|
|
41
|
+
- `route-render-orchestrator.ts`: one route render from page-module loading through final HTML output
|
|
42
42
|
- `integration-renderer.ts`: abstract base class for route rendering, explicit view rendering, and foreign-child delegation
|
|
43
43
|
- `ownership-planning.service.ts`: declared ownership graph construction
|
|
44
44
|
- `ownership-validation.service.ts`: up-front ownership validation for route roots and declared descendants
|
|
@@ -56,11 +56,11 @@ These concepts intentionally live in different places:
|
|
|
56
56
|
The route-render contract is:
|
|
57
57
|
|
|
58
58
|
1. `RouteRendererFactory` selects the owning integration renderer.
|
|
59
|
-
2. `IntegrationRenderer.execute()` delegates preparation and finalization to `
|
|
60
|
-
3. `
|
|
59
|
+
2. `IntegrationRenderer.execute()` delegates preparation and finalization to `RouteRenderOrchestrator`.
|
|
60
|
+
3. `RouteRenderOrchestrator.prepareRenderOptions()` loads the page module, validates ownership, builds `ownershipPlan`, resolves page data, resolves dependencies, and builds the page browser graph.
|
|
61
61
|
4. The integration renderer performs page, layout, and document-shell rendering. When it encounters a foreign child, it delegates that child back to the owning renderer.
|
|
62
62
|
5. If a renderer needs queued handoff, it emits internal foreign-subtree tokens and resolves them before returning final HTML.
|
|
63
|
-
6. `
|
|
63
|
+
6. `RouteRenderOrchestrator.executePrepared()` captures the final body, rejects unresolved `<eco-marker>` artifacts, stamps root or document attributes when needed, and runs the HTML transformer.
|
|
64
64
|
|
|
65
65
|
Important:
|
|
66
66
|
|
|
@@ -115,4 +115,5 @@ Not every integration needs queue-based handoff.
|
|
|
115
115
|
## Current Limits
|
|
116
116
|
|
|
117
117
|
- `ownershipPlan` is still diagnostic and preparatory metadata; it does not yet drive a full route-composer execution model.
|
|
118
|
+
- `integration-renderer.ts` remains large (~1,400 lines); document-shell helpers and foreign-subtree wiring are the likely future extraction targets.
|
|
118
119
|
- Different integrations still own different foreign-child runtime strategies, which is intentional where child transport or hydration behavior differs.
|
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
QueuedForeignSubtreeResolutionService
|
|
7
7
|
} from "./queued-foreign-subtree-resolution.service.js";
|
|
8
|
-
|
|
9
|
-
return typeof value === "object" && value !== null && "nodeType" in value && typeof value.nodeType === "number" && (!("outerHTML" in value) || typeof value.outerHTML === "string");
|
|
10
|
-
}
|
|
8
|
+
import { isMarkupNodeLike } from "./render-output.utils.js";
|
|
11
9
|
class ForeignSubtreeExecutionService {
|
|
12
10
|
queuedForeignSubtreeResolutionService;
|
|
13
11
|
constructor(queuedForeignSubtreeResolutionService = new QueuedForeignSubtreeResolutionService()) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import type { EcoPagesAppConfig, IHmrManager } from '../../types/internal-types.js';
|
|
7
|
-
import type { ComponentRenderInput, ComponentRenderResult, ForeignSubtreeRenderPayload, EcoComponent, EcoComponentDependencies, EcoPageFile, EcoPagesElement, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
7
|
+
import type { ComponentRenderInput, ComponentRenderResult, ForeignSubtreeRenderPayload, EcoComponent, EcoComponentDependencies, EcoPageFile, EcoPagesElement, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, PagePackageResult, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
8
8
|
import { type AssetProcessingService, type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
|
|
9
9
|
import { HtmlTransformerService } from '../../services/html/html-transformer.service.js';
|
|
10
10
|
import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
|
|
@@ -59,7 +59,15 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
59
59
|
protected pageModuleLoaderService: PageModuleLoaderService;
|
|
60
60
|
protected routeRenderOrchestrator: RouteRenderOrchestrator;
|
|
61
61
|
protected readonly foreignSubtreeExecutionService: ForeignSubtreeExecutionService;
|
|
62
|
+
/**
|
|
63
|
+
* Serializes route and view renders that mutate `htmlTransformer` state.
|
|
64
|
+
*
|
|
65
|
+
* Integration renderers are cached per integration, so concurrent static builds
|
|
66
|
+
* and overlapping SSR requests must not share one transformer page package.
|
|
67
|
+
*/
|
|
68
|
+
private renderExclusiveChain;
|
|
62
69
|
protected DOC_TYPE: string;
|
|
70
|
+
private runRenderExclusive;
|
|
63
71
|
/**
|
|
64
72
|
* Loads one route module through the owning renderer's import path.
|
|
65
73
|
*
|
|
@@ -351,7 +359,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
351
359
|
}): Promise<ComponentRenderResult | undefined>;
|
|
352
360
|
protected renderRouteBody(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
|
|
353
361
|
protected getRouteHtmlFinalization(renderOptions: IntegrationRendererRenderOptions<C>): RouteHtmlFinalization;
|
|
354
|
-
protected transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[]): Promise<RouteRendererBody>;
|
|
362
|
+
protected transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[], pagePackage?: PagePackageResult): Promise<RouteRendererBody>;
|
|
355
363
|
/**
|
|
356
364
|
* Prepares the render options for the integration renderer.
|
|
357
365
|
* It imports the page file, collects dependencies, and prepares the render options.
|
|
@@ -10,15 +10,12 @@ import { OwnershipValidationService } from "./ownership-validation.service.js";
|
|
|
10
10
|
import {
|
|
11
11
|
RouteRenderOrchestrator
|
|
12
12
|
} from "./route-render-orchestrator.js";
|
|
13
|
-
import { normalizeUnresolvedMarkerArtifactHtml } from "./render-output.utils.js";
|
|
13
|
+
import { normalizeUnresolvedMarkerArtifactHtml, isMarkupNodeLike } from "./render-output.utils.js";
|
|
14
14
|
import {
|
|
15
15
|
ForeignSubtreeExecutionService
|
|
16
16
|
} from "./foreign-subtree-execution.service.js";
|
|
17
17
|
import {} from "./queued-foreign-subtree-resolution.service.js";
|
|
18
18
|
import { buildProcessedAssetDedupeKey } from "./processed-asset-dedupe.js";
|
|
19
|
-
function isMarkupNodeLike(value) {
|
|
20
|
-
return typeof value === "object" && value !== null && "nodeType" in value && typeof value.nodeType === "number" && "outerHTML" in value && typeof value.outerHTML === "string";
|
|
21
|
-
}
|
|
22
19
|
class IntegrationRenderer {
|
|
23
20
|
appConfig;
|
|
24
21
|
assetProcessingService;
|
|
@@ -31,7 +28,22 @@ class IntegrationRenderer {
|
|
|
31
28
|
pageModuleLoaderService;
|
|
32
29
|
routeRenderOrchestrator;
|
|
33
30
|
foreignSubtreeExecutionService = new ForeignSubtreeExecutionService();
|
|
31
|
+
/**
|
|
32
|
+
* Serializes route and view renders that mutate `htmlTransformer` state.
|
|
33
|
+
*
|
|
34
|
+
* Integration renderers are cached per integration, so concurrent static builds
|
|
35
|
+
* and overlapping SSR requests must not share one transformer page package.
|
|
36
|
+
*/
|
|
37
|
+
renderExclusiveChain = Promise.resolve();
|
|
34
38
|
DOC_TYPE = "<!DOCTYPE html>";
|
|
39
|
+
runRenderExclusive(operation) {
|
|
40
|
+
const run = this.renderExclusiveChain.then(operation, operation);
|
|
41
|
+
this.renderExclusiveChain = run.then(
|
|
42
|
+
() => void 0,
|
|
43
|
+
() => void 0
|
|
44
|
+
);
|
|
45
|
+
return run;
|
|
46
|
+
}
|
|
35
47
|
/**
|
|
36
48
|
* Loads one route module through the owning renderer's import path.
|
|
37
49
|
*
|
|
@@ -397,7 +409,7 @@ class IntegrationRenderer {
|
|
|
397
409
|
* @returns Structured component render result for orchestration paths.
|
|
398
410
|
*/
|
|
399
411
|
async renderStringComponentWithSerializedChildren(input, component) {
|
|
400
|
-
const serializedChildren = input.children === void 0 ? void 0 : typeof input.children === "string" ? input.children : isMarkupNodeLike(input.children) ? input.children.outerHTML : void 0;
|
|
412
|
+
const serializedChildren = input.children === void 0 ? void 0 : typeof input.children === "string" ? input.children : isMarkupNodeLike(input.children) && typeof input.children.outerHTML === "string" ? input.children.outerHTML : void 0;
|
|
401
413
|
if (input.children !== void 0 && serializedChildren === void 0) {
|
|
402
414
|
const componentFile = input.component.config?.__eco?.file ?? "unknown component";
|
|
403
415
|
const childTag = Object.prototype.toString.call(input.children);
|
|
@@ -533,7 +545,7 @@ class IntegrationRenderer {
|
|
|
533
545
|
* @returns The imported module.
|
|
534
546
|
*/
|
|
535
547
|
async importPageFile(file, options) {
|
|
536
|
-
const bypassCache = options?.bypassCache ??
|
|
548
|
+
const bypassCache = options?.bypassCache ?? false;
|
|
537
549
|
const pageModule = this.usesIntegrationPageImporter(file) ? await this.importIntegrationPageFile(file, {
|
|
538
550
|
bypassCache,
|
|
539
551
|
cacheScope: options?.cacheScope
|
|
@@ -633,7 +645,7 @@ class IntegrationRenderer {
|
|
|
633
645
|
resolveRoutePageComponentRender: (input) => this.resolveRoutePageComponentRender(input),
|
|
634
646
|
renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
|
|
635
647
|
getRouteHtmlFinalization: (renderOptions) => this.getRouteHtmlFinalization(renderOptions),
|
|
636
|
-
transformRouteResponse: (response, htmlContributions) => this.transformRouteResponse(response, htmlContributions)
|
|
648
|
+
transformRouteResponse: (response, htmlContributions, pagePackage) => this.transformRouteResponse(response, htmlContributions, pagePackage)
|
|
637
649
|
};
|
|
638
650
|
}
|
|
639
651
|
async resolveRouteRenderInputs(routeOptions) {
|
|
@@ -706,8 +718,13 @@ class IntegrationRenderer {
|
|
|
706
718
|
}
|
|
707
719
|
};
|
|
708
720
|
}
|
|
709
|
-
async transformRouteResponse(response, htmlContributions) {
|
|
710
|
-
const
|
|
721
|
+
async transformRouteResponse(response, htmlContributions, pagePackage) {
|
|
722
|
+
const resolvedPagePackage = this.htmlTransformer.getPagePackage() ?? pagePackage;
|
|
723
|
+
const transformedResponse = await this.htmlTransformer.transform(
|
|
724
|
+
response,
|
|
725
|
+
htmlContributions,
|
|
726
|
+
resolvedPagePackage
|
|
727
|
+
);
|
|
711
728
|
return transformedResponse.body ?? await transformedResponse.text();
|
|
712
729
|
}
|
|
713
730
|
/**
|
|
@@ -752,9 +769,12 @@ class IntegrationRenderer {
|
|
|
752
769
|
* @returns Rendered route body plus effective cache strategy.
|
|
753
770
|
*/
|
|
754
771
|
async execute(options) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
772
|
+
return this.runRenderExclusive(async () => {
|
|
773
|
+
this.htmlTransformer.setProcessedDependencies([]);
|
|
774
|
+
const adapter = this.createRouteRenderOrchestratorAdapter();
|
|
775
|
+
const renderOptions = await this.prepareRenderOptions(options, adapter);
|
|
776
|
+
return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
|
|
777
|
+
});
|
|
758
778
|
}
|
|
759
779
|
/**
|
|
760
780
|
* Finalizes already-resolved HTML for explicit renderer-owned paths.
|
|
@@ -21,6 +21,8 @@ type MarkupNodeLikeShape = {
|
|
|
21
21
|
* @typeParam T Expected resolved type of the thenable.
|
|
22
22
|
*/
|
|
23
23
|
export declare function isThenable<T>(value: unknown): value is PromiseLike<T>;
|
|
24
|
+
/** Returns whether a value looks like a DOM-like markup node from SSR integrations. */
|
|
25
|
+
export declare function isMarkupNodeLike(value: unknown): value is MarkupNodeLikeShape;
|
|
24
26
|
/**
|
|
25
27
|
* Injects `data-eco-trigger` into the first real HTML element opening tag of
|
|
26
28
|
* a component's rendered output string.
|
|
@@ -82,6 +82,9 @@ function isTemplateContentShape(value) {
|
|
|
82
82
|
function isMarkupNodeLikeShape(value) {
|
|
83
83
|
return typeof value === "object" && value !== null && typeof value.nodeType === "number";
|
|
84
84
|
}
|
|
85
|
+
function isMarkupNodeLike(value) {
|
|
86
|
+
return isMarkupNodeLikeShape(value);
|
|
87
|
+
}
|
|
85
88
|
function injectTriggerAttributeIntoString(content, triggerId) {
|
|
86
89
|
const str = content;
|
|
87
90
|
let i = 0;
|
|
@@ -165,6 +168,7 @@ export {
|
|
|
165
168
|
addTriggerAttribute,
|
|
166
169
|
decodeHtmlEntities,
|
|
167
170
|
inspectUnresolvedMarkerArtifactHtml,
|
|
171
|
+
isMarkupNodeLike,
|
|
168
172
|
isThenable,
|
|
169
173
|
normalizeUnresolvedMarkerArtifactHtml,
|
|
170
174
|
wrapWithScriptsInjector
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
2
|
-
import type { ComponentRenderResult, EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
2
|
+
import type { ComponentRenderResult, EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, PagePackageResult, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
3
3
|
import { type AssetProcessingService, type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
|
|
4
4
|
import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
|
|
5
5
|
import { OwnershipValidationService } from './ownership-validation.service.js';
|
|
@@ -64,7 +64,7 @@ export interface RouteRenderOrchestratorAdapter<C> {
|
|
|
64
64
|
/**
|
|
65
65
|
* Runs SSR-policy response transformation and returns the body value exposed to callers.
|
|
66
66
|
*/
|
|
67
|
-
transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[]): Promise<RouteRendererBody>;
|
|
67
|
+
transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[], pagePackage?: PagePackageResult): Promise<RouteRendererBody>;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Captured route-render output in both replayable body and string HTML forms.
|
|
@@ -205,21 +205,20 @@ class RouteRenderOrchestrator {
|
|
|
205
205
|
return result.assetsByRoute;
|
|
206
206
|
}
|
|
207
207
|
const cacheKey = input.integrationName;
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
208
|
+
let pending = this.groupedPageBrowserGraphCache.get(cacheKey);
|
|
209
|
+
if (!pending) {
|
|
210
|
+
pending = this.buildGroupedPageBrowserAssets(input, currentContribution).then((result) => {
|
|
211
|
+
if (result.hasCollectionFailures) {
|
|
212
|
+
this.groupedPageBrowserGraphCache.delete(cacheKey);
|
|
213
|
+
}
|
|
214
|
+
return result.assetsByRoute;
|
|
215
|
+
}).catch((error) => {
|
|
214
216
|
this.groupedPageBrowserGraphCache.delete(cacheKey);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
this.groupedPageBrowserGraphCache.set(cacheKey, pendingGroupedAssets);
|
|
222
|
-
return await pendingGroupedAssets;
|
|
217
|
+
throw error;
|
|
218
|
+
});
|
|
219
|
+
this.groupedPageBrowserGraphCache.set(cacheKey, pending);
|
|
220
|
+
}
|
|
221
|
+
return await pending;
|
|
223
222
|
}
|
|
224
223
|
/**
|
|
225
224
|
* Builds the shared grouped page-browser asset map for one integration.
|
|
@@ -362,7 +361,8 @@ class RouteRenderOrchestrator {
|
|
|
362
361
|
"Content-Type": "text/html"
|
|
363
362
|
}
|
|
364
363
|
}),
|
|
365
|
-
htmlFinalization.htmlContributions
|
|
364
|
+
htmlFinalization.htmlContributions,
|
|
365
|
+
renderOptions.pagePackage
|
|
366
366
|
);
|
|
367
367
|
return {
|
|
368
368
|
body: body2,
|
|
@@ -376,7 +376,8 @@ class RouteRenderOrchestrator {
|
|
|
376
376
|
"Content-Type": "text/html"
|
|
377
377
|
}
|
|
378
378
|
}),
|
|
379
|
-
htmlFinalization.htmlContributions
|
|
379
|
+
htmlFinalization.htmlContributions,
|
|
380
|
+
renderOptions.pagePackage
|
|
380
381
|
);
|
|
381
382
|
return {
|
|
382
383
|
body,
|
|
@@ -198,6 +198,16 @@ function createEcoNavigationRuntime() {
|
|
|
198
198
|
await runtime.cleanupOwner(owner);
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
|
+
if (typeof window !== "undefined") {
|
|
202
|
+
window.addEventListener("pageshow", (event) => {
|
|
203
|
+
if (event.persisted) {
|
|
204
|
+
runtime.cancelCurrentNavigationTransaction();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
window.addEventListener("pagehide", () => {
|
|
208
|
+
runtime.cancelCurrentNavigationTransaction();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
201
211
|
return runtime;
|
|
202
212
|
}
|
|
203
213
|
function getEcoNavigationRuntime(windowObject = window) {
|
|
@@ -31,6 +31,7 @@ function createBrowserRuntimeModuleAsset(options) {
|
|
|
31
31
|
modules: options.modules,
|
|
32
32
|
fileName: `${options.name}-entry.mjs`,
|
|
33
33
|
rootDir: options.rootDir,
|
|
34
|
+
workDir: options.workDir,
|
|
34
35
|
cacheDirName: options.cacheDirName
|
|
35
36
|
});
|
|
36
37
|
return createBrowserRuntimeScriptAsset({
|
|
@@ -9,12 +9,14 @@ export type BrowserRuntimeEntryModuleConfig = {
|
|
|
9
9
|
* @remarks
|
|
10
10
|
* Integrations use this helper when they need a browser runtime asset composed
|
|
11
11
|
* from multiple bare specifiers but do not want to own temporary file assembly
|
|
12
|
-
* logic themselves. The generated file lives under
|
|
13
|
-
*
|
|
12
|
+
* logic themselves. The generated file lives under the app work directory so
|
|
13
|
+
* repeated runs can reuse the same location without placing sources inside
|
|
14
|
+
* `node_modules`, which can cause bundlers to externalize bare imports.
|
|
14
15
|
*/
|
|
15
16
|
export declare function createBrowserRuntimeEntryModule(options: {
|
|
16
17
|
modules: BrowserRuntimeEntryModuleConfig[];
|
|
17
18
|
fileName: string;
|
|
18
19
|
rootDir?: string;
|
|
20
|
+
workDir?: string;
|
|
19
21
|
cacheDirName?: string;
|
|
20
22
|
}): string;
|
|
@@ -1,37 +1,57 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { DEFAULT_ECOPAGES_WORK_DIR } from "../../../config/constants.js";
|
|
4
5
|
function createBrowserRuntimeEntryModule(options) {
|
|
6
|
+
if (options.modules.some((module) => !module.specifier.startsWith("node:")) && !options.rootDir) {
|
|
7
|
+
throw new Error("createBrowserRuntimeEntryModule requires rootDir to resolve package specifiers");
|
|
8
|
+
}
|
|
5
9
|
const rootDir = options.rootDir ?? process.cwd();
|
|
10
|
+
const workDir = options.workDir ?? path.join(rootDir, DEFAULT_ECOPAGES_WORK_DIR);
|
|
6
11
|
const artifactsDir = path.join(
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
".cache",
|
|
12
|
+
workDir,
|
|
13
|
+
".browser-runtime-entries",
|
|
10
14
|
options.cacheDirName ?? "ecopages-browser-runtime"
|
|
11
15
|
);
|
|
12
16
|
fs.mkdirSync(artifactsDir, { recursive: true });
|
|
13
17
|
const requireFromRoot = createRequire(path.join(rootDir, "package.json"));
|
|
14
18
|
const seenExports = /* @__PURE__ */ new Set();
|
|
15
19
|
const statements = [];
|
|
20
|
+
const filePath = path.join(artifactsDir, options.fileName);
|
|
21
|
+
const entryDir = path.dirname(filePath);
|
|
16
22
|
for (const module of options.modules) {
|
|
23
|
+
const importSpecifier = resolveEntryImportSpecifier(module.specifier, requireFromRoot, entryDir);
|
|
17
24
|
if (module.defaultExport) {
|
|
18
|
-
statements.push(`import __ecopages_default_export__ from '${
|
|
25
|
+
statements.push(`import __ecopages_default_export__ from '${importSpecifier}';`);
|
|
19
26
|
statements.push("export default __ecopages_default_export__;");
|
|
20
27
|
}
|
|
21
28
|
const exportNames = getModuleExportNames(module.specifier, requireFromRoot).filter(
|
|
22
29
|
(name) => !seenExports.has(name)
|
|
23
30
|
);
|
|
24
31
|
if (exportNames.length > 0) {
|
|
25
|
-
statements.push(`export { ${exportNames.join(", ")} } from '${
|
|
32
|
+
statements.push(`export { ${exportNames.join(", ")} } from '${importSpecifier}';`);
|
|
26
33
|
for (const exportName of exportNames) {
|
|
27
34
|
seenExports.add(exportName);
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
|
-
const
|
|
32
|
-
fs.
|
|
38
|
+
const content = statements.join("\n");
|
|
39
|
+
if (!fs.existsSync(filePath) || fs.readFileSync(filePath, "utf-8") !== content) {
|
|
40
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
41
|
+
}
|
|
33
42
|
return filePath;
|
|
34
43
|
}
|
|
44
|
+
function resolveEntryImportSpecifier(specifier, requireFromRoot, entryDir) {
|
|
45
|
+
if (specifier.startsWith("node:") || specifier.startsWith("file:")) {
|
|
46
|
+
return specifier;
|
|
47
|
+
}
|
|
48
|
+
const resolvedPath = requireFromRoot.resolve(specifier);
|
|
49
|
+
let relativePath = path.relative(entryDir, resolvedPath).replace(/\\/g, "/");
|
|
50
|
+
if (!relativePath.startsWith(".")) {
|
|
51
|
+
relativePath = `./${relativePath}`;
|
|
52
|
+
}
|
|
53
|
+
return relativePath;
|
|
54
|
+
}
|
|
35
55
|
function getModuleExportNames(specifier, requireFromRoot) {
|
|
36
56
|
const moduleExports = requireFromRoot(specifier);
|
|
37
57
|
return Object.keys(moduleExports).filter((name) => name !== "__esModule" && name !== "default").filter((name) => /^[$A-Z_a-z][$\w]*$/.test(name)).sort();
|