@ecopages/core 0.2.0-beta.22 → 0.2.0-beta.24
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 +13 -3
- package/src/adapters/abstract/application-adapter.js +3 -0
- package/src/adapters/bun/create-app.js +2 -0
- package/src/adapters/node/create-app.js +2 -0
- package/src/adapters/shared/server-adapter.d.ts +1 -0
- package/src/adapters/shared/server-adapter.js +31 -22
- package/src/build/app-build-manifest-runtime.js +12 -1
- package/src/build/dev-browser-script-cache.d.ts +34 -0
- package/src/build/dev-browser-script-cache.js +90 -0
- package/src/build/lit-static-render-worker-context.d.ts +2 -0
- package/src/build/lit-static-render-worker-context.js +6 -0
- package/src/diagnostics/request-build-dedupe.d.ts +18 -0
- package/src/diagnostics/request-build-dedupe.js +33 -0
- package/src/diagnostics/startup-trace.d.ts +25 -0
- package/src/diagnostics/startup-trace.js +121 -0
- package/src/env.d.ts +2 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +4 -1
- package/src/plugins/integration-plugin.d.ts +8 -0
- package/src/plugins/processor.d.ts +6 -0
- package/src/plugins/processor.js +2 -1
- package/src/route-renderer/orchestration/page-browser-graph.service.js +15 -9
- package/src/route-renderer/page-loading/ecopages-virtual-imports.d.ts +6 -0
- package/src/route-renderer/page-loading/ecopages-virtual-imports.js +7 -2
- package/src/route-renderer/page-loading/module-declaration-aggregation.js +4 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.d.ts +3 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.js +41 -4
- package/src/services/assets/asset-processing-service/finalize-processed-asset.d.ts +3 -0
- package/src/services/assets/asset-processing-service/finalize-processed-asset.js +7 -0
- package/src/services/assets/asset-processing-service/grouped-content-bundles.d.ts +2 -1
- package/src/services/assets/asset-processing-service/grouped-content-bundles.js +20 -10
- package/src/services/assets/asset-processing-service/index.d.ts +1 -0
- package/src/services/assets/asset-processing-service/index.js +1 -0
- package/src/services/assets/asset-processing-service/materialize-content-script-asset.d.ts +11 -0
- package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +17 -0
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.d.ts +7 -0
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +105 -79
- package/src/services/assets/asset-processing-service/resolve-integration-plugin.d.ts +4 -0
- package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +17 -0
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.d.ts +0 -1
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +5 -10
- package/src/services/assets/browser-bundle.service.js +10 -2
- package/src/services/validation/schema-validation-service.d.ts +1 -4
- package/src/services/validation/schema-validation-service.js +12 -13
- package/src/services/validation/standard-schema.types.d.ts +9 -62
- package/src/services/validation/validate-standard-schema.d.ts +7 -0
- package/src/services/validation/validate-standard-schema.js +14 -0
- package/src/types/public-types.d.ts +2 -2
package/src/plugins/processor.js
CHANGED
|
@@ -103,6 +103,9 @@ class PageBrowserGraphService {
|
|
|
103
103
|
);
|
|
104
104
|
continue;
|
|
105
105
|
}
|
|
106
|
+
if (this.isHmrEnabled()) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
106
109
|
if (!contribution?.dependencies?.length) {
|
|
107
110
|
continue;
|
|
108
111
|
}
|
|
@@ -130,15 +133,18 @@ class PageBrowserGraphService {
|
|
|
130
133
|
);
|
|
131
134
|
const groupedAssetsByRoute = /* @__PURE__ */ new Map();
|
|
132
135
|
for (const [routeFile, groupedAssetKeys] of groupedAssetKeysByRoute) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
const matchedAssets = processedGroupedDependencies.filter((asset) => {
|
|
137
|
+
if (!asset.groupedBundle) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return groupedAssetKeys.has(getGroupedBundleAssetKey(asset.groupedBundle));
|
|
141
|
+
});
|
|
142
|
+
if (groupedAssetKeys.size > 0 && matchedAssets.length === 0) {
|
|
143
|
+
appLogger.warn(
|
|
144
|
+
`Grouped page-browser assets for ${routeFile} are missing groupedBundle metadata after processing. Hydration scripts may be omitted from HTML.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
groupedAssetsByRoute.set(routeFile, matchedAssets);
|
|
142
148
|
}
|
|
143
149
|
return {
|
|
144
150
|
assetsByRoute: groupedAssetsByRoute,
|
|
@@ -2,6 +2,12 @@ export type EcopagesVirtualImport = {
|
|
|
2
2
|
from: string;
|
|
3
3
|
imports: string[] | undefined;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* @remarks
|
|
7
|
+
* Content server modules static-import every MDX entry. They must never become
|
|
8
|
+
* browser module-script dependencies discovered from page or component sources.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isBrowserEcopagesVirtualImport(specifier: string): boolean;
|
|
5
11
|
/**
|
|
6
12
|
* Extracts runtime `ecopages:` virtual-module imports from a component source file.
|
|
7
13
|
*
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { parseSync } from "oxc-parser";
|
|
3
|
+
const CONTENT_SERVER_VIRTUAL_MODULE_PATTERN = /^ecopages:content\/[a-z][a-z0-9-]+\/server$/;
|
|
4
|
+
function isBrowserEcopagesVirtualImport(specifier) {
|
|
5
|
+
return specifier.startsWith("ecopages:") && !CONTENT_SERVER_VIRTUAL_MODULE_PATTERN.test(specifier);
|
|
6
|
+
}
|
|
3
7
|
function extractEcopagesVirtualImports(file) {
|
|
4
8
|
let source;
|
|
5
9
|
try {
|
|
@@ -18,7 +22,7 @@ function extractEcopagesVirtualImports(file) {
|
|
|
18
22
|
if (node.type !== "ImportDeclaration") continue;
|
|
19
23
|
if (node.importKind === "type") continue;
|
|
20
24
|
const specifier = node.source?.value ?? "";
|
|
21
|
-
if (!specifier
|
|
25
|
+
if (!isBrowserEcopagesVirtualImport(specifier)) continue;
|
|
22
26
|
if (found.get(specifier) === null) {
|
|
23
27
|
continue;
|
|
24
28
|
}
|
|
@@ -53,5 +57,6 @@ function extractEcopagesVirtualImports(file) {
|
|
|
53
57
|
}));
|
|
54
58
|
}
|
|
55
59
|
export {
|
|
56
|
-
extractEcopagesVirtualImports
|
|
60
|
+
extractEcopagesVirtualImports,
|
|
61
|
+
isBrowserEcopagesVirtualImport
|
|
57
62
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeModuleDeclarations } from "../../eco/module-dependencies.js";
|
|
2
|
+
import { isBrowserEcopagesVirtualImport } from "./ecopages-virtual-imports.js";
|
|
2
3
|
function getDeclaredModules(value) {
|
|
3
4
|
if (!Array.isArray(value)) {
|
|
4
5
|
return void 0;
|
|
@@ -22,6 +23,9 @@ function mergeModuleDeclaration(modulesMap, declaration) {
|
|
|
22
23
|
}
|
|
23
24
|
function collectModuleDeclarations(modulesMap, declaredModules, autoVirtualImports) {
|
|
24
25
|
for (const declaration of normalizeModuleDeclarations(getDeclaredModules(declaredModules))) {
|
|
26
|
+
if (declaration.from.startsWith("ecopages:") && !isBrowserEcopagesVirtualImport(declaration.from)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
25
29
|
mergeModuleDeclaration(modulesMap, declaration);
|
|
26
30
|
}
|
|
27
31
|
for (const declaration of autoVirtualImports) {
|
|
@@ -42,6 +42,7 @@ export declare class AssetProcessingService {
|
|
|
42
42
|
* entries.
|
|
43
43
|
*/
|
|
44
44
|
processDependencies(deps: AssetDefinition[], key: string): Promise<ProcessedAsset[]>;
|
|
45
|
+
private prepareDependenciesForProcessing;
|
|
45
46
|
/**
|
|
46
47
|
* Processes deduplicated dependencies grouped by processor type.
|
|
47
48
|
*
|
|
@@ -80,6 +81,8 @@ export declare class AssetProcessingService {
|
|
|
80
81
|
* Returns the cached processed asset for a dependency key when available.
|
|
81
82
|
*/
|
|
82
83
|
private getCachedAsset;
|
|
84
|
+
private getCachedContentScriptAsset;
|
|
85
|
+
private resolveCachedContentScriptFilepath;
|
|
83
86
|
/**
|
|
84
87
|
* Stores one processed asset in the dependency cache.
|
|
85
88
|
*/
|
|
@@ -4,12 +4,19 @@ import { appLogger } from "../../../global/app-logger.js";
|
|
|
4
4
|
import { fileSystem } from "@ecopages/file-system";
|
|
5
5
|
import { deduplicateAssetDependencies, getAssetDependencyKey } from "./asset-dependency-keys.js";
|
|
6
6
|
import {
|
|
7
|
+
ensureGroupedContentScriptsBundle,
|
|
7
8
|
partitionGroupedContentScriptDependencies,
|
|
8
9
|
processGroupedDependencyBundles
|
|
9
10
|
} from "./grouped-content-bundles.js";
|
|
11
|
+
import { resolveIntegrationPluginForProcessingKey } from "./resolve-integration-plugin.js";
|
|
10
12
|
import { isHmrAware } from "./processor.interface.js";
|
|
11
13
|
import { ProcessorRegistry } from "./processor.registry.js";
|
|
12
14
|
import { processUngroupedDependency } from "./ungrouped-dependency-processing.js";
|
|
15
|
+
import { materializeContentScriptAsset } from "./materialize-content-script-asset.js";
|
|
16
|
+
import {
|
|
17
|
+
getDevBrowserScriptCacheEntry,
|
|
18
|
+
setDevBrowserScriptCacheEntry
|
|
19
|
+
} from "../../../build/dev-browser-script-cache.js";
|
|
13
20
|
import {
|
|
14
21
|
ContentScriptProcessor,
|
|
15
22
|
ContentStylesheetProcessor,
|
|
@@ -68,10 +75,16 @@ class AssetProcessingService {
|
|
|
68
75
|
const depsDir = path.join(this.config.absolutePaths.distDir, RESOLVED_ASSETS_DIR);
|
|
69
76
|
fileSystem.ensureDir(depsDir);
|
|
70
77
|
const dedupedDeps = deduplicateAssetDependencies(deps);
|
|
71
|
-
const
|
|
78
|
+
const preparedDeps = this.prepareDependenciesForProcessing(dedupedDeps, key);
|
|
79
|
+
ensureGroupedContentScriptsBundle(preparedDeps);
|
|
80
|
+
const results = await this.processDependenciesParallel(preparedDeps);
|
|
72
81
|
await this.optimizeDependencies(results);
|
|
73
82
|
return results;
|
|
74
83
|
}
|
|
84
|
+
prepareDependenciesForProcessing(deps, processingKey) {
|
|
85
|
+
const plugin = resolveIntegrationPluginForProcessingKey(this.config, processingKey);
|
|
86
|
+
return plugin?.prepareAssetDependencies?.(deps) ?? deps;
|
|
87
|
+
}
|
|
75
88
|
/**
|
|
76
89
|
* Processes deduplicated dependencies grouped by processor type.
|
|
77
90
|
*
|
|
@@ -80,14 +93,13 @@ class AssetProcessingService {
|
|
|
80
93
|
* pair, while still allowing the overall dependency set to resolve in
|
|
81
94
|
* parallel.
|
|
82
95
|
*/
|
|
83
|
-
async processDependenciesParallel(deps
|
|
96
|
+
async processDependenciesParallel(deps) {
|
|
84
97
|
const grouped = this.groupDependenciesByType(deps);
|
|
85
98
|
const groupPromises = Object.entries(grouped).map(async ([, typeDeps]) => {
|
|
86
99
|
const { groupedBundleDeps, ungroupedDeps } = partitionGroupedContentScriptDependencies(typeDeps);
|
|
87
100
|
const typePromises = ungroupedDeps.map(
|
|
88
101
|
(dep) => processUngroupedDependency({
|
|
89
102
|
dep,
|
|
90
|
-
key,
|
|
91
103
|
depKey: getAssetDependencyKey(dep),
|
|
92
104
|
getCachedAsset: (assetDep, depKey) => this.getCachedAsset(assetDep, depKey),
|
|
93
105
|
getProcessor: (assetDep) => this.registry.getProcessor(assetDep.kind, assetDep.source),
|
|
@@ -109,7 +121,6 @@ class AssetProcessingService {
|
|
|
109
121
|
);
|
|
110
122
|
const groupedResults = await processGroupedDependencyBundles({
|
|
111
123
|
bundles: Array.from(groupedBundleDeps.values()),
|
|
112
|
-
key,
|
|
113
124
|
getCachedAsset: (dep, depKey) => this.getCachedAsset(dep, depKey),
|
|
114
125
|
getDependencyKey: getAssetDependencyKey,
|
|
115
126
|
getGroupedProcessor: () => this.registry.getProcessor("script", "content"),
|
|
@@ -229,6 +240,9 @@ class AssetProcessingService {
|
|
|
229
240
|
if (process.env.NODE_ENV !== "production" && dep.source === "file" && dep.kind === "stylesheet") {
|
|
230
241
|
return null;
|
|
231
242
|
}
|
|
243
|
+
if (dep.kind === "script" && dep.source === "content") {
|
|
244
|
+
return this.getCachedContentScriptAsset(dep, depKey);
|
|
245
|
+
}
|
|
232
246
|
const cached = this.cache.get(depKey);
|
|
233
247
|
if (!cached) {
|
|
234
248
|
return null;
|
|
@@ -239,11 +253,34 @@ class AssetProcessingService {
|
|
|
239
253
|
}
|
|
240
254
|
return cached.asset;
|
|
241
255
|
}
|
|
256
|
+
getCachedContentScriptAsset(dep, depKey) {
|
|
257
|
+
const filepath = this.resolveCachedContentScriptFilepath(depKey) ?? getDevBrowserScriptCacheEntry(this.config, depKey)?.filepath;
|
|
258
|
+
if (!filepath) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const materialized = materializeContentScriptAsset(dep, filepath);
|
|
262
|
+
this.cache.set(depKey, { asset: materialized });
|
|
263
|
+
return materialized;
|
|
264
|
+
}
|
|
265
|
+
resolveCachedContentScriptFilepath(depKey) {
|
|
266
|
+
const cached = this.cache.get(depKey);
|
|
267
|
+
if (!cached?.asset.filepath) {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
if (!fileSystem.exists(cached.asset.filepath)) {
|
|
271
|
+
this.cache.delete(depKey);
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
return cached.asset.filepath;
|
|
275
|
+
}
|
|
242
276
|
/**
|
|
243
277
|
* Stores one processed asset in the dependency cache.
|
|
244
278
|
*/
|
|
245
279
|
setCachedAsset(dep, depKey, asset) {
|
|
246
280
|
this.cache.set(depKey, { asset });
|
|
281
|
+
if (dep.kind === "script" && dep.source === "content") {
|
|
282
|
+
setDevBrowserScriptCacheEntry(this.config, depKey, asset);
|
|
283
|
+
}
|
|
247
284
|
}
|
|
248
285
|
/**
|
|
249
286
|
* Clears all cached processed assets.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ProcessedAsset } from './assets.types.js';
|
|
2
|
+
/** Applies the public source URL to one processed asset when available. */
|
|
3
|
+
export declare function finalizeProcessedAsset(processed: ProcessedAsset, resolveProcessedAssetSrcUrl: (processed: ProcessedAsset) => string | undefined): ProcessedAsset;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { AssetDefinition, ProcessedAsset } from './assets.types.js';
|
|
2
|
+
/** Forces grouped content scripts to run through the bundler in production builds. */
|
|
3
|
+
export declare function ensureGroupedContentScriptsBundle(dependencies: AssetDefinition[]): void;
|
|
2
4
|
/**
|
|
3
5
|
* Splits grouped content-script dependencies from ordinary dependencies so callers can
|
|
4
6
|
* route them through `processGrouped` without changing ordering for the remaining assets.
|
|
@@ -12,7 +14,6 @@ type GroupedBundleProcessor = {
|
|
|
12
14
|
};
|
|
13
15
|
type ProcessGroupedDependencyBundlesOptions = {
|
|
14
16
|
bundles: AssetDefinition[][];
|
|
15
|
-
key: string;
|
|
16
17
|
getCachedAsset: (dep: AssetDefinition, depKey: string) => ProcessedAsset | null;
|
|
17
18
|
getDependencyKey: (dep: AssetDefinition) => string;
|
|
18
19
|
getGroupedProcessor: () => GroupedBundleProcessor | undefined;
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
import { isDevelopmentRuntime } from "../../../utils/runtime.js";
|
|
2
|
+
import { finalizeProcessedAsset } from "./finalize-processed-asset.js";
|
|
3
|
+
function ensureGroupedContentScriptsBundle(dependencies) {
|
|
4
|
+
if (isDevelopmentRuntime()) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
for (const dependency of dependencies) {
|
|
8
|
+
if (dependency.kind !== "script" || dependency.source !== "content" || !dependency.groupedBundle?.id) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (dependency.bundle === false) {
|
|
12
|
+
dependency.bundle = true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
1
16
|
function partitionGroupedContentScriptDependencies(typeDeps) {
|
|
2
17
|
const groupedBundleDeps = /* @__PURE__ */ new Map();
|
|
3
18
|
const ungroupedDeps = [];
|
|
@@ -18,7 +33,6 @@ function partitionGroupedContentScriptDependencies(typeDeps) {
|
|
|
18
33
|
async function processGroupedDependencyBundles(options) {
|
|
19
34
|
const {
|
|
20
35
|
bundles,
|
|
21
|
-
key,
|
|
22
36
|
getCachedAsset,
|
|
23
37
|
getDependencyKey,
|
|
24
38
|
getGroupedProcessor,
|
|
@@ -29,7 +43,7 @@ async function processGroupedDependencyBundles(options) {
|
|
|
29
43
|
const groupedPromises = bundles.map(async (bundleDeps) => {
|
|
30
44
|
const cachedResults = bundleDeps.map((dep) => {
|
|
31
45
|
const cached = getCachedAsset(dep, getDependencyKey(dep));
|
|
32
|
-
return cached ?
|
|
46
|
+
return cached ? finalizeProcessedAsset(cached, resolveProcessedAssetSrcUrl) : null;
|
|
33
47
|
});
|
|
34
48
|
if (cachedResults.every((result) => result !== null)) {
|
|
35
49
|
return cachedResults.filter((result) => result !== null);
|
|
@@ -43,14 +57,9 @@ async function processGroupedDependencyBundles(options) {
|
|
|
43
57
|
return processedResults.map((processed, index) => {
|
|
44
58
|
const dep = bundleDeps[index];
|
|
45
59
|
const depKey = getDependencyKey(dep);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...processed,
|
|
50
|
-
srcUrl
|
|
51
|
-
};
|
|
52
|
-
setCachedAsset(dep, depKey, processedWithKey);
|
|
53
|
-
return processedWithKey;
|
|
60
|
+
const finalized = finalizeProcessedAsset(processed, resolveProcessedAssetSrcUrl);
|
|
61
|
+
setCachedAsset(dep, depKey, finalized);
|
|
62
|
+
return finalized;
|
|
54
63
|
});
|
|
55
64
|
} catch (error) {
|
|
56
65
|
logError(error);
|
|
@@ -60,6 +69,7 @@ async function processGroupedDependencyBundles(options) {
|
|
|
60
69
|
return (await Promise.all(groupedPromises)).flat();
|
|
61
70
|
}
|
|
62
71
|
export {
|
|
72
|
+
ensureGroupedContentScriptsBundle,
|
|
63
73
|
partitionGroupedContentScriptDependencies,
|
|
64
74
|
processGroupedDependencyBundles
|
|
65
75
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContentScriptAsset, ProcessedAsset } from './assets.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a processed content-script asset from one dependency declaration and a
|
|
4
|
+
* previously emitted output file.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Dev disk cache entries store only output paths. Callers must materialize the
|
|
8
|
+
* full processed asset from the originating dependency so HTML and page-browser
|
|
9
|
+
* graph assembly receive grouped-bundle metadata and script attributes.
|
|
10
|
+
*/
|
|
11
|
+
export declare function materializeContentScriptAsset(dep: ContentScriptAsset, filepath: string): ProcessedAsset;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function materializeContentScriptAsset(dep, filepath) {
|
|
2
|
+
return {
|
|
3
|
+
filepath,
|
|
4
|
+
kind: "script",
|
|
5
|
+
inline: dep.inline ?? false,
|
|
6
|
+
content: dep.inline ? dep.content : void 0,
|
|
7
|
+
position: dep.position,
|
|
8
|
+
attributes: dep.attributes,
|
|
9
|
+
excludeFromHtml: dep.excludeFromHtml,
|
|
10
|
+
packageRole: dep.packageRole,
|
|
11
|
+
groupedBundle: dep.groupedBundle,
|
|
12
|
+
bundledSourceFilepaths: dep.bundledSourceFilepaths
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
materializeContentScriptAsset
|
|
17
|
+
};
|
package/src/services/assets/asset-processing-service/processors/script/content-script.processor.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { ContentScriptAsset, ProcessedAsset } from '../../assets.types.js';
|
|
2
2
|
import { BaseScriptProcessor } from '../base/base-script-processor.js';
|
|
3
3
|
export declare class ContentScriptProcessor extends BaseScriptProcessor<ContentScriptAsset> {
|
|
4
|
+
private getContentScriptEntryDir;
|
|
5
|
+
private getContentScriptEntryPath;
|
|
6
|
+
private createBundleConfigHash;
|
|
7
|
+
private createContentScriptCacheKey;
|
|
8
|
+
private toProcessedAsset;
|
|
9
|
+
private removeContentScriptEntry;
|
|
4
10
|
processGrouped(deps: ContentScriptAsset[]): Promise<ProcessedAsset[]>;
|
|
11
|
+
private getGroupedBundlerOptions;
|
|
5
12
|
process(dep: ContentScriptAsset): Promise<ProcessedAsset>;
|
|
6
13
|
}
|
package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js
CHANGED
|
@@ -1,7 +1,50 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileSystem } from "@ecopages/file-system";
|
|
3
|
+
import { shouldUseDevBrowserScriptCache } from "../../../../../build/dev-browser-script-cache.js";
|
|
3
4
|
import { BaseScriptProcessor } from "../base/base-script-processor.js";
|
|
4
5
|
class ContentScriptProcessor extends BaseScriptProcessor {
|
|
6
|
+
getContentScriptEntryDir() {
|
|
7
|
+
const dir = path.join(this.appConfig.absolutePaths.workDir, "content-script-entries");
|
|
8
|
+
fileSystem.ensureDir(dir);
|
|
9
|
+
return dir;
|
|
10
|
+
}
|
|
11
|
+
getContentScriptEntryPath(contentHash) {
|
|
12
|
+
return path.join(this.getContentScriptEntryDir(), `${contentHash}.js`);
|
|
13
|
+
}
|
|
14
|
+
createBundleConfigHash(dep, shouldBundle) {
|
|
15
|
+
return this.generateHash(
|
|
16
|
+
JSON.stringify({
|
|
17
|
+
bundle: shouldBundle,
|
|
18
|
+
minify: shouldBundle && this.isProduction,
|
|
19
|
+
opts: dep.bundleOptions
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
createContentScriptCacheKey(dep, shouldBundle) {
|
|
24
|
+
const contentHash = this.generateHash(dep.content);
|
|
25
|
+
const configHash = this.createBundleConfigHash(dep, shouldBundle);
|
|
26
|
+
return `${this.buildCacheKey(`content-script:${contentHash}`, contentHash, dep)}:${configHash}`;
|
|
27
|
+
}
|
|
28
|
+
toProcessedAsset(dep, filepath, inlineContent) {
|
|
29
|
+
return {
|
|
30
|
+
filepath,
|
|
31
|
+
content: dep.inline ? inlineContent : void 0,
|
|
32
|
+
kind: "script",
|
|
33
|
+
position: dep.position,
|
|
34
|
+
attributes: dep.attributes,
|
|
35
|
+
inline: dep.inline,
|
|
36
|
+
excludeFromHtml: dep.excludeFromHtml,
|
|
37
|
+
packageRole: dep.packageRole,
|
|
38
|
+
groupedBundle: dep.groupedBundle,
|
|
39
|
+
bundledSourceFilepaths: dep.bundledSourceFilepaths
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
removeContentScriptEntry(contentHash) {
|
|
43
|
+
if (shouldUseDevBrowserScriptCache()) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
fileSystem.remove(this.getContentScriptEntryPath(contentHash));
|
|
47
|
+
}
|
|
5
48
|
async processGrouped(deps) {
|
|
6
49
|
if (deps.length === 0) {
|
|
7
50
|
return [];
|
|
@@ -10,109 +53,92 @@ class ContentScriptProcessor extends BaseScriptProcessor {
|
|
|
10
53
|
if (!shouldBundle || deps.some((dep) => dep.inline)) {
|
|
11
54
|
return Promise.all(deps.map((dep) => this.process(dep)));
|
|
12
55
|
}
|
|
13
|
-
|
|
14
|
-
this.appConfig.absolutePaths.distDir,
|
|
15
|
-
`grouped-script-entries-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
16
|
-
);
|
|
17
|
-
fileSystem.ensureDir(tempDir);
|
|
56
|
+
let tempEntries = [];
|
|
18
57
|
try {
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const tempFilepath =
|
|
58
|
+
tempEntries = deps.map((dep) => {
|
|
59
|
+
const contentHash = this.generateHash(dep.content);
|
|
60
|
+
const tempFilepath = this.getContentScriptEntryPath(contentHash);
|
|
22
61
|
fileSystem.write(tempFilepath, dep.content);
|
|
23
62
|
return {
|
|
24
63
|
dep,
|
|
25
|
-
|
|
64
|
+
contentHash,
|
|
26
65
|
tempFilepath
|
|
27
66
|
};
|
|
28
67
|
});
|
|
29
|
-
const primaryDep = deps[0];
|
|
30
68
|
const outputPaths = await this.bundleScripts({
|
|
31
|
-
...this.
|
|
32
|
-
entries: tempEntries.map(({
|
|
33
|
-
entryName,
|
|
69
|
+
...this.getGroupedBundlerOptions(deps),
|
|
70
|
+
entries: tempEntries.map(({ dep, contentHash, tempFilepath }) => ({
|
|
71
|
+
entryName: dep.groupedBundle?.entryName ?? dep.name ?? contentHash,
|
|
34
72
|
entrypoint: tempFilepath
|
|
35
73
|
})),
|
|
36
74
|
outdir: this.getAssetsDir(),
|
|
37
75
|
minify: this.isProduction,
|
|
38
76
|
naming: "[name]-[hash].[ext]"
|
|
39
77
|
});
|
|
40
|
-
return tempEntries.map(({ dep,
|
|
78
|
+
return tempEntries.map(({ dep, contentHash }) => {
|
|
79
|
+
const entryName = dep.groupedBundle?.entryName ?? dep.name ?? contentHash;
|
|
41
80
|
const bundledFilePath = outputPaths.get(entryName);
|
|
42
81
|
if (!bundledFilePath) {
|
|
43
82
|
throw new Error(`Missing grouped bundle output for ${entryName}`);
|
|
44
83
|
}
|
|
45
|
-
return
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
attributes: dep.attributes,
|
|
51
|
-
inline: dep.inline,
|
|
52
|
-
excludeFromHtml: dep.excludeFromHtml,
|
|
53
|
-
packageRole: dep.packageRole,
|
|
54
|
-
groupedBundle: dep.groupedBundle,
|
|
55
|
-
bundledSourceFilepaths: dep.bundledSourceFilepaths
|
|
56
|
-
};
|
|
84
|
+
return this.toProcessedAsset(
|
|
85
|
+
dep,
|
|
86
|
+
bundledFilePath,
|
|
87
|
+
dep.inline ? fileSystem.readFileSync(bundledFilePath).toString() : void 0
|
|
88
|
+
);
|
|
57
89
|
});
|
|
58
90
|
} finally {
|
|
59
|
-
|
|
91
|
+
for (const { contentHash } of tempEntries) {
|
|
92
|
+
this.removeContentScriptEntry(contentHash);
|
|
93
|
+
}
|
|
60
94
|
}
|
|
61
95
|
}
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const unbundledProcessedAsset = {
|
|
70
|
-
filepath,
|
|
71
|
-
content: dep.inline ? dep.content : void 0,
|
|
72
|
-
kind: "script",
|
|
73
|
-
position: dep.position,
|
|
74
|
-
attributes: dep.attributes,
|
|
75
|
-
inline: dep.inline,
|
|
76
|
-
excludeFromHtml: dep.excludeFromHtml,
|
|
77
|
-
packageRole: dep.packageRole,
|
|
78
|
-
groupedBundle: dep.groupedBundle,
|
|
79
|
-
bundledSourceFilepaths: dep.bundledSourceFilepaths
|
|
80
|
-
};
|
|
81
|
-
this.writeCacheFile(filename, unbundledProcessedAsset);
|
|
82
|
-
return unbundledProcessedAsset;
|
|
83
|
-
}
|
|
84
|
-
if (dep.content) {
|
|
85
|
-
const tempDir = this.appConfig.absolutePaths.distDir;
|
|
86
|
-
fileSystem.ensureDir(tempDir);
|
|
87
|
-
const tempFileName = path.join(
|
|
88
|
-
tempDir,
|
|
89
|
-
`${path.parse(filename).name}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp.js`
|
|
90
|
-
);
|
|
91
|
-
fileSystem.write(tempFileName, dep.content);
|
|
92
|
-
const bundledFilePath = await this.bundleScript({
|
|
93
|
-
entrypoint: tempFileName,
|
|
94
|
-
outdir: this.getAssetsDir(),
|
|
95
|
-
minify: this.isProduction,
|
|
96
|
-
naming: `${path.parse(filename).name}-[hash].[ext]`,
|
|
97
|
-
...this.getBundlerOptions(dep)
|
|
98
|
-
});
|
|
99
|
-
const processedAsset = {
|
|
100
|
-
filepath: bundledFilePath,
|
|
101
|
-
content: dep.inline ? fileSystem.readFileSync(bundledFilePath).toString() : void 0,
|
|
102
|
-
kind: "script",
|
|
103
|
-
position: dep.position,
|
|
104
|
-
attributes: dep.attributes,
|
|
105
|
-
inline: dep.inline,
|
|
106
|
-
excludeFromHtml: dep.excludeFromHtml,
|
|
107
|
-
packageRole: dep.packageRole,
|
|
108
|
-
groupedBundle: dep.groupedBundle,
|
|
109
|
-
bundledSourceFilepaths: dep.bundledSourceFilepaths
|
|
96
|
+
getGroupedBundlerOptions(deps) {
|
|
97
|
+
const primaryDep = deps[0];
|
|
98
|
+
const options = this.getBundlerOptions(primaryDep);
|
|
99
|
+
if (deps.some((dep) => dep.bundleOptions?.splitting === false)) {
|
|
100
|
+
return {
|
|
101
|
+
...options,
|
|
102
|
+
splitting: false
|
|
110
103
|
};
|
|
111
|
-
fileSystem.remove(tempFileName);
|
|
112
|
-
this.writeCacheFile(filename, processedAsset);
|
|
113
|
-
return processedAsset;
|
|
114
104
|
}
|
|
115
|
-
|
|
105
|
+
return options;
|
|
106
|
+
}
|
|
107
|
+
async process(dep) {
|
|
108
|
+
const shouldBundle = this.shouldBundle(dep);
|
|
109
|
+
const cacheKey = this.createContentScriptCacheKey(dep, shouldBundle);
|
|
110
|
+
return this.getOrProcess(cacheKey, async () => {
|
|
111
|
+
const hash = this.generateHash(dep.content);
|
|
112
|
+
const filename = dep.name ? `${dep.name}.js` : `script-${hash}.js`;
|
|
113
|
+
const filepath = path.join(this.getAssetsDir(), "scripts", filename);
|
|
114
|
+
if (!shouldBundle) {
|
|
115
|
+
if (!dep.inline) {
|
|
116
|
+
fileSystem.write(filepath, dep.content);
|
|
117
|
+
}
|
|
118
|
+
return this.toProcessedAsset(dep, filepath, dep.inline ? dep.content : void 0);
|
|
119
|
+
}
|
|
120
|
+
if (!dep.content) {
|
|
121
|
+
throw new Error("No content found for script asset");
|
|
122
|
+
}
|
|
123
|
+
const entryPath = this.getContentScriptEntryPath(hash);
|
|
124
|
+
fileSystem.write(entryPath, dep.content);
|
|
125
|
+
try {
|
|
126
|
+
const bundledFilePath = await this.bundleScript({
|
|
127
|
+
entrypoint: entryPath,
|
|
128
|
+
outdir: this.getAssetsDir(),
|
|
129
|
+
minify: this.isProduction,
|
|
130
|
+
naming: `${path.parse(filename).name}-[hash].[ext]`,
|
|
131
|
+
...this.getBundlerOptions(dep)
|
|
132
|
+
});
|
|
133
|
+
return this.toProcessedAsset(
|
|
134
|
+
dep,
|
|
135
|
+
bundledFilePath,
|
|
136
|
+
dep.inline ? fileSystem.readFileSync(bundledFilePath).toString() : void 0
|
|
137
|
+
);
|
|
138
|
+
} finally {
|
|
139
|
+
this.removeContentScriptEntry(hash);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
116
142
|
}
|
|
117
143
|
}
|
|
118
144
|
export {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AnyIntegrationPlugin } from '../../../plugins/integration-plugin.js';
|
|
2
|
+
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
3
|
+
/** Resolves the integration plugin that owns one asset-processing batch key. */
|
|
4
|
+
export declare function resolveIntegrationPluginForProcessingKey(appConfig: EcoPagesAppConfig, processingKey: string): AnyIntegrationPlugin | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function resolveIntegrationPluginForProcessingKey(appConfig, processingKey) {
|
|
2
|
+
if (!appConfig.integrations?.length) {
|
|
3
|
+
return void 0;
|
|
4
|
+
}
|
|
5
|
+
const exactMatch = appConfig.integrations.find((integration) => integration.name === processingKey);
|
|
6
|
+
if (exactMatch) {
|
|
7
|
+
return exactMatch;
|
|
8
|
+
}
|
|
9
|
+
const baseName = processingKey.split(":")[0];
|
|
10
|
+
if (!baseName || baseName === processingKey) {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
return appConfig.integrations.find((integration) => integration.name === baseName);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
resolveIntegrationPluginForProcessingKey
|
|
17
|
+
};
|
|
@@ -2,7 +2,6 @@ import type { AssetProcessor } from './processor.interface.js';
|
|
|
2
2
|
import type { AssetDefinition, ProcessedAsset } from './assets.types.js';
|
|
3
3
|
type ProcessUngroupedDependencyOptions = {
|
|
4
4
|
dep: AssetDefinition;
|
|
5
|
-
key: string;
|
|
6
5
|
depKey: string;
|
|
7
6
|
getCachedAsset: (dep: AssetDefinition, depKey: string) => ProcessedAsset | null;
|
|
8
7
|
getProcessor: (dep: AssetDefinition) => AssetProcessor | undefined;
|