Skip to content

Commit 4cf1aa3

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular/build): create shared utility for external metadata
The logic to process external metadata from build results was previously implemented directly within the dev-server. This commit extracts this logic into a shared utility function, `updateExternalMetadata`, and moves it to a common location. The dev-server is updated to use this new function. Additionally, the Vitest unit test builder is modified to leverage this utility, allowing it to correctly track external dependencies for Vite's dependency optimization.
1 parent 04fda13 commit 4cf1aa3

File tree

5 files changed

+79
-36
lines changed

5 files changed

+79
-36
lines changed

packages/angular/build/src/builders/dev-server/vite/index.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { join } from 'node:path';
1414
import type { Connect, ViteDevServer } from 'vite';
1515
import type { ComponentStyleRecord } from '../../../tools/vite/middlewares';
1616
import { ServerSsrMode } from '../../../tools/vite/plugins';
17-
import { EsbuildLoaderOption } from '../../../tools/vite/utils';
17+
import { EsbuildLoaderOption, updateExternalMetadata } from '../../../tools/vite/utils';
1818
import { normalizeSourceMaps } from '../../../utils';
1919
import { useComponentStyleHmr, useComponentTemplateHmr } from '../../../utils/environment-options';
2020
import { Result, ResultKind } from '../../application/results';
@@ -35,7 +35,6 @@ import {
3535
DevServerExternalResultMetadata,
3636
OutputAssetRecord,
3737
OutputFileRecord,
38-
isAbsoluteUrl,
3938
updateResultRecord,
4039
} from './utils';
4140

@@ -333,34 +332,7 @@ export async function* serveWithVite(
333332
}
334333

335334
// To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
336-
if (result.detail?.['externalMetadata']) {
337-
const { implicitBrowser, implicitServer, explicit } = result.detail[
338-
'externalMetadata'
339-
] as ExternalResultMetadata;
340-
const implicitServerFiltered = implicitServer.filter(
341-
(m) => !isBuiltin(m) && !isAbsoluteUrl(m),
342-
);
343-
const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m));
344-
345-
// Empty Arrays to avoid growing unlimited with every re-build.
346-
externalMetadata.explicitBrowser.length = 0;
347-
externalMetadata.explicitServer.length = 0;
348-
externalMetadata.implicitServer.length = 0;
349-
externalMetadata.implicitBrowser.length = 0;
350-
351-
const externalDeps = browserOptions.externalDependencies ?? [];
352-
externalMetadata.explicitBrowser.push(...explicit, ...externalDeps);
353-
externalMetadata.explicitServer.push(...explicit, ...externalDeps, ...builtinModules);
354-
externalMetadata.implicitServer.push(...implicitServerFiltered);
355-
externalMetadata.implicitBrowser.push(...implicitBrowserFiltered);
356-
357-
// The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm.
358-
// See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239
359-
externalMetadata.explicitBrowser.sort();
360-
externalMetadata.explicitServer.sort();
361-
externalMetadata.implicitServer.sort();
362-
externalMetadata.implicitBrowser.sort();
363-
}
335+
updateExternalMetadata(result, externalMetadata, browserOptions.externalDependencies);
364336

