diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 99d2863263ce..dee27d84135d 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -13,6 +13,7 @@ import {Type, Writable} from '../interface/type'; import {NgModuleDef} from '../metadata/ng_module_def'; import {SchemaMetadata} from '../metadata/schema'; import {ViewEncapsulation} from '../metadata/view'; +import {assertNotEqual} from '../util/assert'; import {noSideEffects} from '../util/closure'; import {EMPTY_ARRAY, EMPTY_OBJ} from '../util/empty'; import {initNgDevMode} from '../util/ng_dev_mode'; @@ -688,6 +689,14 @@ export const GENERATED_COMP_IDS = new Map>(); function getComponentId(componentDef: ComponentDef): string { let hash = 0; + // For components with i18n in templates, the `consts` array is generated by the compiler + // as a function. If client and server bundles were produced with different minification + // configurations, the serializable contents of the function body would be different on + // the client and on the server. This might result in different ids generated. To avoid this + // issue, we do not take the `consts` contents into account if it's a function. + // See https://github.com/angular/angular/issues/58713. + const componentDefConsts = typeof componentDef.consts === 'function' ? '' : componentDef.consts; + // We cannot rely solely on the component selector as the same selector can be used in different // modules. // @@ -697,13 +706,12 @@ function getComponentId(componentDef: ComponentDef): string { // Example: // https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248 // https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32 - const hashSelectors = [ componentDef.selectors, componentDef.ngContentSelectors, componentDef.hostVars, componentDef.hostAttrs, - componentDef.consts, + componentDefConsts, componentDef.vars, componentDef.decls, componentDef.encapsulation, @@ -717,9 +725,22 @@ function getComponentId(componentDef: ComponentDef): string { Object.getOwnPropertyNames(componentDef.type.prototype), !!componentDef.contentQueries, !!componentDef.viewQuery, - ].join('|'); + ]; + + if (typeof ngDevMode === 'undefined' || ngDevMode) { + // If client and server bundles were produced with different minification configurations, + // the serializable contents of the function body would be different on the client and on + // the server. Ensure that we do not accidentally use functions in component id computation. + for (const item of hashSelectors) { + assertNotEqual( + typeof item, + 'function', + 'Internal error: attempting to use a function in component id computation logic.', + ); + } + } - for (const char of hashSelectors) { + for (const char of hashSelectors.join('|')) { hash = (Math.imul(31, hash) + char.charCodeAt(0)) << 0; }