Skip to content

Commit 99e9474

Browse files
alxhubatscott
authored andcommitted
fix(compiler-cli): symbol feature detection for the compiler (#54711)
Use the actual symbol presence in the .d.ts to detect whether two-way binding to writable signals should be template type-checked. PR Close #54711
1 parent 766bdf3 commit 99e9474

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {R3Identifiers} from '@angular/compiler';
910
import ts from 'typescript';
1011

1112
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
@@ -39,6 +40,7 @@ import {getSourceFileOrNull, isDtsPath, toUnredirectedSourceFile} from '../../ut
3940
import {Xi18nContext} from '../../xi18n';
4041
import {DiagnosticCategoryLabel, NgCompilerAdapter, NgCompilerOptions} from '../api';
4142

43+
import {coreHasSymbol} from './core_version';
4244
import {coreVersionSupportsFeature} from './feature_detection';
4345

4446
const SHOULD_USE_TEMPLATE_PIPELINE = true;
@@ -798,10 +800,15 @@ export class NgCompiler {
798800

799801
const useInlineTypeConstructors = this.programDriver.supportsInlineOperations;
800802

801-
// Only Angular versions greater than 17.2 have the necessary symbols to type check signals in
802-
// two-way bindings. We also allow version 0.0.0 in case somebody is using Angular at head.
803-
const allowSignalsInTwoWayBindings = this.angularCoreVersion === null ||
804-
coreVersionSupportsFeature(this.angularCoreVersion, '>= 17.2.0-0');
803+
// Check whether the loaded version of `@angular/core` in the `ts.Program` supports unwrapping
804+
// writable signals for type-checking. If this check fails to find a suitable .d.ts file, fall
805+
// back to version detection. Only Angular versions greater than 17.2 have the necessary symbols
806+
// to type check signals in two-way bindings. We also allow version 0.0.0 in case somebody is
807+
// using Angular at head.
808+
let allowSignalsInTwoWayBindings =
809+
coreHasSymbol(this.inputProgram, R3Identifiers.unwrapWritableSignal) ??
810+
(this.angularCoreVersion === null ||
811+
coreVersionSupportsFeature(this.angularCoreVersion, '>= 17.2.0-0'));
805812

806813
// First select a type-checking configuration, based on whether full template type-checking is
807814
// requested.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ExternalReference} from '@angular/compiler';
10+
import ts from 'typescript';
11+
12+
export function coreHasSymbol(program: ts.Program, symbol: ExternalReference): boolean|null {
13+
const checker = program.getTypeChecker();
14+
for (const sf of program.getSourceFiles().filter(isMaybeCore)) {
15+
const sym = checker.getSymbolAtLocation(sf);
16+
if (sym === undefined || sym.exports === undefined) {
17+
continue;
18+
}
19+
if (!sym.exports.has('ɵɵtemplate' as ts.__String)) {
20+
// This is not @angular/core.
21+
continue;
22+
}
23+
return sym.exports.has(symbol.name as ts.__String);
24+
}
25+
// No @angular/core file found, so we have no information.
26+
return null;
27+
}
28+
29+
export function isMaybeCore(sf: ts.SourceFile): boolean {
30+
return sf.isDeclarationFile && sf.fileName.includes('@angular/core') &&
31+
sf.fileName.endsWith('index.d.ts');
32+
}

0 commit comments

Comments
 (0)