@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.
Files changed (48) hide show
  1. package/package.json +13 -3
  2. package/src/adapters/abstract/application-adapter.js +3 -0
  3. package/src/adapters/bun/create-app.js +2 -0
  4. package/src/adapters/node/create-app.js +2 -0
  5. package/src/adapters/shared/server-adapter.d.ts +1 -0
  6. package/src/adapters/shared/server-adapter.js +31 -22
  7. package/src/build/app-build-manifest-runtime.js +12 -1
  8. package/src/build/dev-browser-script-cache.d.ts +34 -0
  9. package/src/build/dev-browser-script-cache.js +90 -0
  10. package/src/build/lit-static-render-worker-context.d.ts +2 -0
  11. package/src/build/lit-static-render-worker-context.js +6 -0
  12. package/src/diagnostics/request-build-dedupe.d.ts +18 -0
  13. package/src/diagnostics/request-build-dedupe.js +33 -0
  14. package/src/diagnostics/startup-trace.d.ts +25 -0
  15. package/src/diagnostics/startup-trace.js +121 -0
  16. package/src/env.d.ts +2 -0
  17. package/src/index.d.ts +1 -0
  18. package/src/index.js +4 -1
  19. package/src/plugins/integration-plugin.d.ts +8 -0
  20. package/src/plugins/processor.d.ts +6 -0
  21. package/src/plugins/processor.js +2 -1
  22. package/src/route-renderer/orchestration/page-browser-graph.service.js +15 -9
  23. package/src/route-renderer/page-loading/ecopages-virtual-imports.d.ts +6 -0
  24. package/src/route-renderer/page-loading/ecopages-virtual-imports.js +7 -2
  25. package/src/route-renderer/page-loading/module-declaration-aggregation.js +4 -0
  26. package/src/services/assets/asset-processing-service/asset-processing.service.d.ts +3 -0
  27. package/src/services/assets/asset-processing-service/asset-processing.service.js +41 -4
  28. package/src/services/assets/asset-processing-service/finalize-processed-asset.d.ts +3 -0
  29. package/src/services/assets/asset-processing-service/finalize-processed-asset.js +7 -0
  30. package/src/services/assets/asset-processing-service/grouped-content-bundles.d.ts +2 -1
  31. package/src/services/assets/asset-processing-service/grouped-content-bundles.js +20 -10
  32. package/src/services/assets/asset-processing-service/index.d.ts +1 -0
  33. package/src/services/assets/asset-processing-service/index.js +1 -0
  34. package/src/services/assets/asset-processing-service/materialize-content-script-asset.d.ts +11 -0
  35. package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +17 -0
  36. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.d.ts +7 -0
  37. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +105 -79
  38. package/src/services/assets/asset-processing-service/resolve-integration-plugin.d.ts +4 -0
  39. package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +17 -0
  40. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.d.ts +0 -1
  41. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +5 -10
  42. package/src/services/assets/browser-bundle.service.js +10 -2
  43. package/src/services/validation/schema-validation-service.d.ts +1 -4
  44. package/src/services/validation/schema-validation-service.js +12 -13
  45. package/src/services/validation/standard-schema.types.d.ts +9 -62
  46. package/src/services/validation/validate-standard-schema.d.ts +7 -0
  47. package/src/services/validation/validate-standard-schema.js +14 -0
  48. package/src/types/public-types.d.ts +2 -2
@@ -155,5 +155,6 @@ class Processor {
155
155
  export {
156
156
  PROCESSOR_ERRORS,
157
157
  Processor,
158
- mergeProcessorOptions
158
+ mergeProcessorOptions,
159
+ resolveGeneratedPath
159
160
  };
@@ -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
- groupedAssetsByRoute.set(
134
- routeFile,
135
- processedGroupedDependencies.filter((asset) => {
136
- if (!asset.groupedBundle) {
137
- return false;
138
- }
139
- return groupedAssetKeys.has(getGroupedBundleAssetKey(asset.groupedBundle));
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.startsWith("ecopages:")) continue;
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 results = await this.processDependenciesParallel(dedupedDeps, key);
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, key) {
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;
@@ -0,0 +1,7 @@
1
+ function finalizeProcessedAsset(processed, resolveProcessedAssetSrcUrl) {
2
+ const srcUrl = resolveProcessedAssetSrcUrl(processed);
3
+ return srcUrl ? { ...processed, srcUrl } : processed;
4
+ }
5
+ export {
6
+ finalizeProcessedAsset
7
+ };
@@ -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 ? { key, ...cached } : null;
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 srcUrl = resolveProcessedAssetSrcUrl(processed);
47
- const processedWithKey = {
48
- key,
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
  };
@@ -1,4 +1,5 @@
1
1
  export * from './asset.factory.js';
2
+ export * from './grouped-content-bundles.js';
2
3
  export * from './page-package.js';
3
4
  export * from './asset-processing.service.js';
4
5
  export * from './assets.types.js';
@@ -1,4 +1,5 @@
1
1
  export * from "./asset.factory.js";
2
+ export * from "./grouped-content-bundles.js";
2
3
  export * from "./page-package.js";
3
4
  export * from "./asset-processing.service.js";
4
5
  export * from "./assets.types.js";
@@ -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
+ };
@@ -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
  }
@@ -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
- const tempDir = path.join(
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
- const tempEntries = deps.map((dep, index) => {
20
- const entryName = dep.groupedBundle?.entryName ?? dep.name ?? `grouped-script-${index}`;
21
- const tempFilepath = path.join(tempDir, `${entryName}.js`);
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
- entryName,
64
+ contentHash,
26
65
  tempFilepath
27
66
  };
28
67
  });
29
- const primaryDep = deps[0];
30
68
  const outputPaths = await this.bundleScripts({
31
- ...this.getBundlerOptions(primaryDep),
32
- entries: tempEntries.map(({ entryName, tempFilepath }) => ({
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, entryName }) => {
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
- filepath: bundledFilePath,
47
- content: dep.inline ? fileSystem.readFileSync(bundledFilePath).toString() : void 0,
48
- kind: "script",
49
- position: dep.position,
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
- fileSystem.remove(tempDir);
91
+ for (const { contentHash } of tempEntries) {
92
+ this.removeContentScriptEntry(contentHash);
93
+ }
60
94
  }
61
95
  }
62
- async process(dep) {
63
- const hash = this.generateHash(dep.content);
64
- const filename = dep.name ? `${dep.name}.js` : `script-${hash}.js`;
65
- const shouldBundle = this.shouldBundle(dep);
66
- const filepath = path.join(this.getAssetsDir(), "scripts", filename);
67
- if (!shouldBundle) {
68
- if (!dep.inline) fileSystem.write(filepath, dep.content);
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
- throw new Error("No content found for script asset");
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;