mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(compiler-cli): extract parenthesized dependencies during HMR (#59644)
Fixes that the HMR dependency extraction logic wasn't accounting for parenthesized identifiers correctly. PR Close #59644
This commit is contained in:
parent
65f51e16aa
commit
67be7d2e06
2 changed files with 48 additions and 3 deletions
|
|
@ -196,10 +196,11 @@ class PotentialTopLevelReadsVisitor extends o.RecursiveAstVisitor {
|
|||
* TypeScript identifiers are used both when referring to a variable (e.g. `console.log(foo)`)
|
||||
* and for names (e.g. `{foo: 123}`). This function determines if the identifier is a top-level
|
||||
* variable read, rather than a nested name.
|
||||
* @param node Identifier to check.
|
||||
* @param identifier Identifier to check.
|
||||
*/
|
||||
private isTopLevelIdentifierReference(node: ts.Identifier): boolean {
|
||||
const parent = node.parent;
|
||||
private isTopLevelIdentifierReference(identifier: ts.Identifier): boolean {
|
||||
let node = identifier as ts.Expression;
|
||||
let parent = node.parent;
|
||||
|
||||
// The parent might be undefined for a synthetic node or if `setParentNodes` is set to false
|
||||
// when the SourceFile was created. We can account for such cases using the type checker, at
|
||||
|
|
@ -209,6 +210,15 @@ class PotentialTopLevelReadsVisitor extends o.RecursiveAstVisitor {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Unwrap parenthesized identifiers, but use the closest parenthesized expression
|
||||
// as the reference node so that we can check cases like `{prop: ((value))}`.
|
||||
if (ts.isParenthesizedExpression(parent) && parent.expression === node) {
|
||||
while (parent && ts.isParenthesizedExpression(parent)) {
|
||||
node = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// Identifier referenced at the top level. Unlikely.
|
||||
if (ts.isSourceFile(parent)) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -549,6 +549,41 @@ runInEachFileSystem(() => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should capture parenthesized dependencies', () => {
|
||||
enableHmr();
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component, InjectionToken} from '@angular/core';
|
||||
|
||||
const token = new InjectionToken<number>('TEST');
|
||||
const value = 123;
|
||||
const otherValue = 321;
|
||||
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [{
|
||||
provide: token,
|
||||
useFactory: () => [(value), ((((otherValue))))]
|
||||
}]
|
||||
})
|
||||
export class Cmp {}
|
||||
`,
|
||||
);
|
||||
|
||||
env.driveMain();
|
||||
|
||||
const jsContents = env.getContents('test.js');
|
||||
const hmrContents = env.driveHmr('test.ts', 'Cmp');
|
||||
expect(jsContents).toContain(
|
||||
'ɵɵreplaceMetadata(Cmp, m.default, [i0], [token, value, otherValue, Component]));',
|
||||
);
|
||||
expect(jsContents).toContain('useFactory: () => [(value), ((((otherValue))))]');
|
||||
expect(hmrContents).toContain(
|
||||
'export default function Cmp_UpdateMetadata(Cmp, ɵɵnamespaces, token, value, otherValue, Component) {',
|
||||
);
|
||||
});
|
||||
|
||||
it('should preserve eager standalone imports in HMR even if they are not used in the template', () => {
|
||||
enableHmr({
|
||||
// Disable class metadata since it can add noise to the test.
|
||||
|
|
|
|||
Loading…
Reference in a new issue