Skip to content

Commit

Permalink
fix(compiler-cli): symbol feature detection for the compiler (#54711)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alxhub authored and atscott committed Mar 20, 2024
1 parent 766bdf3 commit 99e9474
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
15 changes: 11 additions & 4 deletions packages/compiler-cli/src/ngtsc/core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {R3Identifiers} from '@angular/compiler';
import ts from 'typescript';

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

import {coreHasSymbol} from './core_version';
import {coreVersionSupportsFeature} from './feature_detection';

const SHOULD_USE_TEMPLATE_PIPELINE = true;
Expand Down Expand Up @@ -798,10 +800,15 @@ export class NgCompiler {

const useInlineTypeConstructors = this.programDriver.supportsInlineOperations;

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

// First select a type-checking configuration, based on whether full template type-checking is
// requested.
Expand Down
32 changes: 32 additions & 0 deletions packages/compiler-cli/src/ngtsc/core/src/core_version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @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.io/license
*/

import {ExternalReference} from '@angular/compiler';
import ts from 'typescript';

export function coreHasSymbol(program: ts.Program, symbol: ExternalReference): boolean|null {
const checker = program.getTypeChecker();
for (const sf of program.getSourceFiles().filter(isMaybeCore)) {
const sym = checker.getSymbolAtLocation(sf);
if (sym === undefined || sym.exports === undefined) {
continue;
}
if (!sym.exports.has('ɵɵtemplate' as ts.__String)) {
// This is not @angular/core.
continue;
}
return sym.exports.has(symbol.name as ts.__String);
}
// No @angular/core file found, so we have no information.
return null;
}

export function isMaybeCore(sf: ts.SourceFile): boolean {
return sf.isDeclarationFile && sf.fileName.includes('@angular/core') &&
sf.fileName.endsWith('index.d.ts');
}

0 comments on commit 99e9474

Please sign in to comment.