From 6f7803a3ff653c0ad0ef74e4e6c3dfeb3ffb272f Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 10 Oct 2024 19:29:00 +0200 Subject: [PATCH] refactor(compiler): add logic to generate the HMR initializer (#58150) Adds the logic that will generate the `import` expression that will initializer HMR for a specific component. PR Close #58150 --- packages/compiler/src/compiler.ts | 1 + .../compiler/src/render3/r3_hmr_compiler.ts | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 packages/compiler/src/render3/r3_hmr_compiler.ts diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index 2ed8439c258..4485488a3b0 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -173,6 +173,7 @@ export { compileOpaqueAsyncClassMetadata, } from './render3/r3_class_metadata_compiler'; export {compileClassDebugInfo, R3ClassDebugInfo} from './render3/r3_class_debug_info_compiler'; +export {compileClassHmrInitializer, R3HmrInitializerMetadata} from './render3/r3_hmr_compiler'; export { compileFactoryFunction, R3DependencyMetadata, diff --git a/packages/compiler/src/render3/r3_hmr_compiler.ts b/packages/compiler/src/render3/r3_hmr_compiler.ts new file mode 100644 index 00000000000..5e3ae3a3096 --- /dev/null +++ b/packages/compiler/src/render3/r3_hmr_compiler.ts @@ -0,0 +1,67 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import * as o from '../output/output_ast'; +import {Identifiers as R3} from './r3_identifiers'; +import {devOnlyGuardedExpression} from './util'; + +/** Metadata necessary to compile the HMR initializer call. */ +export interface R3HmrInitializerMetadata { + /** Component class for which HMR is being enabled. */ + type: o.Expression; + + /** Name of the component class. */ + className: string; + + /** File path of the component class. */ + filePath: string; + + /** + * Timestamp when the compilation took place. + * Necessary to invalidate the browser cache. + */ + timestamp: string; +} + +/** Compiles the HMR initializer expression. */ +export function compileClassHmrInitializer(meta: R3HmrInitializerMetadata): o.Expression { + const id = encodeURIComponent(`${meta.filePath}@${meta.className}`); + const timestamp = encodeURIComponent(meta.timestamp); + const url = `/@ng/component?c=${id}&t=${timestamp}`; + const moduleName = 'm'; + const dataName = 'd'; + + // ɵɵreplaceMetadata(Comp, m.default); + const replaceMetadata = o + .importExpr(R3.replaceMetadata) + .callFn([meta.type, o.variable(moduleName).prop('default')]); + + // (m) => ɵɵreplaceMetadata(...) + const replaceCallback = o.arrowFn([new o.FnParam(moduleName)], replaceMetadata); + + // import(url).then(() => replaceMetadata(...)); + const dynamicImport = new o.DynamicImportExpr(url).prop('then').callFn([replaceCallback]); + + // (d) => { if (d.id === ) { replaceMetadata(...) } } + const listenerCallback = o.arrowFn( + [new o.FnParam(dataName)], + [o.ifStmt(o.variable(dataName).prop('id').equals(o.literal(id)), [dynamicImport.toStmt()])], + ); + + // import.meta.hot + const hotRead = o.variable('import').prop('meta').prop('hot'); + + // import.meta.hot.on('angular:component-update', () => ...); + const hotListener = hotRead + .clone() + .prop('on') + .callFn([o.literal('angular:component-update'), listenerCallback]); + + // import.meta.hot && import.meta.hot.on(...) + return o.arrowFn([], [devOnlyGuardedExpression(hotRead.and(hotListener)).toStmt()]).callFn([]); +}