feat(core): support TypeScript 4.8 (#47038)

Adds support for TypeScript 4.8 and resolves some issues that came up as a result of the update.

Most of the issues came from some changes in TypeScript where the `decorators` and `modifiers` properties were removed from most node types, and were combined into a single `modifiers` array. Since we need to continue supporting TS 4.6 and 4.7 until v15, I ended up creating a new `ngtsc/ts_compatibility` directory to make it easier to reuse the new backwards-compatible code.

PR Close #47038
This commit is contained in:
Kristiyan Kostadinov
2022-08-09 17:53:22 +02:00
committed by Pawel Kozlowski
parent 75df404467
commit 31429eaccc
97 changed files with 29842 additions and 23447 deletions
+1 -1
View File
@@ -36,7 +36,7 @@
"rollup": "^2.56.3",
"rollup-plugin-sourcemaps": "^0.6.3",
"terser": "^5.9.0",
"typescript": ">=4.6.2 <4.8"
"typescript": ">=4.6.2 <4.9"
},
"peerDependenciesMeta": {
"terser": {
+1
View File
@@ -32,6 +32,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/sourcemaps",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/convert-source-map",
"@npm//@types/node",
@@ -11,6 +11,7 @@ import ts from 'typescript';
import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
import {Logger} from '../../../src/ngtsc/logging';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, DeclarationNode, Decorator, EnumMember, Import, isConcreteDeclaration, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection';
import {getModifiers} from '../../../src/ngtsc/ts_compatibility';
import {isSymbolWithValueDeclaration, SymbolWithValueDeclaration} from '../../../src/ngtsc/util/src/typescript';
import {isWithinPackage} from '../analysis/util';
import {BundleProgram} from '../packages/bundle_program';
@@ -1534,8 +1535,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
// If we have still not determined if this is a static or instance member then
// look for the `static` keyword on the declaration
if (isStatic === undefined) {
isStatic = node.modifiers !== undefined &&
node.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
const modifiers = getModifiers(node);
isStatic = modifiers !== undefined &&
modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
}
const type: ts.TypeNode = (node as any).type || null;
+1 -1
View File
@@ -66,7 +66,7 @@
},
"peerDependencies": {
"@angular/compiler": "0.0.0-PLACEHOLDER",
"typescript": ">=4.6.2 <4.8"
"typescript": ">=4.6.2 <4.9"
},
"repository": {
"type": "git",
@@ -32,6 +32,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/shims:api",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/typecheck",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/diagnostics",
@@ -27,6 +27,7 @@ import {ComponentScopeReader, CompoundComponentScopeReader, LocalModuleScopeRegi
import {StandaloneComponentScopeReader} from '../../scope/src/standalone';
import {generatedFactoryTransform} from '../../shims';
import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
import {getModifiers} from '../../ts_compatibility';
import {TemplateTypeCheckerImpl} from '../../typecheck';
import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig} from '../../typecheck/api';
import {ALL_DIAGNOSTIC_FACTORIES, ExtendedTemplateCheckerImpl} from '../../typecheck/extended';
@@ -1107,8 +1108,9 @@ export function isAngularCorePackage(program: ts.Program): boolean {
return false;
}
// It must be exported.
if (stmt.modifiers === undefined ||
!stmt.modifiers.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) {
const modifiers = getModifiers(stmt);
if (modifiers === undefined ||
!modifiers.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) {
return false;
}
// It must declare ITS_JUST_ANGULAR.
@@ -12,6 +12,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//typescript",
],
@@ -10,6 +10,7 @@ import ts from 'typescript';
import {OwningModule, Reference} from '../../imports';
import {ClassDeclaration, ClassMember, ClassMemberKind, isNamedClassDeclaration, ReflectionHost, reflectTypeEntityToDeclaration} from '../../reflection';
import {getModifiers} from '../../ts_compatibility';
import {nodeDebugInfo} from '../../util/src/typescript';
import {DirectiveMeta, DirectiveTypeCheckMeta, MetadataReader, NgModuleMeta, PipeMeta, TemplateGuardMeta} from './api';
@@ -150,14 +151,12 @@ export function extractDirectiveTypeCheckMeta(
}
function isRestricted(node: ts.Node): boolean {
if (node.modifiers === undefined) {
return false;
}
const modifiers = getModifiers(node);
return node.modifiers.some(
modifier => modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword ||
modifier.kind === ts.SyntaxKind.ReadonlyKeyword);
return modifiers !== undefined && modifiers.some(({kind}) => {
return kind === ts.SyntaxKind.PrivateKeyword || kind === ts.SyntaxKind.ProtectedKeyword ||
kind === ts.SyntaxKind.ReadonlyKeyword;
});
}
function extractTemplateGuard(member: ClassMember): TemplateGuardMeta|null {
@@ -15,6 +15,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/incremental:api",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",
"@npm//typescript",
@@ -12,6 +12,7 @@ import {Reference} from '../../imports';
import {OwningModule} from '../../imports/src/references';
import {DependencyTracker} from '../../incremental/api';
import {Declaration, DeclarationKind, DeclarationNode, EnumMember, FunctionDefinition, isConcreteDeclaration, ReflectionHost, SpecialDeclarationKind} from '../../reflection';
import {getModifiers} from '../../ts_compatibility';
import {isDeclaration} from '../../util/src/typescript';
import {ArrayConcatBuiltinFn, ArraySliceBuiltinFn, StringConcatBuiltinFn} from './builtin';
@@ -746,8 +747,9 @@ function isVariableDeclarationDeclared(node: ts.VariableDeclaration): boolean {
return false;
}
const varStmt = declList.parent;
return varStmt.modifiers !== undefined &&
varStmt.modifiers.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword);
const modifiers = getModifiers(varStmt);
return modifiers !== undefined &&
modifiers.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword);
}
const EMPTY = {};
@@ -7,5 +7,8 @@ ts_library(
srcs = ["index.ts"] + glob([
"src/**/*.ts",
]),
deps = ["@npm//typescript"],
deps = [
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"@npm//typescript",
],
)
@@ -8,6 +8,8 @@
import ts from 'typescript';
import {getDecorators, getModifiers} from '../../ts_compatibility';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, DeclarationKind, DeclarationNode, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host';
import {typeToValue} from './type_to_value';
import {isNamedClassDeclaration} from './util';
@@ -20,11 +22,12 @@ export class TypeScriptReflectionHost implements ReflectionHost {
constructor(protected checker: ts.TypeChecker) {}
getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null {
if (declaration.decorators === undefined || declaration.decorators.length === 0) {
return null;
}
return declaration.decorators.map(decorator => this._reflectDecorator(decorator))
.filter((dec): dec is Decorator => dec !== null);
const decorators = getDecorators(declaration);
return decorators !== undefined && decorators.length ?
decorators.map(decorator => this._reflectDecorator(decorator))
.filter((dec): dec is Decorator => dec !== null) :
null;
}
getMembersOfClass(clazz: ClassDeclaration): ClassMember[] {
@@ -205,8 +208,9 @@ export class TypeScriptReflectionHost implements ReflectionHost {
if (ts.isVariableDeclaration(decl) && ts.isVariableDeclarationList(decl.parent)) {
topLevel = decl.parent.parent;
}
if (topLevel.modifiers !== undefined &&
topLevel.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
const modifiers = getModifiers(topLevel);
if (modifiers !== undefined &&
modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
// The node is part of a declaration that's directly exported.
return true;
}
@@ -427,8 +431,9 @@ export class TypeScriptReflectionHost implements ReflectionHost {
}
const decorators = this.getDecoratorsOfDeclaration(node);
const isStatic = node.modifiers !== undefined &&
node.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
const modifiers = getModifiers(node);
const isStatic =
modifiers !== undefined && modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
return {
node,
@@ -662,7 +667,8 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
*/
function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null {
return ts.isImportSpecifier(node) ? node.parent!.parent!.parent! :
ts.isNamespaceImport(node) ? node.parent.parent : null;
ts.isNamespaceImport(node) ? node.parent.parent :
null;
}
/**
@@ -21,6 +21,7 @@ ts_library(
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",
"@npm//typescript",
@@ -9,6 +9,7 @@ import ts from 'typescript';
import {absoluteFromSourceFile, AbsoluteFsPath, basename} from '../../file_system';
import {ImportRewriter} from '../../imports';
import {getDecorators, getModifiers, updateImportDeclaration} from '../../ts_compatibility';
import {FactoryInfo, FactoryTracker, ModuleInfo, PerFileShimGenerator} from '../api';
import {generatedModuleName} from './util';
@@ -41,7 +42,7 @@ export class FactoryGenerator implements PerFileShimGenerator, FactoryTracker {
.filter(ts.isClassDeclaration)
// which are named, exported, and have decorators.
.filter(
decl => isExported(decl) && decl.decorators !== undefined &&
decl => isExported(decl) && getDecorators(decl) !== undefined &&
decl.name !== undefined)
// Grab the symbol name.
.map(decl => decl.name!.text);
@@ -103,8 +104,8 @@ export class FactoryGenerator implements PerFileShimGenerator, FactoryTracker {
}
function isExported(decl: ts.Declaration): boolean {
return decl.modifiers !== undefined &&
decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
const modifiers = getModifiers(decl);
return modifiers !== undefined && modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
}
export function generatedFactoryTransform(
@@ -159,8 +160,8 @@ function transformFactorySourceFile(
const rewrittenModuleSpecifier =
importRewriter.rewriteSpecifier('@angular/core', sourceFilePath);
if (rewrittenModuleSpecifier !== stmt.moduleSpecifier.text) {
transformedStatements.push(ts.factory.updateImportDeclaration(
stmt, stmt.decorators, stmt.modifiers, stmt.importClause,
transformedStatements.push(updateImportDeclaration(
stmt, getModifiers(stmt), stmt.importClause,
ts.factory.createStringLiteral(rewrittenModuleSpecifier), undefined));
// Record the identifier by which this imported module goes, so references to its symbols
@@ -316,13 +317,14 @@ function updateInitializers(
): ts.VariableStatement {
return ts.factory.updateVariableStatement(
stmt,
stmt.modifiers,
getModifiers(stmt),
ts.factory.updateVariableDeclarationList(
stmt.declarationList,
stmt.declarationList.declarations.map(
(decl) => ts.updateVariableDeclaration(
(decl) => ts.factory.updateVariableDeclaration(
decl,
decl.name,
decl.exclamationToken,
decl.type,
update(decl.initializer),
),
@@ -9,6 +9,7 @@
import ts from 'typescript';
import {AbsoluteFsPath} from '../../file_system';
import {getDecorators, getModifiers} from '../../ts_compatibility';
import {PerFileShimGenerator} from '../api';
import {generatedModuleName} from './util';
@@ -27,7 +28,7 @@ export class SummaryGenerator implements PerFileShimGenerator {
for (const stmt of sf.statements) {
if (ts.isClassDeclaration(stmt)) {
// If the class isn't exported, or if it's not decorated, then skip it.
if (!isExported(stmt) || stmt.decorators === undefined || stmt.name === undefined) {
if (!isExported(stmt) || stmt.name === undefined || getDecorators(stmt) === undefined) {
continue;
}
symbolNames.push(stmt.name.text);
@@ -68,6 +69,8 @@ export class SummaryGenerator implements PerFileShimGenerator {
}
function isExported(decl: ts.Declaration): boolean {
return decl.modifiers !== undefined &&
decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
const modifiers = getModifiers(decl);
return modifiers !== undefined &&
modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword) ||
false;
}
@@ -19,6 +19,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
"//packages/compiler-cli/src/ngtsc/util",
@@ -12,6 +12,7 @@ import ts from 'typescript';
import {ImportRewriter} from '../../imports';
import {ClassDeclaration} from '../../reflection';
import {ImportManager, translateType} from '../../translator';
import {createPropertyDeclaration, updateClassDeclaration} from '../../ts_compatibility';
import {DtsTransform} from './api';
import {addImports} from './utils';
@@ -140,9 +141,8 @@ class DtsTransformer {
// If some elements have been transformed but the class itself has not been transformed, create
// an updated class declaration with the updated elements.
if (elementsChanged && clazz === newClazz) {
newClazz = ts.factory.updateClassDeclaration(
newClazz = updateClassDeclaration(
/* node */ clazz,
/* decorators */ clazz.decorators,
/* modifiers */ clazz.modifiers,
/* name */ clazz.name,
/* typeParameters */ clazz.typeParameters,
@@ -194,8 +194,7 @@ export class IvyDeclarationDtsTransform implements DtsTransform {
const modifiers = [ts.factory.createModifier(ts.SyntaxKind.StaticKeyword)];
const typeRef = translateType(decl.type, imports);
markForEmitAsSingleLine(typeRef);
return ts.factory.createPropertyDeclaration(
/* decorators */ undefined,
return createPropertyDeclaration(
/* modifiers */ modifiers,
/* name */ decl.name,
/* questionOrExclamationToken */ undefined,
@@ -203,9 +202,8 @@ export class IvyDeclarationDtsTransform implements DtsTransform {
/* initializer */ undefined);
});
return ts.factory.updateClassDeclaration(
return updateClassDeclaration(
/* node */ clazz,
/* decorators */ clazz.decorators,
/* modifiers */ clazz.modifiers,
/* name */ clazz.name,
/* typeParameters */ clazz.typeParameters,
@@ -14,6 +14,7 @@ import {getDefaultImportDeclaration} from '../../imports/src/default';
import {PerfPhase, PerfRecorder} from '../../perf';
import {Decorator, ReflectionHost} from '../../reflection';
import {ImportManager, RecordWrappedNodeFn, translateExpression, translateStatement, TranslatorOptions} from '../../translator';
import {combineModifiers, createPropertyDeclaration, getDecorators, getModifiers, updateClassDeclaration, updateConstructorDeclaration, updateGetAccessorDeclaration, updateMethodDeclaration, updateParameterDeclaration, updatePropertyDeclaration, updateSetAccessorDeclaration} from '../../ts_compatibility';
import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor';
import {CompileResult} from './api';
@@ -109,9 +110,9 @@ class IvyTransformationVisitor extends Visitor {
const exprNode = translateExpression(field.initializer, this.importManager, translateOptions);
// Create a static property declaration for the new field.
const property = ts.factory.createPropertyDeclaration(
undefined, [ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], field.name, undefined,
undefined, exprNode);
const property = createPropertyDeclaration(
[ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], field.name, undefined, undefined,
exprNode);
if (this.isClosureCompilerEnabled) {
// Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To
@@ -129,12 +130,14 @@ class IvyTransformationVisitor extends Visitor {
members.push(property);
}
// Replace the class declaration with an updated version.
node = ts.factory.updateClassDeclaration(
node,
const filteredDecorators =
// Remove the decorator which triggered this compilation, leaving the others alone.
maybeFilterDecorator(node.decorators, this.compilation.decoratorsFor(node)), node.modifiers,
node.name, node.typeParameters, node.heritageClauses || [],
maybeFilterDecorator(getDecorators(node), this.compilation.decoratorsFor(node));
// Replace the class declaration with an updated version.
node = updateClassDeclaration(
node, combineModifiers(filteredDecorators, getModifiers(node)), node.name,
node.typeParameters, node.heritageClauses || [],
// Map over the class members and remove any Angular decorators from them.
members.map(member => this._stripAngularDecorators(member)));
return {node, after: statements};
@@ -158,31 +161,26 @@ class IvyTransformationVisitor extends Visitor {
}
}
/**
* Given a `ts.Node`, filter the decorators array and return a version containing only non-Angular
* decorators.
*
* If all decorators are removed (or none existed in the first place), this method returns
* `undefined`.
*/
private _nonCoreDecoratorsOnly(node: ts.Declaration): ts.NodeArray<ts.Decorator>|undefined {
const decorators = getDecorators(node);
// Shortcut if the node has no decorators.
if (node.decorators === undefined) {
if (decorators === undefined) {
return undefined;
}
// Build a Set of the decorators on this node from @angular/core.
const coreDecorators = this._angularCoreDecorators(node);
if (coreDecorators.size === node.decorators.length) {
if (coreDecorators.size === decorators.length) {
// If all decorators are to be removed, return `undefined`.
return undefined;
} else if (coreDecorators.size === 0) {
// If no decorators need to be removed, return the original decorators array.
return node.decorators;
return nodeArrayFromDecoratorsArray(decorators);
}
// Filter out the core decorators.
const filtered = node.decorators.filter(dec => !coreDecorators.has(dec));
const filtered = decorators.filter(dec => !coreDecorators.has(dec));
// If no decorators survive, return `undefined`. This can only happen if a core decorator is
// repeated on the node.
@@ -191,10 +189,7 @@ class IvyTransformationVisitor extends Visitor {
}
// Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original.
const array = ts.factory.createNodeArray(filtered);
(array.pos as number) = node.decorators.pos;
(array.end as number) = node.decorators.end;
return array;
return nodeArrayFromDecoratorsArray(filtered);
}
/**
@@ -206,40 +201,40 @@ class IvyTransformationVisitor extends Visitor {
private _stripAngularDecorators<T extends ts.Node>(node: T): T {
if (ts.isParameter(node)) {
// Strip decorators from parameters (probably of the constructor).
node = ts.factory.updateParameterDeclaration(
node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.dotDotDotToken,
node.name, node.questionToken, node.type, node.initializer) as T &
node = updateParameterDeclaration(
node, combineModifiers(this._nonCoreDecoratorsOnly(node), getModifiers(node)),
node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer) as
T &
ts.ParameterDeclaration;
} else if (ts.isMethodDeclaration(node) && node.decorators !== undefined) {
} else if (ts.isMethodDeclaration(node) && getDecorators(node) !== undefined) {
// Strip decorators of methods.
node = ts.factory.updateMethodDeclaration(
node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.asteriskToken,
node.name, node.questionToken, node.typeParameters, node.parameters, node.type,
node.body) as T &
node = updateMethodDeclaration(
node, combineModifiers(this._nonCoreDecoratorsOnly(node), getModifiers(node)),
node.asteriskToken, node.name, node.questionToken, node.typeParameters,
node.parameters, node.type, node.body) as T &
ts.MethodDeclaration;
} else if (ts.isPropertyDeclaration(node) && node.decorators !== undefined) {
} else if (ts.isPropertyDeclaration(node) && getDecorators(node) !== undefined) {
// Strip decorators of properties.
node = ts.factory.updatePropertyDeclaration(
node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name,
node.questionToken, node.type, node.initializer) as T &
node = updatePropertyDeclaration(
node, combineModifiers(this._nonCoreDecoratorsOnly(node), getModifiers(node)),
node.name, node.questionToken, node.type, node.initializer) as T &
ts.PropertyDeclaration;
} else if (ts.isGetAccessor(node)) {
// Strip decorators of getters.
node = ts.factory.updateGetAccessorDeclaration(
node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name,
node.parameters, node.type, node.body) as T &
node = updateGetAccessorDeclaration(
node, combineModifiers(this._nonCoreDecoratorsOnly(node), getModifiers(node)),
node.name, node.parameters, node.type, node.body) as T &
ts.GetAccessorDeclaration;
} else if (ts.isSetAccessor(node)) {
// Strip decorators of setters.
node = ts.factory.updateSetAccessorDeclaration(
node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name,
node.parameters, node.body) as T &
node = updateSetAccessorDeclaration(
node, combineModifiers(this._nonCoreDecoratorsOnly(node), getModifiers(node)),
node.name, node.parameters, node.body) as T &
ts.SetAccessorDeclaration;
} else if (ts.isConstructorDeclaration(node)) {
// For constructors, strip decorators of the parameters.
const parameters = node.parameters.map(param => this._stripAngularDecorators(param));
node = ts.factory.updateConstructorDeclaration(
node, node.decorators, node.modifiers, parameters, node.body) as T &
node = updateConstructorDeclaration(node, getModifiers(node), parameters, node.body) as T &
ts.ConstructorDeclaration;
}
return node;
@@ -354,7 +349,7 @@ function setFileOverviewComment(sf: ts.SourceFile, fileoverview: FileOverviewMet
}
function maybeFilterDecorator(
decorators: ts.NodeArray<ts.Decorator>|undefined,
decorators: readonly ts.Decorator[]|undefined,
toRemove: ts.Decorator[]): ts.NodeArray<ts.Decorator>|undefined {
if (decorators === undefined) {
return undefined;
@@ -380,3 +375,16 @@ function createRecorderFn(defaultImportTracker: DefaultImportTracker):
}
};
}
/** Creates a `NodeArray` with the correct offsets from an array of decorators. */
function nodeArrayFromDecoratorsArray(decorators: readonly ts.Decorator[]):
ts.NodeArray<ts.Decorator> {
const array = ts.factory.createNodeArray(decorators);
if (array.length > 0) {
(array.pos as number) = decorators[0].pos;
(array.end as number) = decorators[decorators.length - 1].end;
}
return array;
}
@@ -0,0 +1,11 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//visibility:public"])
ts_library(
name = "ts_compatibility",
srcs = ["index.ts"] + glob([
"src/**/*.ts",
]),
deps = ["@npm//typescript"],
)
@@ -0,0 +1,9 @@
/**
* @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
*/
export * from './src/ts_cross_version_utils';
@@ -0,0 +1,296 @@
/*!
* @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 ts from 'typescript';
/** Whether the current TypeScript version is after 4.8. */
export const IS_AFTER_TS_48 = isAfterVersion(4, 8);
/** Whether the current TypeScript version is after 4.7. */
const IS_AFTER_TS_47 = isAfterVersion(4, 7);
/** Equivalent of `ts.ModifierLike` which is only present in TS 4.8+. */
export type ModifierLike = ts.Modifier|ts.Decorator;
/** Type of `ts.factory.updateTypeParameterDeclaration` in TS 4.7+. */
type Ts47UpdateTypeParameterDeclarationFn =
(node: ts.TypeParameterDeclaration, modifiers: readonly ts.Modifier[]|undefined,
name: ts.Identifier, constraint: ts.TypeNode|undefined, defaultType: ts.TypeNode|undefined) =>
ts.TypeParameterDeclaration;
/**
* Updates a `ts.TypeParameter` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.7.
* We should remove it once we have dropped support for the older versions.
*/
export const updateTypeParameterDeclaration: Ts47UpdateTypeParameterDeclarationFn = IS_AFTER_TS_47 ?
(ts.factory.updateTypeParameterDeclaration as any) :
(node, _modifiers, name, constraint, defaultType) =>
(ts.factory.updateTypeParameterDeclaration as any)(node, name, constraint, defaultType);
/** Type of `ts.factory.updateParameterDeclaration` in TS 4.8+. */
type Ts48UpdateParameterDeclarationFn =
(node: ts.ParameterDeclaration, modifiers: readonly ModifierLike[]|undefined,
dotDotDotToken: ts.DotDotDotToken|undefined, name: string|ts.BindingName,
questionToken: ts.QuestionToken|undefined, type: ts.TypeNode|undefined,
initializer: ts.Expression|undefined) => ts.ParameterDeclaration;
/**
* Updates a `ts.ParameterDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateParameterDeclaration: Ts48UpdateParameterDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateParameterDeclaration as any) :
(node, modifiers, dotDotDotToken, name, questionToken, type, initializer) => (
ts.factory.updateParameterDeclaration as any)(
node, ...splitModifiers(modifiers), dotDotDotToken, name, questionToken, type, initializer);
/** Type of `ts.factory.updateImportDeclaration` in TS 4.8+. */
type Ts48UpdateImportDeclarationFn =
(node: ts.ImportDeclaration, modifiers: readonly ts.Modifier[]|undefined,
importClause: ts.ImportClause|undefined, moduleSpecifier: ts.Expression,
assertClause: ts.AssertClause|undefined) => ts.ImportDeclaration;
/**
* Updates a `ts.ImportDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateImportDeclaration: Ts48UpdateImportDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateImportDeclaration as any) :
(node, modifiers, importClause, moduleSpecifier, assertClause) =>
(ts.factory.updateImportDeclaration as any)(
node, undefined, modifiers, importClause, moduleSpecifier, assertClause);
/** Type of `ts.factory.updateClassDeclaration` in TS 4.8+. */
type Ts48UpdateClassDeclarationFn =
(node: ts.ClassDeclaration, modifiers: readonly ModifierLike[]|undefined,
name: ts.Identifier|undefined,
typeParameters: readonly ts.TypeParameterDeclaration[]|undefined,
heritageClauses: readonly ts.HeritageClause[]|undefined,
members: readonly ts.ClassElement[]) => ts.ClassDeclaration;
/**
* Updates a `ts.ClassDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateClassDeclaration: Ts48UpdateClassDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateClassDeclaration as any) :
(node, combinedModifiers, name, typeParameters, heritageClauses, members) => (
ts.factory.updateClassDeclaration as any)(
node, ...splitModifiers(combinedModifiers), name, typeParameters, heritageClauses, members);
/** Type of `ts.factory.updateMethodDeclaration` in TS 4.8+. */
type Ts48UpdateMethodDeclarationFn =
(node: ts.MethodDeclaration, modifiers: readonly ModifierLike[]|undefined,
asteriskToken: ts.AsteriskToken|undefined, name: ts.PropertyName,
questionToken: ts.QuestionToken|undefined,
typeParameters: readonly ts.TypeParameterDeclaration[]|undefined,
parameters: readonly ts.ParameterDeclaration[], type: ts.TypeNode|undefined,
body: ts.Block|undefined) => ts.MethodDeclaration;
/**
* Updates a `ts.MethodDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateMethodDeclaration: Ts48UpdateMethodDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateMethodDeclaration as any) :
(node, modifiers, asteriskToken, name, questionToken, typeParameters, parameters, type, body) =>
(ts.factory.updateMethodDeclaration as any)(
node, ...splitModifiers(modifiers), asteriskToken, name, questionToken, typeParameters,
parameters, type, body);
/** Type of `ts.factory.updatePropertyDeclaration` in TS 4.8+. */
type Ts48UpdatePropertyDeclarationFn =
(node: ts.PropertyDeclaration, modifiers: readonly ModifierLike[]|undefined,
name: string|ts.PropertyName,
questionOrExclamationToken: ts.QuestionToken|ts.ExclamationToken|undefined,
type: ts.TypeNode|undefined, initializer: ts.Expression|undefined) => ts.PropertyDeclaration;
/**
* Updates a `ts.PropertyDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updatePropertyDeclaration: Ts48UpdatePropertyDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updatePropertyDeclaration as any) :
(node, modifiers, name, questionOrExclamationToken, type, initializer) => (
ts.factory.updatePropertyDeclaration as any)(
node, ...splitModifiers(modifiers), name, questionOrExclamationToken, type, initializer);
/** Type of `ts.factory.createPropertyDeclaration` in TS 4.8+. */
type Ts48CreatePropertyDeclarationFn =
(modifiers: readonly ModifierLike[]|undefined, name: string|ts.PropertyName,
questionOrExclamationToken: ts.QuestionToken|ts.ExclamationToken|undefined,
type: ts.TypeNode|undefined, initializer: ts.Expression|undefined) => ts.PropertyDeclaration;
/**
* Creates a `ts.PropertyDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const createPropertyDeclaration: Ts48CreatePropertyDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.createPropertyDeclaration as any) :
(modifiers, name, questionOrExclamationToken, type, initializer) =>
(ts.factory.createPropertyDeclaration as any)(
...splitModifiers(modifiers), name, questionOrExclamationToken, type, initializer);
/** Type of `ts.factory.updateGetAccessorDeclaration` in TS 4.8+. */
type Ts48UpdateGetAccessorDeclarationFn =
(node: ts.GetAccessorDeclaration, modifiers: readonly ModifierLike[]|undefined,
name: ts.PropertyName, parameters: readonly ts.ParameterDeclaration[],
type: ts.TypeNode|undefined, body: ts.Block|undefined) => ts.GetAccessorDeclaration;
/**
* Updates a `ts.GetAccessorDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateGetAccessorDeclaration: Ts48UpdateGetAccessorDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateGetAccessorDeclaration as any) :
(node, modifiers, name, parameters, type, body) =>
(ts.factory.updateGetAccessorDeclaration as any)(
node, ...splitModifiers(modifiers), name, parameters, type, body);
/** Type of `ts.factory.updateSetAccessorDeclaration` in TS 4.8+. */
type Ts48UpdateSetAccessorDeclarationFn =
(node: ts.SetAccessorDeclaration, modifiers: readonly ModifierLike[]|undefined,
name: ts.PropertyName, parameters: readonly ts.ParameterDeclaration[],
body: ts.Block|undefined) => ts.SetAccessorDeclaration;
/**
* Updates a `ts.GetAccessorDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateSetAccessorDeclaration: Ts48UpdateSetAccessorDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateSetAccessorDeclaration as any) :
(node, modifiers, name, parameters, body) => (ts.factory.updateSetAccessorDeclaration as any)(
node, ...splitModifiers(modifiers), name, parameters, body);
/** Type of `ts.factory.updateConstructorDeclaration` in TS 4.8+. */
type Ts48UpdateConstructorDeclarationFn =
(node: ts.ConstructorDeclaration, modifiers: readonly ts.Modifier[]|undefined,
parameters: readonly ts.ParameterDeclaration[], body: ts.Block|undefined) =>
ts.ConstructorDeclaration;
/**
* Updates a `ts.ConstructorDeclaration` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const updateConstructorDeclaration: Ts48UpdateConstructorDeclarationFn = IS_AFTER_TS_48 ?
(ts.factory.updateConstructorDeclaration as any) :
(node, modifiers, parameters, body) => (ts.factory.updateConstructorDeclaration as any)(
node, undefined, modifiers, parameters, body);
/**
* Gets the decorators that have been applied to a node.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const getDecorators: (node: ts.Node) => readonly ts.Decorator[] | undefined =
IS_AFTER_TS_48 ? (ts as any).getDecorators : node => node.decorators;
/**
* Gets the modifiers that have been set on a node.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export const getModifiers: (node: ts.Node) => readonly ts.Modifier[] | undefined =
IS_AFTER_TS_48 ? (ts as any).getModifiers : node => node.modifiers;
/**
* Combines an optional array of decorators with an optional array of modifiers into a single
* `ts.ModifierLike` array. Used in version of TypeScript after 4.8 where the `decorators` and
* `modifiers` arrays have been combined.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.8.
* We should remove it once we have dropped support for the older versions.
*/
export function combineModifiers(
decorators: readonly ts.Decorator[]|undefined,
modifiers: readonly ModifierLike[]|undefined): readonly ModifierLike[]|undefined {
const hasDecorators = decorators?.length;
const hasModifiers = modifiers?.length;
// This function can be written more compactly, but it is somewhat performance-sensitive
// so we have some additional logic only to create new arrays when necessary.
if (hasDecorators && hasModifiers) {
return [...decorators, ...modifiers];
}
if (hasDecorators && !hasModifiers) {
return decorators;
}
if (hasModifiers && !hasDecorators) {
return modifiers;
}
return undefined;
}
/**
* Splits a `ModifierLike` into two arrays: decorators and modifiers. Used for backwards
* compatibility with TS 4.7 and below where most factory functions require separate `decorators`
* and `modifiers` arrays.
*/
function splitModifiers(allModifiers: readonly ModifierLike[]|
undefined): [ts.Decorator[]|undefined, ts.Modifier[]|undefined] {
if (!allModifiers) {
return [undefined, undefined];
}
const decorators: ts.Decorator[] = [];
const modifiers: ts.Modifier[] = [];
for (const current of allModifiers) {
if (ts.isDecorator(current)) {
decorators.push(current);
} else {
modifiers.push(current);
}
}
return [decorators.length ? decorators : undefined, modifiers.length ? modifiers : undefined];
}
/** Checks if the current version of TypeScript is after the specified major/minor versions. */
function isAfterVersion(targetMajor: number, targetMinor: number): boolean {
const [major, minor] = ts.versionMajorMinor.split('.').map(part => parseInt(part));
if (major < targetMajor) {
return false;
}
return major === targetMajor ? minor >= targetMinor : true;
}
@@ -22,6 +22,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/shims:api",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/diagnostics",
"//packages/compiler-cli/src/ngtsc/util",
@@ -8,8 +8,6 @@
import ts from 'typescript';
const PARSED_TS_VERSION = parseFloat(ts.versionMajorMinor);
/**
* A `Set` of `ts.SyntaxKind`s of `ts.Expression` which are safe to wrap in a `ts.AsExpression`
@@ -134,21 +132,6 @@ export function tsCallMethod(
/* argumentsArray */ args);
}
/**
* Updates a `ts.TypeParameter` declaration.
*
* TODO(crisbeto): this is a backwards-compatibility layer for versions of TypeScript less than 4.7.
* We should remove it once we have dropped support for the older versions.
*/
export function tsUpdateTypeParameterDeclaration(
node: ts.TypeParameterDeclaration, name: ts.Identifier, constraint: ts.TypeNode|undefined,
defaultType: ts.TypeNode|undefined): ts.TypeParameterDeclaration {
return PARSED_TS_VERSION < 4.7 ?
ts.factory.updateTypeParameterDeclaration(node, name, constraint, defaultType) :
(ts.factory.updateTypeParameterDeclaration as any)(
node, /* modifiers */[], name, constraint, defaultType);
}
export function isAccessExpression(node: ts.Node): node is ts.ElementAccessExpression|
ts.PropertyAccessExpression {
return ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
@@ -9,10 +9,11 @@
import ts from 'typescript';
import {ClassDeclaration, ReflectionHost} from '../../reflection';
import {updateTypeParameterDeclaration} from '../../ts_compatibility';
import {TypeCtorMetadata} from '../api';
import {checkIfGenericTypeBoundsCanBeEmitted, ReferenceEmitEnvironment} from './tcb_util';
import {tsCreateTypeQueryForCoercedInput, tsUpdateTypeParameterDeclaration} from './ts_util';
import {tsCreateTypeQueryForCoercedInput} from './ts_util';
export function generateTypeCtorDeclarationFn(
node: ClassDeclaration<ts.ClassDeclaration>, meta: TypeCtorMetadata, nodeTypeRef: ts.EntityName,
@@ -244,8 +245,8 @@ function typeParametersWithDefaultTypes(params: ReadonlyArray<ts.TypeParameterDe
return params.map(param => {
if (param.default === undefined) {
return tsUpdateTypeParameterDeclaration(
param, param.name, param.constraint,
return updateTypeParameterDeclaration(
param, param.modifiers, param.name, param.constraint,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword));
} else {
return param;
@@ -9,8 +9,8 @@ import ts from 'typescript';
import {OwningModule, Reference} from '../../imports';
import {DeclarationNode, ReflectionHost} from '../../reflection';
import {updateTypeParameterDeclaration} from '../../ts_compatibility';
import {tsUpdateTypeParameterDeclaration} from './ts_util';
import {canEmitType, TypeEmitter} from './type_emitter';
@@ -74,7 +74,8 @@ export class TypeParameterEmitter {
const defaultType =
typeParam.default !== undefined ? emitter.emitType(typeParam.default) : undefined;
return tsUpdateTypeParameterDeclaration(typeParam, typeParam.name, constraint, defaultType);
return updateTypeParameterDeclaration(
typeParam, typeParam.modifiers, typeParam.name, constraint, defaultType);
});
}
@@ -12,6 +12,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/incremental:api",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"@npm//@types/node",
"@npm//typescript",
],
@@ -12,6 +12,7 @@ const D_TS = /\.d\.ts$/i;
import ts from 'typescript';
import {AbsoluteFsPath, getFileSystem} from '../../file_system';
import {DeclarationNode} from '../../reflection';
import {getModifiers} from '../../ts_compatibility';
/**
* Type describing a symbol that is guaranteed to have a value declaration.
@@ -110,8 +111,9 @@ export function isExported(node: DeclarationNode): boolean {
if (ts.isVariableDeclaration(node) && ts.isVariableDeclarationList(node.parent)) {
topLevel = node.parent.parent;
}
return topLevel.modifiers !== undefined &&
topLevel.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword);
const modifiers = getModifiers(topLevel);
return modifiers !== undefined &&
modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword);
}
export function getRootDirs(
@@ -13,6 +13,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/file_system/testing",
"//packages/compiler-cli/src/ngtsc/testing",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//typescript",
],
@@ -10,6 +10,7 @@ import ts from 'typescript';
import {absoluteFrom, getSourceFileOrError} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing';
import {makeProgram} from '../../testing';
import {getModifiers} from '../../ts_compatibility';
import {visit, VisitListEntryResult, Visitor} from '../src/visitor';
class TestAstVisitor extends Visitor {
@@ -17,8 +18,8 @@ class TestAstVisitor extends Visitor {
VisitListEntryResult<ts.Statement, ts.ClassDeclaration> {
const name = node.name!.text;
const statics = node.members.filter(
member => (member.modifiers as ReadonlyArray<ts.Modifier>||
[]).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword));
member =>
(getModifiers(member) || []).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword));
const idStatic = statics.find(
el => ts.isPropertyDeclaration(el) && ts.isIdentifier(el.name) &&
el.name.text === 'id') as ts.PropertyDeclaration |
@@ -7,6 +7,7 @@ ts_library(
srcs = glob(["*.ts"]),
deps = [
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"@npm//typescript",
],
)
@@ -9,6 +9,7 @@
import ts from 'typescript';
import {Decorator, ReflectionHost} from '../../ngtsc/reflection';
import {combineModifiers, createPropertyDeclaration, getDecorators, getModifiers, IS_AFTER_TS_48, updateClassDeclaration, updateConstructorDeclaration, updateParameterDeclaration} from '../../ngtsc/ts_compatibility';
import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution';
@@ -136,9 +137,9 @@ function createCtorParametersClassProperty(
undefined, undefined, [], undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createArrayLiteralExpression(params, true));
const ctorProp = ts.factory.createPropertyDeclaration(
undefined, [ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], 'ctorParameters', undefined,
undefined, initializer);
const ctorProp = createPropertyDeclaration(
[ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], 'ctorParameters', undefined, undefined,
initializer);
if (isClosureCompilerEnabled) {
ts.setSyntheticLeadingComments(ctorProp, [
{
@@ -290,8 +291,8 @@ export function getDownlevelDecoratorsTransform(
// case previously, and adding it back in leads to substantial code size
// increases as Closure fails to tree shake these props
// without @nocollapse.
const prop = ts.factory.createPropertyDeclaration(
undefined, [modifier], 'decorators', undefined, undefined, initializer);
const prop =
createPropertyDeclaration([modifier], 'decorators', undefined, undefined, initializer);
addJSDocTypeAnnotation(prop, DECORATOR_INVOCATION_JSDOC_TYPE);
return prop;
}
@@ -319,9 +320,9 @@ export function getDownlevelDecoratorsTransform(
decorators.map(deco => extractMetadataFromSingleDecorator(deco, diagnostics)))));
}
const initializer = ts.factory.createObjectLiteralExpression(entries, true);
const prop = ts.factory.createPropertyDeclaration(
undefined, [ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], 'propDecorators',
undefined, undefined, initializer);
const prop = createPropertyDeclaration(
[ts.factory.createToken(ts.SyntaxKind.StaticKeyword)], 'propDecorators', undefined,
undefined, initializer);
addJSDocTypeAnnotation(prop, `!Object<string, ${DECORATOR_INVOCATION_JSDOC_TYPE}>`);
return prop;
}
@@ -422,9 +423,20 @@ export function getDownlevelDecoratorsTransform(
const name = (element.name as ts.Identifier).text;
const mutable = ts.getMutableClone(element);
(mutable as any).decorators = decoratorsToKeep.length ?
ts.setTextRange(ts.factory.createNodeArray(decoratorsToKeep), mutable.decorators) :
undefined;
if (IS_AFTER_TS_48) {
// As of TS 4.8, the decorators are part of the `modifiers` array.
(mutable as any).modifiers = decoratorsToKeep.length ?
ts.setTextRange(
ts.factory.createNodeArray(
combineModifiers(decoratorsToKeep, getModifiers(mutable))),
mutable.modifiers) :
undefined;
} else {
(mutable as any).decorators = decoratorsToKeep.length ?
ts.setTextRange(ts.factory.createNodeArray(decoratorsToKeep), mutable.decorators) :
undefined;
}
return [name, mutable, toLower];
}
@@ -463,15 +475,16 @@ export function getDownlevelDecoratorsTransform(
paramInfo!.type = param.type;
}
parametersInfo.push(paramInfo);
const newParam = ts.factory.updateParameterDeclaration(
const newParam = updateParameterDeclaration(
param,
// Must pass 'undefined' to avoid emitting decorator metadata.
decoratorsToKeep.length ? decoratorsToKeep : undefined, param.modifiers,
combineModifiers(
// Must pass 'undefined' to avoid emitting decorator metadata.
decoratorsToKeep.length ? decoratorsToKeep : undefined, getModifiers(param)),
param.dotDotDotToken, param.name, param.questionToken, param.type, param.initializer);
newParameters.push(newParam);
}
const updated = ts.factory.updateConstructorDeclaration(
ctor, ctor.decorators, ctor.modifiers, newParameters, ctor.body);
const updated =
updateConstructorDeclaration(ctor, getModifiers(ctor), newParameters, ctor.body);
return [updated, parametersInfo];
}
@@ -519,7 +532,7 @@ export function getDownlevelDecoratorsTransform(
// decorators that will never be Angular decorators. So we cannot rely on it to capture all
// the decorators that should be kept. Instead we start off with a set of the raw decorators
// on the class, and only remove the ones that have been identified for downleveling.
const decoratorsToKeep = new Set<ts.Decorator>(classDecl.decorators);
const decoratorsToKeep = new Set<ts.Decorator>(getDecorators(classDecl));
const possibleAngularDecorators = host.getDecoratorsOfDeclaration(classDecl) || [];
let hasAngularDecorator = false;
@@ -561,10 +574,12 @@ export function getDownlevelDecoratorsTransform(
ts.factory.createNodeArray(newMembers, classDecl.members.hasTrailingComma),
classDecl.members);
return ts.factory.updateClassDeclaration(
classDecl, decoratorsToKeep.size ? Array.from(decoratorsToKeep) : undefined,
classDecl.modifiers, classDecl.name, classDecl.typeParameters, classDecl.heritageClauses,
members);
return updateClassDeclaration(
classDecl,
combineModifiers(
decoratorsToKeep.size ? Array.from(decoratorsToKeep) : undefined,
getModifiers(classDecl)),
classDecl.name, classDecl.typeParameters, classDecl.heritageClauses, members);
}
/**
@@ -26,7 +26,7 @@ const MIN_TS_VERSION = '4.6.2';
* Note: this check is disabled in g3, search for
* `angularCompilerOptions.disableTypeScriptVersionCheck` config param value in g3.
*/
const MAX_TS_VERSION = '4.8.0';
const MAX_TS_VERSION = '4.9.0';
/**
* The currently used version of TypeScript, which can be adjusted for testing purposes using
@@ -625,7 +625,7 @@ describe('downlevel decorator transform', () => {
export class MyDir {
constructor(@Inject('$state') param: angular.IState,
@Inject('$overlay') other: IOverlay,
@Inject('$default') default: TypeFromDefaultImport,
@Inject('$default') fromDefaultImport: TypeFromDefaultImport,
@Inject('$keyCodes') keyCodes: KeyCodes) {}
}
`);
@@ -652,6 +652,7 @@ describe('downlevel decorator transform', () => {
if (ts.isClassDeclaration(node) || ts.isClassElement(node)) {
const cloned = ts.getMutableClone(node);
(cloned.decorators as undefined) = undefined;
(cloned.modifiers as undefined) = undefined;
return cloned;
}
return ts.visitEachChild(node, visitNode, context);
@@ -96,7 +96,8 @@ describe('element discovery', () => {
fixture.detectChanges();
const host = fixture.nativeElement;
const [section, div1, p, div2] = Array.from(host.querySelectorAll('section, div, p'));
const [section, div1, p, div2] =
Array.from<HTMLElement>(host.querySelectorAll('section, div, p'));
expect(section.nodeName.toLowerCase()).toBe('section');
expect(readPatchedData(section)).toBeTruthy();
+1 -1
View File
@@ -64,7 +64,7 @@ abstract class OverrideResolver<T> implements Resolver<T> {
}
resolve(type: Type<any>): T|null {
let resolved = this.resolved.get(type) || null;
let resolved: T|null = this.resolved.get(type) || null;
if (!resolved) {
resolved = this.getAnnotation(type);
+1 -1
View File
@@ -19,7 +19,7 @@
"mocha": "^10.0.0",
"mock-require": "3.0.3",
"promises-aplus-tests": "^2.1.2",
"typescript": "~4.7.2"
"typescript": "4.8.1-rc"
},
"scripts": {
"closuretest": "./scripts/closure/closure_compiler.sh",
+1 -1
View File
@@ -14,6 +14,6 @@
"zone.js": "file:../../../../dist/bin/packages/zone.js/npm_package"
},
"devDependencies": {
"typescript": "~4.7.2"
"typescript": "4.8.1-rc"
}
}
+4 -4
View File
@@ -4049,10 +4049,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@~4.7.2:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
typescript@4.8.1-rc:
version "4.8.1-rc"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.1-rc.tgz#2baff2b14b916f06a97effbfcf59e46bab93e48a"
integrity sha512-ZoXadPUeEe1XOZe6CHG/QHZ6IFeRjrfzkpraRi9HOpGH0UOG/WaUrKvtSwDFigG8GuDA4zsDQHEZyqhmlCIyEw==
unbox-primitive@^1.0.2:
version "1.0.2"