365337
if (server) {
366338
// Update fs allow list to include any new assets from the build option.

packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,8 @@ export async function getVitestBuildOptions(
9999
entryPoints.set('init-testbed', 'angular:test-bed-init');
100100

101101
const externalDependencies = new Set(['vitest']);
102-
if (!options.browsers?.length) {
103-
// Only add for non-browser setups.
104-
// Comprehensive browser prebundling will be handled separately.
105-
ANGULAR_PACKAGES_TO_EXTERNALIZE.forEach((dep) => externalDependencies.add(dep));
106-
}
102+
ANGULAR_PACKAGES_TO_EXTERNALIZE.forEach((dep) => externalDependencies.add(dep));
103+
107104
if (baseBuildOptions.externalDependencies) {
108105
baseBuildOptions.externalDependencies.forEach((dep) => externalDependencies.add(dep));
109106
}

packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
import type { BuilderOutput } from '@angular-devkit/architect';
1010
import assert from 'node:assert';
1111
import path from 'node:path';
12-
import { isMatch } from 'picomatch';
1312
import type { Vitest } from 'vitest/node';
13+
import {
14+
DevServerExternalResultMetadata,
15+
updateExternalMetadata,
16+
} from '../../../../tools/vite/utils';
1417
import { assertIsError } from '../../../../utils/error';
1518
import {
1619
type FullResult,
@@ -30,6 +33,12 @@ export class VitestExecutor implements TestExecutor {
3033
private readonly projectName: string;
3134
private readonly options: NormalizedUnitTestBuilderOptions;
3235
private readonly buildResultFiles = new Map<string, ResultFile>();
36+
private readonly externalMetadata: DevServerExternalResultMetadata = {
37+
implicitBrowser: [],
38+
implicitServer: [],
39+
explicitBrowser: [],
40+
explicitServer: [],
41+
};
3342

3443
// This is a reverse map of the entry points created in `build-options.ts`.
3544
// It is used by the in-memory provider plugin to map the requested test file
@@ -71,6 +80,8 @@ export class VitestExecutor implements TestExecutor {
7180
}
7281
}
7382

83+
updateExternalMetadata(buildResult, this.externalMetadata, undefined, true);
84+
7485
// Initialize Vitest if not already present.
7586
this.vitest ??= await this.initializeVitest();
7687
const vitest = this.vitest;
@@ -220,6 +231,7 @@ export class VitestExecutor implements TestExecutor {
220231
coverage,
221232
projectName,
222233
projectSourceRoot: this.options.projectSourceRoot,
234+
optimizeDepsInclude: this.externalMetadata.explicitBrowser,
223235
reporters,
224236
setupFiles: testSetupFiles,
225237
projectPlugins,

packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ interface VitestConfigPluginOptions {
4242
setupFiles: string[];
4343
projectPlugins: Exclude<UserWorkspaceConfig['plugins'], undefined>;
4444
include: string[];
45+
optimizeDepsInclude: string[];
4546
}
4647

4748
async function findTestEnvironment(
@@ -133,6 +134,7 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi
133134
},
134135
optimizeDeps: {
135136
noDiscovery: true,
137+
include: options.optimizeDepsInclude,
136138
},
137139
plugins: projectPlugins,
138140
};

packages/angular/build/src/tools/vite/utils.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
*/
88

99
import { lookup as lookupMimeType } from 'mrmime';
10+
import { builtinModules, isBuiltin } from 'node:module';
1011
import { extname } from 'node:path';
1112
import type { DepOptimizationConfig } from 'vite';
13+
import type { ExternalResultMetadata } from '../esbuild/bundler-execution-result';
1214
import { JavaScriptTransformer } from '../esbuild/javascript-transformer';
1315
import { getFeatureSupport } from '../esbuild/utils';
1416

@@ -109,3 +111,61 @@ export function getDepOptimizationConfig({
109111
},
110112
};
111113
}
114+
115+
export interface DevServerExternalResultMetadata {
116+
implicitBrowser: string[];
117+
implicitServer: string[];
118+
explicitBrowser: string[];
119+
explicitServer: string[];
120+
}
121+
122+
export function isAbsoluteUrl(url: string): boolean {
123+
try {
124+
new URL(url);
125+
126+
return true;
127+
} catch {
128+
return false;
129+
}
130+
}
131+
132+
export function updateExternalMetadata(
133+
result: { detail?: { externalMetadata?: ExternalResultMetadata } },
134+
externalMetadata: DevServerExternalResultMetadata,
135+
externalDependencies: string[] | undefined,
136+
explicitPackagesOnly: boolean = false,
137+
): void {
138+
if (!result.detail?.['externalMetadata']) {
139+
return;
140+
}
141+
142+
const { implicitBrowser, implicitServer, explicit } = result.detail['externalMetadata'];
143+
const implicitServerFiltered = implicitServer.filter((m) => !isBuiltin(m) && !isAbsoluteUrl(m));
144+
const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m));
145+
const explicitBrowserFiltered = explicitPackagesOnly
146+
? explicit.filter((m) => !isAbsoluteUrl(m))
147+
: explicit;
148+
149+
// Empty Arrays to avoid growing unlimited with every re-build.
150+
externalMetadata.explicitBrowser.length = 0;
151+
externalMetadata.explicitServer.length = 0;
152+
externalMetadata.implicitServer.length = 0;
153+
externalMetadata.implicitBrowser.length = 0;
154+
155+
const externalDeps = externalDependencies ?? [];
156+
externalMetadata.explicitBrowser.push(...explicitBrowserFiltered, ...externalDeps);
157+
externalMetadata.explicitServer.push(
158+
...explicitBrowserFiltered,
159+
...externalDeps,
160+
...builtinModules,
161+
);
162+
externalMetadata.implicitServer.push(...implicitServerFiltered);
163+
externalMetadata.implicitBrowser.push(...implicitBrowserFiltered);
164+
165+
// The below needs to be sorted as Vite uses these options as part of the hashing invalidation algorithm.
166+
// See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239
167+
externalMetadata.explicitBrowser.sort();
168+
externalMetadata.explicitServer.sort();
169+
externalMetadata.implicitServer.sort();
170+
externalMetadata.implicitBrowser.sort();
171+
}

0 commit comments

Comments
 (0)