diff --git a/packages/compiler-cli/test/ngtsc/hmr_spec.ts b/packages/compiler-cli/test/ngtsc/hmr_spec.ts index 2d47acfdd42..bc087c10be5 100644 --- a/packages/compiler-cli/test/ngtsc/hmr_spec.ts +++ b/packages/compiler-cli/test/ngtsc/hmr_spec.ts @@ -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)', ); expect(jsContents).toContain( ').then(m => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [i0], ' + @@ -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], ' + diff --git a/packages/compiler/src/render3/r3_hmr_compiler.ts b/packages/compiler/src/render3/r3_hmr_compiler.ts index db916e07915..6250c838219 100644 --- a/packages/compiler/src/render3/r3_hmr_compiler.ts +++ b/packages/compiler/src/render3/r3_hmr_compiler.ts @@ -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)); - // '?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, @@ -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(), diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 63f5445255c..306d2bb4d03 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -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}; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 75d3b9bea68..0ccfa68ba7a 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -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'; diff --git a/packages/core/src/render3/hmr.ts b/packages/core/src/render3/hmr.ts index 527f46415d0..047b12b96f7 100644 --- a/packages/core/src/render3/hmr.ts +++ b/packages/core/src/render3/hmr.ts @@ -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. diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 0cc6b79d91e..7b95032e8b3 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -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'; diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 6724f3c0a9f..e0fce28c036 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -219,4 +219,5 @@ export const angularCoreEnv: {[name: string]: unknown} = (() => ({ 'ɵɵtwoWayListener': r3.ɵɵtwoWayListener, 'ɵɵreplaceMetadata': r3.ɵɵreplaceMetadata, + 'ɵɵgetReplaceMetadataURL': r3.ɵɵgetReplaceMetadataURL, }))();