Skip to content

Commit 44bb328

Browse files
crisbetothePunderWoman
authored andcommitted
fix(compiler): avoid conflicts between HMR code and local symbols (#61550)
Currently we construct the HMR replacement URL inline by calling into the native `URL` constructor. This can cause conflicts with user code that defines a symbol called `URL`. These changes resolve the issue by moving the URL construction into a separate function. This has a secondary benefit of making the generated code easier to follow and allowing us to update the URL without changing the compiled code. Fixes #61517. PR Close #61550
1 parent 8f9b05e commit 44bb328

File tree

7 files changed

+32
-17
lines changed

7 files changed

+32
-17
lines changed

packages/compiler-cli/test/ngtsc/hmr_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ runInEachFileSystem(() => {
115115
expect(jsContents).toContain('const id = "test.ts%40Cmp";');
116116
expect(jsContents).toContain('function Cmp_HmrLoad(t) {');
117117
expect(jsContents).toContain(
118-
'import(/* @vite-ignore */\nnew URL("./@ng/component?c=" + id + "&t=" + encodeURIComponent(t), import.meta.url).href)',
118+
'import(/* @vite-ignore */\ni0.ɵɵgetReplaceMetadataURL(id, t, import.meta.url)',
119119
);
120120
expect(jsContents).toContain(
121121
').then(m => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [i0], ' +
@@ -183,7 +183,7 @@ runInEachFileSystem(() => {
183183
expect(jsContents).toContain('const id = "test.ts%40Cmp";');
184184
expect(jsContents).toContain('function Cmp_HmrLoad(t) {');
185185
expect(jsContents).toContain(
186-
'import(/* @vite-ignore */\nnew URL("./@ng/component?c=" + id + "&t=" + encodeURIComponent(t), import.meta.url).href)',
186+
'import(/* @vite-ignore */\ni0.ɵɵgetReplaceMetadataURL(id, t, import.meta.url)',
187187
);
188188
expect(jsContents).toContain(
189189
').then(m => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [i0, i1], ' +

packages/compiler/src/render3/r3_hmr_compiler.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,17 @@ export function compileHmrInitializer(meta: R3HmrMetadata): o.Expression {
8080
// (m) => m.default && ɵɵreplaceMetadata(...)
8181
const replaceCallback = o.arrowFn([new o.FnParam(moduleName)], defaultRead.and(replaceCall));
8282

83-
// '<url>?c=' + id + '&t=' + encodeURIComponent(t)
84-
const urlValue = o
85-
.literal(`./@ng/component?c=`)
86-
.plus(o.variable(idName))
87-
.plus(o.literal('&t='))
88-
.plus(o.variable('encodeURIComponent').callFn([o.variable(timestampName)]));
89-
90-
// import.meta.url
91-
const urlBase = o.variable('import').prop('meta').prop('url');
92-
93-
// new URL(urlValue, urlBase).href
94-
const urlHref = new o.InstantiateExpr(o.variable('URL'), [urlValue, urlBase]).prop('href');
83+
// getReplaceMetadataURL(id, timestamp, import.meta.url)
84+
const url = o
85+
.importExpr(R3.getReplaceMetadataURL)
86+
.callFn([
87+
o.variable(idName),
88+
o.variable(timestampName),
89+
o.variable('import').prop('meta').prop('url'),
90+
]);
9591

9692
// function Cmp_HmrLoad(t) {
97-
// import(/* @vite-ignore */ urlHref).then((m) => m.default && replaceMetadata(...));
93+
// import(/* @vite-ignore */ url).then((m) => m.default && replaceMetadata(...));
9894
// }
9995
const importCallback = new o.DeclareFunctionStmt(
10096
importCallbackName,
@@ -103,7 +99,7 @@ export function compileHmrInitializer(meta: R3HmrMetadata): o.Expression {
10399
// The vite-ignore special comment is required to prevent Vite from generating a superfluous
104100
// warning for each usage within the development code. If Vite provides a method to
105101
// programmatically avoid this warning in the future, this added comment can be removed here.
106-
new o.DynamicImportExpr(urlHref, null, '@vite-ignore')
102+
new o.DynamicImportExpr(url, null, '@vite-ignore')
107103
.prop('then')
108104
.callFn([replaceCallback])
109105
.toStmt(),

packages/compiler/src/render3/r3_identifiers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ export class Identifiers {
411411
static resolveForwardRef: o.ExternalReference = {name: 'resolveForwardRef', moduleName: CORE};
412412

413413
static replaceMetadata: o.ExternalReference = {name: 'ɵɵreplaceMetadata', moduleName: CORE};
414+
static getReplaceMetadataURL: o.ExternalReference = {
415+
name: 'ɵɵgetReplaceMetadataURL',
416+
moduleName: CORE,
417+
};
414418

415419
static ɵɵdefineInjectable: o.ExternalReference = {name: 'ɵɵdefineInjectable', moduleName: CORE};
416420
static declareInjectable: o.ExternalReference = {name: 'ɵɵngDeclareInjectable', moduleName: CORE};

packages/core/src/core_render3_private_export.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ export {
247247
ɵɵstoreLet,
248248
ɵɵreadContextLet,
249249
ɵɵreplaceMetadata,
250+
ɵɵgetReplaceMetadataURL,
250251
ɵɵattachSourceLocations,
251252
} from './render3/index';
252253
export {CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET} from './render3/interfaces/container';

packages/core/src/render3/hmr.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ type ImportMetaExtended = ImportMeta & {
5252
};
5353
};
5454

55+
/**
56+
* Gets the URL from which the client will fetch a new version of a component's metadata so it
57+
* can be replaced during hot module reloading.
58+
* @param id Unique ID for the component, generated during compile time.
59+
* @param timestamp Time at which the request happened.
60+
* @param base Base URL against which to resolve relative paths.
61+
* @codeGenApi
62+
*/
63+
export function ɵɵgetReplaceMetadataURL(id: string, timestamp: string, base: string): string {
64+
const url = `./@ng/component?c=${id}&t=${encodeURIComponent(timestamp)}`;
65+
return new URL(url, base).href;
66+
}
67+
5568
/**
5669
* Replaces the metadata of a component type and re-renders all live instances of the component.
5770
* @param type Class whose metadata will be replaced.

packages/core/src/render3/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ export {ɵɵresolveBody, ɵɵresolveDocument, ɵɵresolveWindow} from './util/mi
217217
export {ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound';
218218
export {ɵɵgetComponentDepsFactory} from './local_compilation';
219219
export {ɵsetClassDebugInfo} from './debug/set_debug_info';
220-
export {ɵɵreplaceMetadata} from './hmr';
220+
export {ɵɵreplaceMetadata, ɵɵgetReplaceMetadataURL} from './hmr';
221221

222222
export {store} from './util/view_utils';
223223

packages/core/src/render3/jit/environment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,5 @@ export const angularCoreEnv: {[name: string]: unknown} = (() => ({
217217
'ɵɵtwoWayListener': r3.ɵɵtwoWayListener,
218218

219219
'ɵɵreplaceMetadata': r3.ɵɵreplaceMetadata,
220+
'ɵɵgetReplaceMetadataURL': r3.ɵɵgetReplaceMetadataURL,
220221
}))();

0 commit comments

Comments
 (0)