Skip to content

fix(compiler-cli): symbol feature detection for the compiler #54711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -37,6 +38,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 @@ -796,10 +798,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');
}