Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions 4 packages/compiler-cli/test/ngtsc/hmr_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ runInEachFileSystem(() => {
expect(jsContents).toContain('const id = "test.ts%40Cmp";');
expect(jsContents).toContain('function Cmp_HmrLoad(t) {');
expect(jsContents).toContain(
'import(/* @vite-ignore */\nnew URL("./@ng/component?c=" + id + "&t=" + encodeURIComponent(t), import.meta.url).href)',
'import(/* @vite-ignore */\ni0.ɵɵgetReplaceMetadataURL(id, t, import.meta.url)',
Copy link
Member Author

@crisbeto crisbeto May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could've fixed the bug by changing this to new globalThis.URL as well, but I feel like the dedicated function gives us more flexibility anyways.

);
expect(jsContents).toContain(
').then(m => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [i0], ' +
Expand Down Expand Up @@ -183,7 +183,7 @@ runInEachFileSystem(() => {
expect(jsContents).toContain('const id = "test.ts%40Cmp";');
expect(jsContents).toContain('function Cmp_HmrLoad(t) {');
expect(jsContents).toContain(
'import(/* @vite-ignore */\nnew URL("./@ng/component?c=" + id + "&t=" + encodeURIComponent(t), import.meta.url).href)',
'import(/* @vite-ignore */\ni0.ɵɵgetReplaceMetadataURL(id, t, import.meta.url)',
);
expect(jsContents).toContain(
').then(m => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [i0, i1], ' +
Expand Down
24 changes: 10 additions & 14 deletions 24 packages/compiler/src/render3/r3_hmr_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,17 @@ export function compileHmrInitializer(meta: R3HmrMetadata): o.Expression {
// (m) => m.default && ɵɵreplaceMetadata(...)
const replaceCallback = o.arrowFn([new o.FnParam(moduleName)], defaultRead.and(replaceCall));

// '<url>?c=' + id + '&t=' + encodeURIComponent(t)
const urlValue = o
.literal(`./@ng/component?c=`)
.plus(o.variable(idName))
.plus(o.literal('&t='))
.plus(o.variable('encodeURIComponent').callFn([o.variable(timestampName)]));

// import.meta.url
const urlBase = o.variable('import').prop('meta').prop('url');

// new URL(urlValue, urlBase).href
const urlHref = new o.InstantiateExpr(o.variable('URL'), [urlValue, urlBase]).prop('href');
// getReplaceMetadataURL(id, timestamp, import.meta.url)
const url = o
.importExpr(R3.getReplaceMetadataURL)
.callFn([
o.variable(idName),
o.variable(timestampName),
o.variable('import').prop('meta').prop('url'),
]);

// function Cmp_HmrLoad(t) {
// import(/* @vite-ignore */ urlHref).then((m) => m.default && replaceMetadata(...));
// import(/* @vite-ignore */ url).then((m) => m.default && replaceMetadata(...));
// }
const importCallback = new o.DeclareFunctionStmt(
importCallbackName,
Expand All @@ -103,7 +99,7 @@ export function compileHmrInitializer(meta: R3HmrMetadata): o.Expression {
// The vite-ignore special comment is required to prevent Vite from generating a superfluous
// warning for each usage within the development code. If Vite provides a method to
// programmatically avoid this warning in the future, this added comment can be removed here.
new o.DynamicImportExpr(urlHref, null, '@vite-ignore')
new o.DynamicImportExpr(url, null, '@vite-ignore')
.prop('then')
.callFn([replaceCallback])
.toStmt(),
Expand Down
4 changes: 4 additions & 0 deletions 4 packages/compiler/src/render3/r3_identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ export class Identifiers {
static resolveForwardRef: o.ExternalReference = {name: 'resolveForwardRef', moduleName: CORE};

static replaceMetadata: o.ExternalReference = {name: 'ɵɵreplaceMetadata', moduleName: CORE};
static getReplaceMetadataURL: o.ExternalReference = {
name: 'ɵɵgetReplaceMetadataURL',
moduleName: CORE,
};

static ɵɵdefineInjectable: o.ExternalReference = {name: 'ɵɵdefineInjectable', moduleName: CORE};
static declareInjectable: o.ExternalReference = {name: 'ɵɵngDeclareInjectable', moduleName: CORE};
Expand Down
1 change: 1 addition & 0 deletions 1 packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ export {
ɵɵstoreLet,
ɵɵreadContextLet,
ɵɵreplaceMetadata,
ɵɵgetReplaceMetadataURL,
ɵɵattachSourceLocations,
} from './render3/index';
export {CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET} from './render3/interfaces/container';
Expand Down
13 changes: 13 additions & 0 deletions 13 packages/core/src/render3/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ type ImportMetaExtended = ImportMeta & {
};
};

/**
* Gets the URL from which the client will fetch a new version of a component's metadata so it
* can be replaced during hot module reloading.
* @param id Unique ID for the component, generated during compile time.
* @param timestamp Time at which the request happened.
* @param base Base URL against which to resolve relative paths.
* @codeGenApi
*/
export function ɵɵgetReplaceMetadataURL(id: string, timestamp: string, base: string): string {
const url = `./@ng/component?c=${id}&t=${encodeURIComponent(timestamp)}`;
return new URL(url, base).href;
}

/**
* Replaces the metadata of a component type and re-renders all live instances of the component.
* @param type Class whose metadata will be replaced.
Expand Down
2 changes: 1 addition & 1 deletion 2 packages/core/src/render3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export {ɵɵresolveBody, ɵɵresolveDocument, ɵɵresolveWindow} from './util/mi
export {ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound';
export {ɵɵgetComponentDepsFactory} from './local_compilation';
export {ɵsetClassDebugInfo} from './debug/set_debug_info';
export {ɵɵreplaceMetadata} from './hmr';
export {ɵɵreplaceMetadata, ɵɵgetReplaceMetadataURL} from './hmr';

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

Expand Down
1 change: 1 addition & 0 deletions 1 packages/core/src/render3/jit/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,5 @@ export const angularCoreEnv: {[name: string]: unknown} = (() => ({
'ɵɵtwoWayListener': r3.ɵɵtwoWayListener,

'ɵɵreplaceMetadata': r3.ɵɵreplaceMetadata,
'ɵɵgetReplaceMetadataURL': r3.ɵɵgetReplaceMetadataURL,
}))();
Morty Proxy This is a proxified and sanitized view of the page, visit original site.