Skip to content

Commit f6ef3de

Browse files
committed
fixup! fix(@angular/build): strip all vite id prefixes from minified code with external dependencies
1 parent df0fed7 commit f6ef3de

2 files changed

Lines changed: 40 additions & 58 deletions

File tree

packages/angular/build/src/tools/vite/plugins/id-prefix-plugin.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,7 @@ export function createRemoveIdPrefixPlugin(externals: string[]): Plugin {
2929
return;
3030
}
3131

32-
// The path suffix is bounded so that a match can never extend past the end of an
33-
// import specifier string literal. With a greedy `.+`, minified (single-line) code
34-
// would let the first match consume the remainder of the line, leaving all later
35-
// `/@id/` occurrences on that line unstripped.
36-
const escapedExternals = externals.map(
37-
(e) => escapeRegexSpecialChars(e) + '(?:/[^\'"`\\s]+)?',
38-
);
39-
const prefixedExternalRegex = new RegExp(
40-
`${resolvedConfig.base}${VITE_ID_PREFIX}(${escapedExternals.join('|')})`,
41-
'g',
42-
);
32+
const transformFn = createTransformer(resolvedConfig.base, externals);
4333

4434
// @ts-expect-error: Property 'push' does not exist on type 'readonly Plugin<any>[]'
4535
// Reasoning:
@@ -48,15 +38,34 @@ export function createRemoveIdPrefixPlugin(externals: string[]): Plugin {
4838
// AFTER the import-analysis.
4939
resolvedConfig.plugins.push({
5040
name: 'angular-plugin-remove-id-prefix-transform',
51-
transform: (code: string) => {
52-
// don't do anything when code does not contain the Vite prefix
53-
if (!code.includes(VITE_ID_PREFIX)) {
54-
return code;
55-
}
56-
57-
return code.replace(prefixedExternalRegex, (_, externalName) => externalName);
58-
},
41+
transform: transformFn,
5942
});
6043
},
6144
};
6245
}
46+
47+
/**
48+
* Creates a transform function that removes the Vite ID prefix from externals.
49+
* @param base The base path of the application.
50+
* @param externals The external package names.
51+
* @returns A function that transforms code by removing the Vite ID prefix.
52+
*/
53+
export function createTransformer(base: string, externals: string[]): (code: string) => string {
54+
// The path suffix is bounded so that a match can never extend past the end of an
55+
// import specifier string literal. With a greedy `.+`, minified (single-line) code
56+
// would let the first match consume the remainder of the line, leaving all later
57+
// `/@id/` occurrences on that line unstripped.
58+
const escapedExternals = externals.map((e) => escapeRegexSpecialChars(e) + '(?:/[^\'"`\\s]+)?');
59+
60+
const prefixedExternalRegex = new RegExp(
61+
`${base}${VITE_ID_PREFIX}(${escapedExternals.join('|')})`,
62+
'g',
63+
);
64+
65+
return (code: string) => {
66+
return code.includes(VITE_ID_PREFIX)
67+
? code.replace(prefixedExternalRegex, (_, externalName) => externalName)
68+
: // don't do anything when code does not contain the Vite prefix
69+
code;
70+
};
71+
}

packages/angular/build/src/tools/vite/plugins/id-prefix-plugin_spec.ts

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,16 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { createRemoveIdPrefixPlugin } from './id-prefix-plugin';
9+
import { createTransformer } from './id-prefix-plugin';
1010

11-
/**
12-
* Runs the plugin's `configResolved` hook against a minimal fake resolved
13-
* configuration and returns the transform function of the plugin it registers.
14-
*/
15-
function getTransform(externals: string[], base: string): (code: string) => string {
16-
const plugin = createRemoveIdPrefixPlugin(externals);
17-
const resolvedConfig = {
18-
base,
19-
plugins: [] as { name: string; transform?: (code: string) => string }[],
20-
};
21-
22-
(plugin.configResolved as (config: unknown) => void)(resolvedConfig);
23-
24-
const pushedPlugin = resolvedConfig.plugins[0];
25-
expect(pushedPlugin?.transform).toBeDefined();
26-
27-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28-
return pushedPlugin.transform!;
29-
}
30-
31-
describe('createRemoveIdPrefixPlugin', () => {
11+
describe('createTransformer', () => {
3212
it('should strip the prefix from every occurrence on a single (minified) line', () => {
33-
const transform = getTransform(
34-
['@angular/common', '@angular/common/http', '@angular/core', '@angular/router'],
35-
'/',
36-
);
13+
const transform = createTransformer('/', [
14+
'@angular/common',
15+
'@angular/common/http',
16+
'@angular/core',
17+
'@angular/router',
18+
]);
3719

3820
const minified =
3921
'import{a}from"/@id/@angular/common/http";' +
@@ -48,23 +30,23 @@ describe('createRemoveIdPrefixPlugin', () => {
4830
});
4931

5032
it('should strip the prefix from an external with a deep import path', () => {
51-
const transform = getTransform(['@angular/common'], '/');
33+
const transform = createTransformer('/', ['@angular/common']);
5234

5335
expect(transform('import{h}from"/@id/@angular/common/http";')).toBe(
5436
'import{h}from"@angular/common/http";',
5537
);
5638
});
5739

5840
it('should strip the prefix when a non-root base is configured', () => {
59-
const transform = getTransform(['@angular/router'], '/app/');
41+
const transform = createTransformer('/app/', ['@angular/router']);
6042

6143
expect(transform('import{r}from"/app/@id/@angular/router";')).toBe(
6244
'import{r}from"@angular/router";',
6345
);
6446
});
6547

6648
it('should strip the prefix from multi-line (unminified) code', () => {
67-
const transform = getTransform(['@angular/common', '@angular/router'], '/');
49+
const transform = createTransformer('/', ['@angular/common', '@angular/router']);
6850

6951
const code =
7052
'import { CommonModule } from "/@id/@angular/common";\n' +
@@ -77,18 +59,9 @@ describe('createRemoveIdPrefixPlugin', () => {
7759
});
7860

7961
it('should not modify imports that are not configured externals', () => {
80-
const transform = getTransform(['@angular/router'], '/');
62+
const transform = createTransformer('/', ['@angular/router']);
8163

8264
const code = 'import{x}from"/@id/some-other-package";';
8365
expect(transform(code)).toBe(code);
8466
});
85-
86-
it('should not register a transform when there are no externals', () => {
87-
const plugin = createRemoveIdPrefixPlugin([]);
88-
const resolvedConfig = { base: '/', plugins: [] };
89-
90-
(plugin.configResolved as (config: unknown) => void)(resolvedConfig);
91-
92-
expect(resolvedConfig.plugins).toHaveSize(0);
93-
});
9467
});

0 commit comments

Comments
 (0)