@ -28,7 +28,7 @@ export class Rule extends Rules.TypedRule {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const queryVisitor = new NgQueryResolveVisitor(program.getTypeChecker());
|
||||
const templateVisitor = new NgComponentTemplateVisitor(typeChecker);
|
||||
const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f) !);
|
||||
const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f)!);
|
||||
const printer = ts.createPrinter();
|
||||
const failures: RuleFailure[] = [];
|
||||
|
||||
@ -44,7 +44,7 @@ export class Rule extends Rules.TypedRule {
|
||||
// check component templates for static query usage.
|
||||
templateVisitor.resolvedTemplates.forEach(template => {
|
||||
if (classMetadata.has(template.container)) {
|
||||
classMetadata.get(template.container) !.template = template;
|
||||
classMetadata.get(template.container)!.template = template;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -50,7 +50,7 @@ export class Rule extends Rules.TypedRule {
|
||||
transformer.recordChanges();
|
||||
|
||||
if (updateRecorders.has(sourceFile)) {
|
||||
failures.push(...updateRecorders.get(sourceFile) !.failures);
|
||||
failures.push(...updateRecorders.get(sourceFile)!.failures);
|
||||
}
|
||||
|
||||
return failures;
|
||||
@ -58,7 +58,7 @@ export class Rule extends Rules.TypedRule {
|
||||
/** Gets the update recorder for the specified source file. */
|
||||
function getUpdateRecorder(sourceFile: ts.SourceFile): TslintUpdateRecorder {
|
||||
if (updateRecorders.has(sourceFile)) {
|
||||
return updateRecorders.get(sourceFile) !;
|
||||
return updateRecorders.get(sourceFile)!;
|
||||
}
|
||||
const recorder = new TslintUpdateRecorder(ruleName, sourceFile);
|
||||
updateRecorders.set(sourceFile, recorder);
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {Replacement, RuleFailure, Rules} from 'tslint';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {HelperFunction, getHelper} from '../renderer-to-renderer2/helpers';
|
||||
import {getHelper, HelperFunction} from '../renderer-to-renderer2/helpers';
|
||||
import {migrateExpression, replaceImport} from '../renderer-to-renderer2/migration';
|
||||
import {findCoreImport, findRendererReferences} from '../renderer-to-renderer2/util';
|
||||
|
||||
@ -75,7 +75,7 @@ export class Rule extends Rules.TypedRule {
|
||||
private _getTypedNodeFailure(
|
||||
node: ts.ParameterDeclaration|ts.PropertyDeclaration|ts.AsExpression,
|
||||
sourceFile: ts.SourceFile): RuleFailure {
|
||||
const type = node.type !;
|
||||
const type = node.type!;
|
||||
|
||||
return new RuleFailure(
|
||||
sourceFile, type.getStart(), type.getEnd(),
|
||||
|
@ -32,14 +32,14 @@ export class Rule extends Rules.TypedRule {
|
||||
transform.recordChanges();
|
||||
|
||||
if (updateRecorders.has(sourceFile)) {
|
||||
return updateRecorders.get(sourceFile) !.failures;
|
||||
return updateRecorders.get(sourceFile)!.failures;
|
||||
}
|
||||
return [];
|
||||
|
||||
/** Gets the update recorder for the specified source file. */
|
||||
function getUpdateRecorder(sourceFile: ts.SourceFile): TslintUpdateRecorder {
|
||||
if (updateRecorders.has(sourceFile)) {
|
||||
return updateRecorders.get(sourceFile) !;
|
||||
return updateRecorders.get(sourceFile)!;
|
||||
}
|
||||
const recorder = new TslintUpdateRecorder(ruleName, sourceFile);
|
||||
updateRecorders.set(sourceFile, recorder);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators';
|
||||
import {getPropertyNameText} from '../../utils/typescript/property_name';
|
||||
|
||||
export interface ResolvedNgModule {
|
||||
|
@ -21,7 +21,8 @@ export class TslintUpdateRecorder implements UpdateRecorder {
|
||||
// are handled in reverse and in case a decorator and import are inserted at
|
||||
// the start of the file, the class decorator should come after the import.
|
||||
this.failures.unshift(new RuleFailure(
|
||||
this.sourceFile, node.getStart(), 0, `Class needs to be decorated with ` +
|
||||
this.sourceFile, node.getStart(), 0,
|
||||
`Class needs to be decorated with ` +
|
||||
`"${decoratorText}" because it has been provided by "${className}".`,
|
||||
this.ruleName, Replacement.appendText(node.getStart(), `${decoratorText}\n`)));
|
||||
}
|
||||
|
@ -46,7 +46,9 @@ export class MissingInjectableTransform {
|
||||
new TypeScriptReflectionHost(typeChecker), typeChecker, /* dependencyTracker */ null);
|
||||
}
|
||||
|
||||
recordChanges() { this.importManager.recordChanges(); }
|
||||
recordChanges() {
|
||||
this.importManager.recordChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates all specified NgModule's by walking through referenced providers
|
||||
@ -76,10 +78,9 @@ export class MissingInjectableTransform {
|
||||
this._migrateLiteralProviders(literals);
|
||||
|
||||
if (!Array.isArray(resolvedValue)) {
|
||||
return [{
|
||||
node: module.providersExpr,
|
||||
message: 'Providers of module are not statically analyzable.'
|
||||
}];
|
||||
return [
|
||||
{node: module.providersExpr, message: 'Providers of module are not statically analyzable.'}
|
||||
];
|
||||
}
|
||||
|
||||
return this._visitProviderResolvedValue(resolvedValue, module);
|
||||
@ -194,8 +195,9 @@ export class MissingInjectableTransform {
|
||||
|
||||
const sourceFile = node.getSourceFile();
|
||||
const newObjectLiteral = ts.updateObjectLiteral(
|
||||
node, node.properties.concat(
|
||||
ts.createPropertyAssignment('useValue', ts.createIdentifier('undefined'))));
|
||||
node,
|
||||
node.properties.concat(
|
||||
ts.createPropertyAssignment('useValue', ts.createIdentifier('undefined'))));
|
||||
|
||||
this.getUpdateRecorder(sourceFile)
|
||||
.updateObjectLiteral(
|
||||
@ -217,11 +219,12 @@ export class MissingInjectableTransform {
|
||||
// decorate the class. This is because the class is instantiated through the
|
||||
// specified "deps" and the class does not need a factory definition.
|
||||
if (value.has('provide') && value.has('useClass') && value.get('deps') == null) {
|
||||
return this._visitProviderResolvedValue(value.get('useClass') !, module);
|
||||
return this._visitProviderResolvedValue(value.get('useClass')!, module);
|
||||
}
|
||||
} else if (Array.isArray(value)) {
|
||||
return value.reduce((res, v) => res.concat(this._visitProviderResolvedValue(v, module)), [
|
||||
] as AnalysisFailure[]);
|
||||
return value.reduce(
|
||||
(res, v) => res.concat(this._visitProviderResolvedValue(v, module)),
|
||||
[] as AnalysisFailure[]);
|
||||
} else if (value instanceof DynamicValue) {
|
||||
return [{node: value.node, message: `Provider is not statically analyzable.`}];
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators';
|
||||
|
||||
import {isModuleWithProvidersNotGeneric} from './util';
|
||||
|
||||
export interface ResolvedNgModule {
|
||||
|
@ -35,7 +35,7 @@ export class ModuleWithProvidersTransform {
|
||||
/** Migrates a given NgModule by walking through the referenced providers and static methods. */
|
||||
migrateModule(module: ResolvedNgModule): AnalysisFailure[] {
|
||||
return module.staticMethodsWithoutType.map(this._migrateStaticNgModuleMethod.bind(this))
|
||||
.filter(v => v) as AnalysisFailure[];
|
||||
.filter(v => v) as AnalysisFailure[];
|
||||
}
|
||||
|
||||
/** Migrates a ModuleWithProviders type definition that has no explicit generic type */
|
||||
@ -98,8 +98,10 @@ export class ModuleWithProvidersTransform {
|
||||
return ngModule && (value.size === 1 || (providers && value.size === 2));
|
||||
}
|
||||
|
||||
/** Determine the generic type of a suspected ModuleWithProviders return type and add it
|
||||
* explicitly */
|
||||
/**
|
||||
* Determine the generic type of a suspected ModuleWithProviders return type and add it
|
||||
* explicitly
|
||||
*/
|
||||
private _migrateStaticNgModuleMethod(node: ts.MethodDeclaration): AnalysisFailure|null {
|
||||
const returnStatement = node.body &&
|
||||
node.body.statements.find(n => ts.isReturnStatement(n)) as ts.ReturnStatement | undefined;
|
||||
@ -131,7 +133,7 @@ export class ModuleWithProvidersTransform {
|
||||
*/
|
||||
private _getTypeOfResolvedValue(value: ResolvedValue): string|undefined {
|
||||
if (value instanceof Map && this.isModuleWithProvidersType(value)) {
|
||||
const mapValue = value.get('ngModule') !;
|
||||
const mapValue = value.get('ngModule')!;
|
||||
if (mapValue instanceof Reference && ts.isClassDeclaration(mapValue.node) &&
|
||||
mapValue.node.name) {
|
||||
return mapValue.node.name.text;
|
||||
|
@ -69,7 +69,7 @@ function createAnyTypeHelper(): ts.TypeAliasDeclaration {
|
||||
|
||||
/** Creates a function parameter that is typed as `any`. */
|
||||
function getAnyTypedParameter(
|
||||
parameterName: string | ts.Identifier, isRequired = true): ts.ParameterDeclaration {
|
||||
parameterName: string|ts.Identifier, isRequired = true): ts.ParameterDeclaration {
|
||||
// Declare the parameter as `any` so we don't have to add extra logic to ensure that the
|
||||
// generated code will pass type checking. Use our custom `any` type so people have an incentive
|
||||
// to clean it up afterwards and to avoid potentially introducing lint warnings in G3.
|
||||
@ -153,10 +153,11 @@ function getCreationHelper(
|
||||
|
||||
// `if (parent) { renderer.appendChild(parent, node) }`.
|
||||
const guardedAppendChildCall = ts.createIf(
|
||||
parent, ts.createBlock(
|
||||
[ts.createExpressionStatement(ts.createCall(
|
||||
ts.createPropertyAccess(renderer, 'appendChild'), [], [parent, node]))],
|
||||
true));
|
||||
parent,
|
||||
ts.createBlock(
|
||||
[ts.createExpressionStatement(
|
||||
ts.createCall(ts.createPropertyAccess(renderer, 'appendChild'), [], [parent, node]))],
|
||||
true));
|
||||
|
||||
return ts.createFunctionDeclaration(
|
||||
[], [], undefined, functionName, [],
|
||||
@ -258,10 +259,11 @@ function getDetachViewHelper(): ts.FunctionDeclaration {
|
||||
|
||||
// const node = rootNodes[i];
|
||||
const nodeVariableStatement = ts.createVariableStatement(
|
||||
undefined, ts.createVariableDeclarationList(
|
||||
[ts.createVariableDeclaration(
|
||||
node, undefined, ts.createElementAccess(rootNodes, incrementor))],
|
||||
ts.NodeFlags.Const));
|
||||
undefined,
|
||||
ts.createVariableDeclarationList(
|
||||
[ts.createVariableDeclaration(
|
||||
node, undefined, ts.createElementAccess(rootNodes, incrementor))],
|
||||
ts.NodeFlags.Const));
|
||||
// renderer.removeChild(renderer.parentNode(node), node);
|
||||
const removeCall = ts.createCall(
|
||||
ts.createPropertyAccess(renderer, 'removeChild'), [],
|
||||
|
@ -12,7 +12,7 @@ import {HelperFunction} from './helpers';
|
||||
import {findImportSpecifier} from './util';
|
||||
|
||||
/** A call expression that is based on a property access. */
|
||||
type PropertyAccessCallExpression = ts.CallExpression & {expression: ts.PropertyAccessExpression};
|
||||
type PropertyAccessCallExpression = ts.CallExpression&{expression: ts.PropertyAccessExpression};
|
||||
|
||||
/** Replaces an import inside an import statement with a different one. */
|
||||
export function replaceImport(node: ts.NamedImports, oldImport: string, newImport: string) {
|
||||
@ -42,7 +42,7 @@ export function replaceImport(node: ts.NamedImports, oldImport: string, newImpor
|
||||
* Returns null if the expression should be dropped.
|
||||
*/
|
||||
export function migrateExpression(node: ts.CallExpression, typeChecker: ts.TypeChecker):
|
||||
{node: ts.Node | null, requiredHelpers?: HelperFunction[]} {
|
||||
{node: ts.Node|null, requiredHelpers?: HelperFunction[]} {
|
||||
if (isPropertyAccessCallExpression(node)) {
|
||||
switch (node.expression.name.getText()) {
|
||||
case 'setElementProperty':
|
||||
@ -152,7 +152,7 @@ function migrateSetElementClass(node: PropertyAccessCallExpression): ts.Node {
|
||||
// Clone so we don't mutate by accident. Note that we assume that
|
||||
// the user's code is providing all three required arguments.
|
||||
const outputMethodArgs = node.arguments.slice();
|
||||
const isAddArgument = outputMethodArgs.pop() !;
|
||||
const isAddArgument = outputMethodArgs.pop()!;
|
||||
const createRendererCall = (isAdd: boolean) => {
|
||||
const innerExpression = node.expression.expression;
|
||||
const topExpression =
|
||||
@ -263,6 +263,6 @@ function migrateAnimateCall() {
|
||||
*/
|
||||
function switchToHelperCall(
|
||||
node: PropertyAccessCallExpression, helper: HelperFunction,
|
||||
args: ts.Expression[] | ts.NodeArray<ts.Expression>): ts.Node {
|
||||
args: ts.Expression[]|ts.NodeArray<ts.Expression>): ts.Node {
|
||||
return ts.createCall(ts.createIdentifier(helper), [], [node.expression.expression, ...args]);
|
||||
}
|
||||
|
@ -88,8 +88,7 @@ export function findImportSpecifier(
|
||||
|
||||
/** Checks whether a node is referring to an import spcifier. */
|
||||
function isReferenceToImport(
|
||||
typeChecker: ts.TypeChecker, node: ts.Node,
|
||||
importSpecifier: ts.ImportSpecifier | null): boolean {
|
||||
typeChecker: ts.TypeChecker, node: ts.Node, importSpecifier: ts.ImportSpecifier|null): boolean {
|
||||
if (importSpecifier) {
|
||||
const nodeSymbol = typeChecker.getTypeAtLocation(node).getSymbol();
|
||||
const importSymbol = typeChecker.getTypeAtLocation(importSpecifier).getSymbol();
|
||||
@ -102,7 +101,7 @@ function isReferenceToImport(
|
||||
/** Finds the identifier referring to the `Renderer` inside a `forwardRef` call expression. */
|
||||
function findRendererIdentifierInForwardRef(
|
||||
typeChecker: ts.TypeChecker, node: ts.CallExpression,
|
||||
rendererImport: ts.ImportSpecifier | null): ts.Identifier|null {
|
||||
rendererImport: ts.ImportSpecifier|null): ts.Identifier|null {
|
||||
const firstArg = node.arguments[0];
|
||||
|
||||
if (ts.isArrowFunction(firstArg)) {
|
||||
|
@ -24,7 +24,7 @@ export function getInputNamesOfClass(
|
||||
}
|
||||
|
||||
const inputDecorator =
|
||||
getAngularDecorators(typeChecker, m.decorators !).find(d => d.name === 'Input');
|
||||
getAngularDecorators(typeChecker, m.decorators!).find(d => d.name === 'Input');
|
||||
|
||||
if (inputDecorator && hasPropertyNameText(m.name)) {
|
||||
resolvedInputSetters.push(m.name.text);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {AotCompiler, CompileDirectiveMetadata, CompileMetadataResolver, CompileNgModuleMetadata, CompileStylesheetMetadata, ElementAst, EmbeddedTemplateAst, NgAnalyzedModules, QueryMatch, StaticSymbol, TemplateAst} from '@angular/compiler';
|
||||
import {Diagnostic, createProgram, readConfiguration} from '@angular/compiler-cli';
|
||||
import {createProgram, Diagnostic, readConfiguration} from '@angular/compiler-cli';
|
||||
import {resolve} from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@ -46,17 +46,17 @@ export class QueryTemplateStrategy implements TimingStrategy {
|
||||
// expose the logic that is necessary to analyze the determined modules. We work around
|
||||
// this by just accessing the necessary private properties using the bracket notation.
|
||||
this.compiler = (aotProgram as any)['compiler'];
|
||||
this.metadataResolver = this.compiler !['_metadataResolver'];
|
||||
this.metadataResolver = this.compiler!['_metadataResolver'];
|
||||
|
||||
// Modify the "DirectiveNormalizer" to not normalize any referenced external stylesheets.
|
||||
// This is necessary because in CLI projects preprocessor files are commonly referenced
|
||||
// and we don't want to parse them in order to extract relative style references. This
|
||||
// breaks the analysis of the project because we instantiate a standalone AOT compiler
|
||||
// program which does not contain the custom logic by the Angular CLI Webpack compiler plugin.
|
||||
const directiveNormalizer = this.metadataResolver !['_directiveNormalizer'];
|
||||
const directiveNormalizer = this.metadataResolver!['_directiveNormalizer'];
|
||||
directiveNormalizer['_normalizeStylesheet'] = function(metadata: CompileStylesheetMetadata) {
|
||||
return new CompileStylesheetMetadata(
|
||||
{styles: metadata.styles, styleUrls: [], moduleUrl: metadata.moduleUrl !});
|
||||
{styles: metadata.styles, styleUrls: [], moduleUrl: metadata.moduleUrl!});
|
||||
};
|
||||
|
||||
// Retrieves the analyzed modules of the current program. This data can be
|
||||
@ -75,7 +75,7 @@ export class QueryTemplateStrategy implements TimingStrategy {
|
||||
|
||||
/** Analyzes a given directive by determining the timing of all matched view queries. */
|
||||
private _analyzeDirective(symbol: StaticSymbol, analyzedModules: NgAnalyzedModules) {
|
||||
const metadata = this.metadataResolver !.getDirectiveMetadata(symbol);
|
||||
const metadata = this.metadataResolver!.getDirectiveMetadata(symbol);
|
||||
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(symbol);
|
||||
|
||||
if (!metadata.isComponent || !ngModule) {
|
||||
@ -168,7 +168,7 @@ export class QueryTemplateStrategy implements TimingStrategy {
|
||||
const queryKey = this._getViewQueryUniqueKey(filePath, classDecl.name.text, queryName);
|
||||
|
||||
if (this.analyzedQueries.has(queryKey)) {
|
||||
return this.analyzedQueries.get(queryKey) !;
|
||||
return this.analyzedQueries.get(queryKey)!;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -176,7 +176,7 @@ export class QueryTemplateStrategy implements TimingStrategy {
|
||||
private _parseTemplate(component: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata):
|
||||
TemplateAst[] {
|
||||
return this
|
||||
.compiler 
|
||||
.compiler
|
||||
.template;
|
||||
}
|
||||
|
||||
@ -201,11 +201,11 @@ function findStaticQueryIds(
|
||||
nodes.forEach((node) => {
|
||||
const staticQueryIds = new Set<number>();
|
||||
const dynamicQueryIds = new Set<number>();
|
||||
let queryMatches: QueryMatch[] = undefined !;
|
||||
let queryMatches: QueryMatch[] = undefined!;
|
||||
if (node instanceof ElementAst) {
|
||||
findStaticQueryIds(node.children, result);
|
||||
node.children.forEach((child) => {
|
||||
const childData = result.get(child) !;
|
||||
const childData = result.get(child)!;
|
||||
childData.staticQueryIds.forEach(queryId => staticQueryIds.add(queryId));
|
||||
childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
||||
});
|
||||
@ -213,7 +213,7 @@ function findStaticQueryIds(
|
||||
} else if (node instanceof EmbeddedTemplateAst) {
|
||||
findStaticQueryIds(node.children, result);
|
||||
node.children.forEach((child) => {
|
||||
const childData = result.get(child) !;
|
||||
const childData = result.get(child)!;
|
||||
childData.staticQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
||||
childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId));
|
||||
});
|
||||
|
@ -16,5 +16,6 @@ export interface TimingStrategy {
|
||||
}
|
||||
|
||||
export type TimingResult = {
|
||||
timing: QueryTiming | null; message?: string;
|
||||
timing: QueryTiming|null;
|
||||
message?: string;
|
||||
};
|
||||
|
@ -167,7 +167,7 @@ export class DeclarationUsageVisitor {
|
||||
d.body && !this.visitedJumpExprNodes.has(d))
|
||||
.forEach(d => {
|
||||
this.visitedJumpExprNodes.add(d);
|
||||
this.nodeQueue.push(d.body !);
|
||||
this.nodeQueue.push(d.body!);
|
||||
});
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@ export class DeclarationUsageVisitor {
|
||||
this.ambiguousNodeQueue = [];
|
||||
|
||||
while (this.nodeQueue.length) {
|
||||
const node = this.nodeQueue.shift() !;
|
||||
const node = this.nodeQueue.shift()!;
|
||||
|
||||
if (ts.isIdentifier(node) && this.isReferringToSymbol(node)) {
|
||||
return ResolvedUsage.SYNCHRONOUS;
|
||||
@ -306,7 +306,7 @@ export class DeclarationUsageVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
jumpExp.arguments !.forEach((node: ts.Node) => {
|
||||
jumpExp.arguments!.forEach((node: ts.Node) => {
|
||||
node = this._resolveDeclarationOfNode(node);
|
||||
|
||||
if (ts.isVariableDeclaration(node) && node.initializer) {
|
||||
@ -325,7 +325,7 @@ export class DeclarationUsageVisitor {
|
||||
*/
|
||||
private _resolveNodeFromContext(node: ts.Node): ts.Node {
|
||||
if (this.context.has(node)) {
|
||||
return this.context.get(node) !;
|
||||
return this.context.get(node)!;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ export function updateSuperClassAbstractMembersContext(
|
||||
const baseClassImpl = baseClass.members.find(
|
||||
baseClassMethod => !!baseClassMethod.name &&
|
||||
getPropertyNameText(baseClassMethod.name) ===
|
||||
getPropertyNameText(superClassMember.name !));
|
||||
getPropertyNameText(superClassMember.name!));
|
||||
|
||||
if (!baseClassImpl || !isFunctionLikeDeclaration(baseClassImpl) || !baseClassImpl.body) {
|
||||
return;
|
||||
|
@ -17,7 +17,9 @@ export class TemplateUsageVisitor extends NullVisitor {
|
||||
private hasQueryTemplateReference = false;
|
||||
private expressionAstVisitor = new ExpressionAstVisitor(this.queryPropertyName);
|
||||
|
||||
constructor(public queryPropertyName: string) { super(); }
|
||||
constructor(public queryPropertyName: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
/** Checks whether the given query is statically accessed within the specified HTML nodes. */
|
||||
isQueryUsedStatically(htmlNodes: Node[]): boolean {
|
||||
@ -59,7 +61,9 @@ export class TemplateUsageVisitor extends NullVisitor {
|
||||
attribute.value.visit(this.expressionAstVisitor, attribute.sourceSpan);
|
||||
}
|
||||
|
||||
visitBoundText(text: BoundText) { text.value.visit(this.expressionAstVisitor, text.sourceSpan); }
|
||||
visitBoundText(text: BoundText) {
|
||||
text.value.visit(this.expressionAstVisitor, text.sourceSpan);
|
||||
}
|
||||
|
||||
visitBoundEvent(node: BoundEvent) {
|
||||
node.handler.visit(this.expressionAstVisitor, node.handlerSpan);
|
||||
@ -73,7 +77,9 @@ export class TemplateUsageVisitor extends NullVisitor {
|
||||
class ExpressionAstVisitor extends RecursiveAstVisitor {
|
||||
hasQueryPropertyRead = false;
|
||||
|
||||
constructor(private queryPropertyName: string) { super(); }
|
||||
constructor(private queryPropertyName: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
visitPropertyRead(node: PropertyRead, span: ParseSourceSpan): any {
|
||||
// The receiver of the property read needs to be "implicit" as queries are accessed
|
||||
|
@ -70,7 +70,7 @@ export class QueryUsageStrategy implements TimingStrategy {
|
||||
classDecl: ts.ClassDeclaration, query: NgQueryDefinition, knownInputNames: string[],
|
||||
functionCtx: FunctionContext = new Map(), visitInheritedClasses = true): ResolvedUsage {
|
||||
const usageVisitor =
|
||||
new DeclarationUsageVisitor(query.property !, this.typeChecker, functionCtx);
|
||||
new DeclarationUsageVisitor(query.property!, this.typeChecker, functionCtx);
|
||||
const classMetadata = this.classMetadata.get(classDecl);
|
||||
let usage: ResolvedUsage = ResolvedUsage.ASYNCHRONOUS;
|
||||
|
||||
@ -99,10 +99,10 @@ export class QueryUsageStrategy implements TimingStrategy {
|
||||
// In case there is a component template for the current class, we check if the
|
||||
// template statically accesses the current query. In case that's true, the query
|
||||
// can be marked as static.
|
||||
if (classMetadata.template && hasPropertyNameText(query.property !.name)) {
|
||||
if (classMetadata.template && hasPropertyNameText(query.property!.name)) {
|
||||
const template = classMetadata.template;
|
||||
const parsedHtml = parseHtmlGracefully(template.content, template.filePath);
|
||||
const htmlVisitor = new TemplateUsageVisitor(query.property !.name.text);
|
||||
const htmlVisitor = new TemplateUsageVisitor(query.property!.name.text);
|
||||
|
||||
if (parsedHtml && htmlVisitor.isQueryUsedStatically(parsedHtml)) {
|
||||
return ResolvedUsage.SYNCHRONOUS;
|
||||
@ -179,5 +179,5 @@ function filterQueryClassMemberNodes(
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map(member => member.body !);
|
||||
.map(member => member.body!);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import * as ts from 'typescript';
|
||||
import {getPropertyNameText} from '../../utils/typescript/property_name';
|
||||
import {NgQueryDefinition, QueryTiming} from './angular/query-definition';
|
||||
|
||||
export type TransformedQueryResult = null | {
|
||||
export type TransformedQueryResult = null|{
|
||||
/** Transformed call expression. */
|
||||
node: ts.CallExpression;
|
||||
/** Failure message which is set when the query could not be transformed successfully. */
|
||||
@ -25,7 +25,7 @@ const TODO_CHECK_COMMENT = 'TODO: check static flag';
|
||||
* determined timing. The updated decorator call expression node will be returned.
|
||||
*/
|
||||
export function getTransformedQueryCallExpr(
|
||||
query: NgQueryDefinition, timing: QueryTiming | null,
|
||||
query: NgQueryDefinition, timing: QueryTiming|null,
|
||||
createTodo: boolean): TransformedQueryResult {
|
||||
const queryExpr = query.decorator.node.expression;
|
||||
const queryArguments = queryExpr.arguments;
|
||||
@ -81,7 +81,7 @@ export function getTransformedQueryCallExpr(
|
||||
failureMessage,
|
||||
node: ts.updateCall(
|
||||
queryExpr, queryExpr.expression, queryExpr.typeArguments,
|
||||
[queryArguments[0], newOptionsNode !])
|
||||
[queryArguments[0], newOptionsNode!])
|
||||
};
|
||||
}
|
||||
|
||||
@ -94,8 +94,7 @@ export function getTransformedQueryCallExpr(
|
||||
return {
|
||||
failureMessage: null,
|
||||
node: ts.updateCall(
|
||||
queryExpr, queryExpr.expression, queryExpr.typeArguments,
|
||||
[queryArguments[0], optionsNode])
|
||||
queryExpr, queryExpr.expression, queryExpr.typeArguments, [queryArguments[0], optionsNode])
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,8 @@ export class TslintUpdateRecorder implements UpdateRecorder {
|
||||
// are handled in reverse and in case a decorator and import are inserted at
|
||||
// the start of the file, the class decorator should come after the import.
|
||||
this.failures.unshift(new RuleFailure(
|
||||
this.sourceFile, node.getStart(), 0, `Class needs to be decorated with ` +
|
||||
this.sourceFile, node.getStart(), 0,
|
||||
`Class needs to be decorated with ` +
|
||||
`"${decoratorText}" because it uses Angular features.`,
|
||||
this.ruleName, Replacement.appendText(node.getStart(), `${decoratorText}\n`)));
|
||||
}
|
||||
|
@ -7,11 +7,11 @@
|
||||
*/
|
||||
|
||||
import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator';
|
||||
import {TypeScriptReflectionHost, reflectObjectLiteral} from '@angular/compiler-cli/src/ngtsc/reflection';
|
||||
import {reflectObjectLiteral, TypeScriptReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ImportManager} from '../../utils/import_manager';
|
||||
import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators';
|
||||
import {findBaseClassDeclarations} from '../../utils/typescript/find_base_classes';
|
||||
import {unwrapExpression} from '../../utils/typescript/functions';
|
||||
|
||||
@ -56,7 +56,9 @@ export class UndecoratedClassesWithDecoratedFieldsTransform {
|
||||
}
|
||||
|
||||
/** Records all changes that were made in the import manager. */
|
||||
recordChanges() { this.importManager.recordChanges(); }
|
||||
recordChanges() {
|
||||
this.importManager.recordChanges();
|
||||
}
|
||||
|
||||
/** Finds undecorated abstract directives in the specified source files. */
|
||||
private _findUndecoratedAbstractDirectives(sourceFiles: ts.SourceFile[]) {
|
||||
@ -130,9 +132,9 @@ export class UndecoratedClassesWithDecoratedFieldsTransform {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given decorator resolves to an abstract directive. An directive is
|
||||
* considered "abstract" if there is no selector specified.
|
||||
*/
|
||||
* Checks whether the given decorator resolves to an abstract directive. An directive is
|
||||
* considered "abstract" if there is no selector specified.
|
||||
*/
|
||||
private _isAbstractDirective({node}: NgDecorator): boolean {
|
||||
const metadataArgs = node.expression.arguments;
|
||||
if (metadataArgs.length === 0) {
|
||||
@ -146,7 +148,7 @@ export class UndecoratedClassesWithDecoratedFieldsTransform {
|
||||
if (!metadata.has('selector')) {
|
||||
return false;
|
||||
}
|
||||
const selector = this.partialEvaluator.evaluate(metadata.get('selector') !);
|
||||
const selector = this.partialEvaluator.evaluate(metadata.get('selector')!);
|
||||
return selector == null;
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ export function convertDirectiveMetadataToExpression(
|
||||
/**
|
||||
* Gets a valid property name from the given text. If the text cannot be used
|
||||
* as unquoted identifier, the name will be wrapped in a string literal.
|
||||
*/
|
||||
*/
|
||||
function getPropertyName(name: string): string|ts.StringLiteral {
|
||||
// Matches the most common identifiers that do not need quotes. Constructing a
|
||||
// regular expression that matches the ECMAScript specification in order to determine
|
||||
|
@ -122,7 +122,7 @@ export class DecoratorRewriter {
|
||||
|null {
|
||||
try {
|
||||
return ts
|
||||
.transform(prop, [ctx => this.importRewriterFactory.create(ctx, this.newSourceFile !)])
|
||||
.transform(prop, [ctx => this.importRewriterFactory.create(ctx, this.newSourceFile!)])
|
||||
.transformed[0];
|
||||
} catch (e) {
|
||||
// If the error is for an unresolved identifier, we want to return "null" because
|
||||
|
@ -11,11 +11,11 @@ import {dirname, resolve} from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ImportManager} from '../../../utils/import_manager';
|
||||
import {Import, getImportOfIdentifier} from '../../../utils/typescript/imports';
|
||||
import {getImportOfIdentifier, Import} from '../../../utils/typescript/imports';
|
||||
import {getValueSymbolOfDeclaration} from '../../../utils/typescript/symbol';
|
||||
|
||||
import {getPosixPath} from './path_format';
|
||||
import {ResolvedExport, getExportSymbolsOfFile} from './source_file_exports';
|
||||
import {getExportSymbolsOfFile, ResolvedExport} from './source_file_exports';
|
||||
|
||||
|
||||
/**
|
||||
@ -116,7 +116,7 @@ export class ImportRewriteTransformerFactory {
|
||||
*/
|
||||
private _getSourceFileExports(sourceFile: ts.SourceFile): ResolvedExport[] {
|
||||
if (this.sourceFileExports.has(sourceFile)) {
|
||||
return this.sourceFileExports.get(sourceFile) !;
|
||||
return this.sourceFileExports.get(sourceFile)!;
|
||||
}
|
||||
|
||||
const sourceFileExports = getExportSymbolsOfFile(sourceFile, this.typeChecker);
|
||||
|
@ -123,7 +123,7 @@ function runUndecoratedClassesMigration(
|
||||
/** Gets the update recorder for the specified source file. */
|
||||
function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder {
|
||||
if (updateRecorders.has(sourceFile)) {
|
||||
return updateRecorders.get(sourceFile) !;
|
||||
return updateRecorders.get(sourceFile)!;
|
||||
}
|
||||
const treeRecorder = tree.beginUpdate(relative(basePath, sourceFile.fileName));
|
||||
const recorder: UpdateRecorder = {
|
||||
@ -146,7 +146,9 @@ function runUndecoratedClassesMigration(
|
||||
treeRecorder.remove(namedBindings.getStart(), namedBindings.getWidth());
|
||||
treeRecorder.insertRight(namedBindings.getStart(), newNamedBindings);
|
||||
},
|
||||
commitUpdate() { tree.commitUpdate(treeRecorder); }
|
||||
commitUpdate() {
|
||||
tree.commitUpdate(treeRecorder);
|
||||
}
|
||||
};
|
||||
updateRecorders.set(sourceFile, recorder);
|
||||
return recorder;
|
||||
|
@ -10,7 +10,7 @@ import {Reference} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {PartialEvaluator, ResolvedValue} from '@angular/compiler-cli/src/ngtsc/partial_evaluator';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators';
|
||||
import {getPropertyNameText} from '../../utils/typescript/property_name';
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ import {hasExplicitConstructor} from '../../utils/typescript/class_declaration';
|
||||
import {findBaseClassDeclarations} from '../../utils/typescript/find_base_classes';
|
||||
import {getImportOfIdentifier} from '../../utils/typescript/imports';
|
||||
|
||||
import {UnexpectedMetadataValueError, convertDirectiveMetadataToExpression} from './decorator_rewrite/convert_directive_metadata';
|
||||
import {convertDirectiveMetadataToExpression, UnexpectedMetadataValueError} from './decorator_rewrite/convert_directive_metadata';
|
||||
import {DecoratorRewriter} from './decorator_rewrite/decorator_rewriter';
|
||||
import {hasDirectiveDecorator, hasInjectableDecorator} from './ng_declaration_collector';
|
||||
import {UpdateRecorder} from './update_recorder';
|
||||
@ -316,7 +316,9 @@ export class UndecoratedClassesTransform {
|
||||
}
|
||||
|
||||
/** Records all changes that were made in the import manager. */
|
||||
recordChanges() { this.importManager.recordChanges(); }
|
||||
recordChanges() {
|
||||
this.importManager.recordChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a TypeScript decorator node from the specified declaration metadata. Returns
|
||||
|
@ -47,7 +47,7 @@ describe('dynamic queries migration', () => {
|
||||
});
|
||||
|
||||
it('should remove the options object from a dynamic ViewChild query that only has one property',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ViewChild } from '@angular/core';
|
||||
|
||||
@ -62,7 +62,7 @@ describe('dynamic queries migration', () => {
|
||||
});
|
||||
|
||||
it('should remove the options object from a dynamic ContentChild query that only has one property',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ContentChild } from '@angular/core';
|
||||
|
||||
@ -77,7 +77,7 @@ describe('dynamic queries migration', () => {
|
||||
});
|
||||
|
||||
it('should only remove the `static` flag from a ViewChild query if it has more than one property',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ViewChild, ElementRef } from '@angular/core';
|
||||
|
||||
@ -93,7 +93,7 @@ describe('dynamic queries migration', () => {
|
||||
});
|
||||
|
||||
it('should only remove the `static` flag from a ContentChild query if it has more than one property',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ContentChild, ElementRef } from '@angular/core';
|
||||
|
||||
@ -108,7 +108,7 @@ describe('dynamic queries migration', () => {
|
||||
.toContain(`@ContentChild('child', { read: ElementRef }) child: ElementRef;`);
|
||||
});
|
||||
|
||||
it('should not change static ViewChild queries', async() => {
|
||||
it('should not change static ViewChild queries', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ViewChild, ElementRef } from '@angular/core';
|
||||
|
||||
@ -123,7 +123,7 @@ describe('dynamic queries migration', () => {
|
||||
.toContain(`@ViewChild('child', { read: ElementRef, static: true }) child: ElementRef;`);
|
||||
});
|
||||
|
||||
it('should not change static ContentChild queries', async() => {
|
||||
it('should not change static ContentChild queries', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ContentChild, ElementRef } from '@angular/core';
|
||||
|
||||
@ -138,7 +138,7 @@ describe('dynamic queries migration', () => {
|
||||
.toContain(`@ContentChild('child', { static: true, read: ElementRef }) child: ElementRef;`);
|
||||
});
|
||||
|
||||
it('should migrate dynamic queries on a setter', async() => {
|
||||
it('should migrate dynamic queries on a setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Directive, ContentChild, ViewChild } from '@angular/core';
|
||||
|
||||
|
@ -17,7 +17,7 @@ describe('Google3 dynamic queries TSLint rule', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
|
||||
@ -31,7 +31,7 @@ describe('Google3 dynamic queries TSLint rule', () => {
|
||||
const config = Configuration.parseConfigFile({rules: {'dynamic-queries': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
@ -41,7 +41,9 @@ describe('Google3 dynamic queries TSLint rule', () => {
|
||||
writeFileSync(join(tmpDir, fileName), content);
|
||||
}
|
||||
|
||||
function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); }
|
||||
function getFile(fileName: string) {
|
||||
return readFileSync(join(tmpDir, fileName), 'utf8');
|
||||
}
|
||||
|
||||
it('should flag dynamic queries', () => {
|
||||
writeFile('/index.ts', `
|
||||
@ -172,5 +174,4 @@ describe('Google3 dynamic queries TSLint rule', () => {
|
||||
expect(content).toContain(`@ContentChild('child') set child(c: any) {}`);
|
||||
expect(content).toContain(`@ViewChild('otherChild') set otherChild(c: any) {}`);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ import * as shx from 'shelljs';
|
||||
import {Configuration, Linter} from 'tslint';
|
||||
|
||||
describe('Google3 explicitQueryTiming TSLint rule', () => {
|
||||
|
||||
/**
|
||||
* Path to the static-query schematic rules directory. The path needs to be resolved through
|
||||
* the Bazel runfiles, because on Windows runfiles are not symlinked into the working directory.
|
||||
@ -23,7 +22,7 @@ describe('Google3 explicitQueryTiming TSLint rule', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
|
||||
@ -41,7 +40,7 @@ describe('Google3 explicitQueryTiming TSLint rule', () => {
|
||||
const config = Configuration.parseConfigFile({rules: {'explicit-query-timing': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
|
@ -18,7 +18,7 @@ describe('Google3 missing injectable tslint rule', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
|
||||
@ -32,7 +32,7 @@ describe('Google3 missing injectable tslint rule', () => {
|
||||
const config = Configuration.parseConfigFile({rules: {'no-missing-injectable': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
@ -42,7 +42,9 @@ describe('Google3 missing injectable tslint rule', () => {
|
||||
writeFileSync(join(tmpDir, fileName), content);
|
||||
}
|
||||
|
||||
function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); }
|
||||
function getFile(fileName: string) {
|
||||
return readFileSync(join(tmpDir, fileName), 'utf8');
|
||||
}
|
||||
|
||||
describe('NgModule', () => createTests('NgModule', 'providers'));
|
||||
describe('Directive', () => createTests('Directive', 'providers'));
|
||||
@ -77,7 +79,7 @@ describe('Google3 missing injectable tslint rule', () => {
|
||||
});
|
||||
|
||||
function createTests(
|
||||
type: 'NgModule' | 'Directive' | 'Component', propName: 'providers' | 'viewProviders') {
|
||||
type: 'NgModule'|'Directive'|'Component', propName: 'providers'|'viewProviders') {
|
||||
it('should create proper failures for missing injectable providers', () => {
|
||||
writeFile('index.ts', `
|
||||
import { ${type} } from '@angular/core';
|
||||
@ -266,6 +268,4 @@ describe('Google3 missing injectable tslint rule', () => {
|
||||
.toMatch(/import { Inject, Injectable } from '@angular\/core';/);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ describe('Google3 noTemplateVariableAssignment TSLint rule', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
|
||||
@ -34,7 +34,7 @@ describe('Google3 noTemplateVariableAssignment TSLint rule', () => {
|
||||
Configuration.parseConfigFile({rules: {'no-template-variable-assignment': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
|
@ -18,7 +18,7 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
// We need to declare the Angular symbols we're testing for, otherwise type checking won't work.
|
||||
@ -46,7 +46,7 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => {
|
||||
const config = Configuration.parseConfigFile({rules: {'renderer-to-renderer2': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
@ -56,7 +56,9 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => {
|
||||
writeFileSync(join(tmpDir, fileName), content);
|
||||
}
|
||||
|
||||
function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); }
|
||||
function getFile(fileName: string) {
|
||||
return readFileSync(join(tmpDir, fileName), 'utf8');
|
||||
}
|
||||
|
||||
it('should flag Renderer imports and typed nodes', () => {
|
||||
writeFile('/index.ts', `
|
||||
@ -223,8 +225,8 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => {
|
||||
runTSLint(true);
|
||||
const content = getFile('index.ts');
|
||||
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length).toBe(1);
|
||||
expect(content.match(/function __ngRendererSetElementAttributeHelper\(/g) !.length).toBe(1);
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length).toBe(1);
|
||||
expect(content.match(/function __ngRendererSetElementAttributeHelper\(/g)!.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should insert helpers after the user\'s code', () => {
|
||||
@ -410,5 +412,4 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => {
|
||||
// Expect the `setInfo` method to only contain whitespace.
|
||||
expect(content).toMatch(/setInfo\(\) \{\s+\}/);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
|
||||
});
|
||||
@ -33,7 +33,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
@ -43,7 +43,9 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
writeFileSync(join(tmpDir, fileName), content);
|
||||
}
|
||||
|
||||
function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); }
|
||||
function getFile(fileName: string) {
|
||||
return readFileSync(join(tmpDir, fileName), 'utf8');
|
||||
}
|
||||
|
||||
it('should flag undecorated classes with decorated fields', () => {
|
||||
writeFile('/index.ts', `
|
||||
@ -97,8 +99,9 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
expect(getFile('/index.ts')).toContain(`import { Directive, Input } from '@angular/core';`);
|
||||
});
|
||||
|
||||
it('should not generate conflicting imports there is a different `Directive` symbol', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should not generate conflicting imports there is a different `Directive` symbol',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { HostBinding } from '@angular/core';
|
||||
|
||||
export class Directive {
|
||||
@ -111,12 +114,12 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
}
|
||||
`);
|
||||
|
||||
runTSLint(true);
|
||||
const fileContent = getFile('/index.ts');
|
||||
expect(fileContent)
|
||||
.toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`);
|
||||
expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/);
|
||||
});
|
||||
runTSLint(true);
|
||||
const fileContent = getFile('/index.ts');
|
||||
expect(fileContent)
|
||||
.toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`);
|
||||
expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/);
|
||||
});
|
||||
|
||||
it('should add @Directive to undecorated classes that have @Input', () => {
|
||||
writeFile('/index.ts', `
|
||||
@ -250,7 +253,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () =>
|
||||
expect(getFile('/index.ts')).toContain(`@Directive()\nexport class Base {`);
|
||||
});
|
||||
|
||||
it('should add @Directive to undecorated derived classes of a migrated class', async() => {
|
||||
it('should add @Directive to undecorated derived classes of a migrated class', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Input, Directive, NgModule } from '@angular/core';
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {computeLineStartsMap, getLineAndCharacterFromPosition} from '../utils/line_mappings';
|
||||
|
||||
describe('line mappings', () => {
|
||||
|
||||
it('should properly compute line starts',
|
||||
() => {
|
||||
expect(computeLineStartsMap(`
|
||||
|
@ -76,7 +76,7 @@ describe('Missing injectable migration', () => {
|
||||
|
||||
it('should migrate all providers defined in "viewProviders" and "providers" in the ' +
|
||||
'same component',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -102,8 +102,8 @@ describe('Missing injectable migration', () => {
|
||||
});
|
||||
|
||||
function createTests(
|
||||
type: 'NgModule' | 'Directive' | 'Component', propName: 'providers' | 'viewProviders') {
|
||||
it(`should migrate type provider in ${type}`, async() => {
|
||||
type: 'NgModule'|'Directive'|'Component', propName: 'providers'|'viewProviders') {
|
||||
it(`should migrate type provider in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -121,7 +121,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ ${type}, Injectable } from '@angular/core`);
|
||||
});
|
||||
|
||||
it(`should migrate object literal provider in ${type} to explicit value provider`, async() => {
|
||||
it(`should migrate object literal provider in ${type} to explicit value provider`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -140,7 +140,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`{${type}} from '@angular/core`);
|
||||
});
|
||||
|
||||
it(`should migrate object literal provider with forwardRef in ${type}`, async() => {
|
||||
it(`should migrate object literal provider with forwardRef in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}, forwardRef} from '@angular/core';
|
||||
|
||||
@ -158,7 +158,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ ${type}, forwardRef, Injectable } from '@angular/core`);
|
||||
});
|
||||
|
||||
it(`should not migrate object literal provider with "useValue" in ${type}`, async() => {
|
||||
it(`should not migrate object literal provider with "useValue" in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -174,7 +174,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it(`should not migrate provider with "useClass" and "deps" in ${type}`, async() => {
|
||||
it(`should not migrate provider with "useClass" and "deps" in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -190,7 +190,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it(`should not migrate object literal provider with "useFactory" in ${type}`, async() => {
|
||||
it(`should not migrate object literal provider with "useFactory" in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -206,7 +206,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it(`should not migrate object literal provider with "useExisting" in ${type}`, async() => {
|
||||
it(`should not migrate object literal provider with "useExisting" in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -226,7 +226,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it(`should migrate object literal provider with "useClass" in ${type}`, async() => {
|
||||
it(`should migrate object literal provider with "useClass" in ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -248,7 +248,7 @@ describe('Missing injectable migration', () => {
|
||||
|
||||
it(`should not migrate references for providers with "useExisting" in ${type}, but migrate ` +
|
||||
`existing token if declared in other ${type}`,
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -281,7 +281,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toMatch(/MyService {}\s+export class MyToken/);
|
||||
});
|
||||
|
||||
it('should not migrate provider which is already decorated with @Injectable', async() => {
|
||||
it('should not migrate provider which is already decorated with @Injectable', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Injectable, ${type}} from '@angular/core';
|
||||
|
||||
@ -299,7 +299,7 @@ describe('Missing injectable migration', () => {
|
||||
.toMatch(/@angular\/core';\s+@Injectable\(\)\s+export class MyService/);
|
||||
});
|
||||
|
||||
it('should not migrate provider which is already decorated with @Directive', async() => {
|
||||
it('should not migrate provider which is already decorated with @Directive', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Directive, ${type}} from '@angular/core';
|
||||
|
||||
@ -316,7 +316,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it('should not migrate provider which is already decorated with @Component', async() => {
|
||||
it('should not migrate provider which is already decorated with @Component', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${type}} from '@angular/core';
|
||||
|
||||
@ -333,7 +333,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it('should not migrate provider which is already decorated with @Pipe', async() => {
|
||||
it('should not migrate provider which is already decorated with @Pipe', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Pipe, ${type}} from '@angular/core';
|
||||
|
||||
@ -350,7 +350,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain('@Injectable');
|
||||
});
|
||||
|
||||
it(`should migrate multiple providers in same ${type}`, async() => {
|
||||
it(`should migrate multiple providers in same ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -370,7 +370,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ ${type}, Injectable } from '@angular/core`);
|
||||
});
|
||||
|
||||
it(`should migrate multiple mixed providers in same ${type}`, async() => {
|
||||
it(`should migrate multiple mixed providers in same ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -399,7 +399,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ provide: ServiceB, useValue: undefined },`);
|
||||
});
|
||||
|
||||
it(`should migrate multiple nested providers in same ${type}`, async() => {
|
||||
it(`should migrate multiple nested providers in same ${type}`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -433,7 +433,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ provide: ServiceD, useValue: undefined },`);
|
||||
});
|
||||
|
||||
it('should migrate providers referenced through identifier', async() => {
|
||||
it('should migrate providers referenced through identifier', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -467,7 +467,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`{ provide: ServiceC, useValue: undefined },`);
|
||||
});
|
||||
|
||||
it('should migrate providers created through static analyzable function call', async() => {
|
||||
it('should migrate providers created through static analyzable function call', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -497,7 +497,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`ServiceB, { provide: ServiceC, useValue: undefined }),`);
|
||||
});
|
||||
|
||||
it('should migrate providers which are computed through spread operator', async() => {
|
||||
it('should migrate providers which are computed through spread operator', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -525,7 +525,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`ServiceB, { provide: ServiceC, useValue: undefined }];`);
|
||||
});
|
||||
|
||||
it(`should migrate provider once if referenced in multiple ${type} definitions`, async() => {
|
||||
it(`should migrate provider once if referenced in multiple ${type} definitions`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -559,7 +559,7 @@ describe('Missing injectable migration', () => {
|
||||
|
||||
it(`should only migrate empty object provider literal once if referenced multiple times ` +
|
||||
`in ${type} definitions`,
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/provider.ts', `
|
||||
export class MyService {}
|
||||
|
||||
@ -592,7 +592,7 @@ describe('Missing injectable migration', () => {
|
||||
.toContain(`const PROVIDER = { provide: MyService, useValue: undefined };`);
|
||||
});
|
||||
|
||||
it('should create new import for @Injectable when migrating provider', async() => {
|
||||
it('should create new import for @Injectable when migrating provider', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
import {MyService, MySecondService} from './service';
|
||||
@ -617,7 +617,7 @@ describe('Missing injectable migration', () => {
|
||||
});
|
||||
|
||||
it('should re-use existing namespace import for importing @Injectable when migrating provider',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import * as core from '@angular/core';
|
||||
|
||||
@ -643,7 +643,7 @@ describe('Missing injectable migration', () => {
|
||||
.toMatch(/@core.Injectable\(\)\s+export class MyService/);
|
||||
});
|
||||
|
||||
it('should warn if a referenced individual provider could not be resolved', async() => {
|
||||
it('should warn if a referenced individual provider could not be resolved', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -659,7 +659,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(warnOutput[0]).toContain(`4:${providerSourceTextColumn}:`);
|
||||
});
|
||||
|
||||
it(`should warn if ${propName} value could not be resolved`, async() => {
|
||||
it(`should warn if ${propName} value could not be resolved`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -675,7 +675,7 @@ describe('Missing injectable migration', () => {
|
||||
expect(warnOutput[0]).toContain(`4:${propValueSourceTextColumn}:`);
|
||||
});
|
||||
|
||||
it(`should not throw if an empty @${type} is analyzed`, async() => {
|
||||
it(`should not throw if an empty @${type} is analyzed`, async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
|
||||
@ -693,7 +693,7 @@ describe('Missing injectable migration', () => {
|
||||
});
|
||||
|
||||
it('should create new import for injectable after full end of last import statement',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
import {MyService} from './service';
|
||||
@ -718,7 +718,7 @@ describe('Missing injectable migration', () => {
|
||||
.toMatch(/'b'; \/\/ some comment\s+import { Injectable } from "@angular\/core";/);
|
||||
});
|
||||
|
||||
it('should create new import at source file start with trailing new-line', async() => {
|
||||
it('should create new import at source file start with trailing new-line', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
import {MyService} from './service';
|
||||
@ -739,7 +739,7 @@ describe('Missing injectable migration', () => {
|
||||
/^import { Injectable } from "@angular\/core";\s+\/\* @license \*\/\s+@Injectable\(\)\s+export class MyService/);
|
||||
});
|
||||
|
||||
it('should remove @Inject decorator for providers which are migrated', async() => {
|
||||
it('should remove @Inject decorator for providers which are migrated', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {${type}} from '@angular/core';
|
||||
import {MyService} from './service';
|
||||
@ -766,7 +766,7 @@ describe('Missing injectable migration', () => {
|
||||
.toMatch(/import { Inject, Injectable } from '@angular\/core';/);
|
||||
});
|
||||
|
||||
it('should not migrate provider classes in library type definitions', async() => {
|
||||
it('should not migrate provider classes in library type definitions', async () => {
|
||||
writeFile('/node_modules/my-lib/index.d.ts', `
|
||||
export declare class MyService {}
|
||||
`);
|
||||
|
@ -46,7 +46,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
shx.rm('-r', tmpDirPath);
|
||||
});
|
||||
|
||||
it('should add generic type for function return', async() => {
|
||||
it('should add generic type for function return', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {NgModule, ModuleWithProviders} from '@angular/core';
|
||||
|
||||
@ -69,7 +69,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should add generic type for function return; external file', async() => {
|
||||
it('should add generic type for function return; external file', async () => {
|
||||
writeFile('/module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@ -96,7 +96,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should add generic type for function return without explicit type', async() => {
|
||||
it('should add generic type for function return without explicit type', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@ -119,7 +119,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should add generic type for const variable', async() => {
|
||||
it('should add generic type for const variable', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {ModuleWithProviders, NgModule} from '@angular/core';
|
||||
|
||||
@ -140,7 +140,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should add generic type for const variable without explicit type', async() => {
|
||||
it('should add generic type for const variable without explicit type', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@ -161,7 +161,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should not add generic type for const variable with invalid base object', async() => {
|
||||
it('should not add generic type for const variable with invalid base object', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@ -182,7 +182,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain(`ModuleWithProviders<BaseModule>`);
|
||||
});
|
||||
|
||||
it('should add generic type for const variables and functions with incomplete type', async() => {
|
||||
it('should add generic type for const variables and functions with incomplete type', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {ModuleWithProviders, NgModule} from '@angular/core';
|
||||
|
||||
@ -214,7 +214,7 @@ describe('ModuleWithProviders migration', () => {
|
||||
expect(tree.readContent('/index.ts')).not.toContain(`ModuleWithProviders `);
|
||||
});
|
||||
|
||||
it('should not add generic type for const variables without initialization', async() => {
|
||||
it('should not add generic type for const variables without initialization', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {ModuleWithProviders} from '@angular/core';
|
||||
|
||||
|
@ -47,7 +47,7 @@ describe('move-document migration', () => {
|
||||
});
|
||||
|
||||
describe('move-document', () => {
|
||||
it('should properly apply import replacement', async() => {
|
||||
it('should properly apply import replacement', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {DOCUMENT} from '@angular/platform-browser';
|
||||
`);
|
||||
@ -73,7 +73,7 @@ describe('move-document migration', () => {
|
||||
expect(content).not.toContain(`import {DOCUMENT} from '@angular/platform-browser';`);
|
||||
});
|
||||
|
||||
it('should properly apply import replacement with existing import', async() => {
|
||||
it('should properly apply import replacement with existing import', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {DOCUMENT} from '@angular/platform-browser';
|
||||
import {someImport} from '@angular/common';
|
||||
@ -96,7 +96,7 @@ describe('move-document migration', () => {
|
||||
expect(contentReverse).not.toContain(`import {DOCUMENT} from '@angular/platform-browser';`);
|
||||
});
|
||||
|
||||
it('should properly apply import replacement with existing import w/ comments', async() => {
|
||||
it('should properly apply import replacement with existing import w/ comments', async () => {
|
||||
writeFile('/index.ts', `
|
||||
/**
|
||||
* this is a comment
|
||||
@ -115,7 +115,7 @@ describe('move-document migration', () => {
|
||||
expect(content).toMatch(/.*this is a comment.*/);
|
||||
});
|
||||
|
||||
it('should properly apply import replacement with existing and redundant imports', async() => {
|
||||
it('should properly apply import replacement with existing and redundant imports', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {DOCUMENT} from '@angular/platform-browser';
|
||||
import {anotherImport} from '@angular/platform-browser-dynamic';
|
||||
@ -131,7 +131,7 @@ describe('move-document migration', () => {
|
||||
});
|
||||
|
||||
it('should properly apply import replacement with existing import and leave original import',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {DOCUMENT, anotherImport} from '@angular/platform-browser';
|
||||
import {someImport} from '@angular/common';
|
||||
@ -145,7 +145,7 @@ describe('move-document migration', () => {
|
||||
expect(content).toContain(`import { anotherImport } from '@angular/platform-browser';`);
|
||||
});
|
||||
|
||||
it('should properly apply import replacement with existing import and alias', async() => {
|
||||
it('should properly apply import replacement with existing import and alias', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {DOCUMENT as doc, anotherImport} from '@angular/platform-browser';
|
||||
import {someImport} from '@angular/common';
|
||||
|
@ -13,13 +13,14 @@ import {getProjectTsConfigPaths} from '../utils/project_tsconfig_paths';
|
||||
describe('project tsconfig paths', () => {
|
||||
let testTree: UnitTestTree;
|
||||
|
||||
beforeEach(() => { testTree = new UnitTestTree(new HostTree()); });
|
||||
beforeEach(() => {
|
||||
testTree = new UnitTestTree(new HostTree());
|
||||
});
|
||||
|
||||
it('should detect build tsconfig path inside of angular.json file', () => {
|
||||
testTree.create('/my-custom-config.json', '');
|
||||
testTree.create('/angular.json', JSON.stringify({
|
||||
projects:
|
||||
{my_name: {architect: {build: {options: {tsConfig: './my-custom-config.json'}}}}}
|
||||
projects: {my_name: {architect: {build: {options: {tsConfig: './my-custom-config.json'}}}}}
|
||||
}));
|
||||
|
||||
expect(getProjectTsConfigPaths(testTree).buildPaths).toEqual(['my-custom-config.json']);
|
||||
@ -57,8 +58,7 @@ describe('project tsconfig paths', () => {
|
||||
it('should detect test tsconfig path inside of .angular.json file', () => {
|
||||
testTree.create('/my-test-config.json', '');
|
||||
testTree.create('/.angular.json', JSON.stringify({
|
||||
projects:
|
||||
{with_tests: {architect: {test: {options: {tsConfig: './my-test-config.json'}}}}}
|
||||
projects: {with_tests: {architect: {test: {options: {tsConfig: './my-test-config.json'}}}}}
|
||||
}));
|
||||
|
||||
expect(getProjectTsConfigPaths(testTree).testPaths).toEqual(['my-test-config.json']);
|
||||
|
@ -52,7 +52,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('import renaming', () => {
|
||||
it('should change Renderer imports to Renderer2', async() => {
|
||||
it('should change Renderer imports to Renderer2', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -67,7 +67,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain(`import { Component, Renderer2 } from '@angular/core';`);
|
||||
});
|
||||
|
||||
it('should change aliased Renderer imports to Renderer2', async() => {
|
||||
it('should change aliased Renderer imports to Renderer2', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer as RenamedRenderer, Component } from '@angular/core';
|
||||
|
||||
@ -82,7 +82,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain(`import { Component, Renderer2 as RenamedRenderer } from '@angular/core';`);
|
||||
});
|
||||
|
||||
it('should not change Renderer imports if they are not from @angular/core', async() => {
|
||||
it('should not change Renderer imports if they are not from @angular/core', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
import { Renderer } from './my-renderer';
|
||||
@ -102,7 +102,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('type renaming', () => {
|
||||
it('should change type of constructor parameter from Renderer to Renderer2', async() => {
|
||||
it('should change type of constructor parameter from Renderer to Renderer2', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -117,7 +117,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('constructor(element: ElementRef, renderer: Renderer2)');
|
||||
});
|
||||
|
||||
it('should change type of method parameter from Renderer to Renderer2', async() => {
|
||||
it('should change type of method parameter from Renderer to Renderer2', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -134,7 +134,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('disable(renderer: Renderer2, element: HTMLElement, isDisabled: boolean)');
|
||||
});
|
||||
|
||||
it('should change type of property declarations', async() => {
|
||||
it('should change type of property declarations', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -148,7 +148,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain('public renderer: Renderer2;');
|
||||
});
|
||||
|
||||
it('should change type of properties initialized via the constructor', async() => {
|
||||
it('should change type of properties initialized via the constructor', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -163,7 +163,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('constructor(element: ElementRef, private _renderer: Renderer2)');
|
||||
});
|
||||
|
||||
it('should change the type of something that was cast to Renderer', async() => {
|
||||
it('should change the type of something that was cast to Renderer', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -184,7 +184,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
expect(content).toContain(`renderer.setStyle(element.nativeElement, 'color', 'red');`);
|
||||
});
|
||||
|
||||
it('should not rename types called Renderer that do not come from Angular', async() => {
|
||||
it('should not rename types called Renderer that do not come from Angular', async () => {
|
||||
// Write a dummy renderer file so type checking picks it up.
|
||||
writeFile('/my-renderer.ts', `export abstract class Renderer {}`);
|
||||
|
||||
@ -203,7 +203,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('constructor(element: ElementRef, renderer: Renderer)');
|
||||
});
|
||||
|
||||
it('should rename inside single-line forwardRef', async() => {
|
||||
it('should rename inside single-line forwardRef', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef, forwardRef, Inject } from '@angular/core';
|
||||
|
||||
@ -221,7 +221,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
`constructor(@Inject(forwardRef(() => Renderer2)) private _renderer: Renderer2)`);
|
||||
});
|
||||
|
||||
it('should rename inside multi-line forwardRef', async() => {
|
||||
it('should rename inside multi-line forwardRef', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef, forwardRef, Inject } from '@angular/core';
|
||||
|
||||
@ -238,11 +238,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
expect(content).toContain(
|
||||
`constructor(@Inject(forwardRef(() => { return Renderer2; })) private _renderer: Renderer2) {}`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('helper insertion', () => {
|
||||
it('should only declare each helper once per file', async() => {
|
||||
it('should only declare each helper once per file', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -260,11 +259,11 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
|
||||
const content = tree.readContent('/index.ts');
|
||||
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length)
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length)
|
||||
.toBe(1, 'Expected exactly one helper for createElement.');
|
||||
});
|
||||
|
||||
it('should insert helpers after the user\'s code', async() => {
|
||||
it('should insert helpers after the user\'s code', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -287,7 +286,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
expect(contentAfterSeparator).toContain('function __ngRendererCreateElementHelper(');
|
||||
});
|
||||
|
||||
it('should be able to handle multiple helpers per file', async() => {
|
||||
it('should be able to handle multiple helpers per file', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -314,15 +313,15 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
|
||||
const content = tree.readContent('/index.ts');
|
||||
|
||||
expect(content.match(/function __ngRendererCreateTextHelper\(/g) !.length)
|
||||
expect(content.match(/function __ngRendererCreateTextHelper\(/g)!.length)
|
||||
.toBe(1, 'Expected exactly one helper for createElement.');
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length)
|
||||
expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length)
|
||||
.toBe(1, 'Expected exactly one helper for createText.');
|
||||
expect(content.match(/function __ngRendererCreateTemplateAnchorHelper\(/g) !.length)
|
||||
expect(content.match(/function __ngRendererCreateTemplateAnchorHelper\(/g)!.length)
|
||||
.toBe(1, 'Expected exactly one helper for createTemplateAnchor.');
|
||||
});
|
||||
|
||||
it('should create the __ngRendererSplitNamespaceHelper', async() => {
|
||||
it('should create the __ngRendererSplitNamespaceHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -347,7 +346,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
`));
|
||||
});
|
||||
|
||||
it('should declare our custom any type', async() => {
|
||||
it('should declare our custom any type', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -365,11 +364,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
type AnyDuringRendererMigration = any;
|
||||
`));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('setElementProperty migration', () => {
|
||||
it('should migrate setElementProperty calls', async() => {
|
||||
it('should migrate setElementProperty calls', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -390,7 +388,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('setText migration', () => {
|
||||
it('should migrate setText calls', async() => {
|
||||
it('should migrate setText calls', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -412,7 +410,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('listenGlobal migration', () => {
|
||||
it('should migrate listenGlobal calls', async() => {
|
||||
it('should migrate listenGlobal calls', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -433,7 +431,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('selectRootElement migration', () => {
|
||||
it('should migrate selectRootElement calls', async() => {
|
||||
it('should migrate selectRootElement calls', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -454,7 +452,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('setElementClass migration', () => {
|
||||
it('should migrate calls with inline isAdd value', async() => {
|
||||
it('should migrate calls with inline isAdd value', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -479,7 +477,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain(`this._renderer.removeClass(this._element.nativeElement, className);`);
|
||||
});
|
||||
|
||||
it('should migrate calls with variable isAdd value', async() => {
|
||||
it('should migrate calls with variable isAdd value', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -503,7 +501,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('setElementStyle migration', () => {
|
||||
it('should migrate calls with two arguments to a removeStyle call', async() => {
|
||||
it('should migrate calls with two arguments to a removeStyle call', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -522,7 +520,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain(`this._renderer.removeStyle(this._element.nativeElement, 'color');`);
|
||||
});
|
||||
|
||||
it('should migrate calls with static third arguments to a setStyle call', async() => {
|
||||
it('should migrate calls with static third arguments to a setStyle call', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -552,7 +550,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
it('should migrate calls with null or undefined value for last argument to a removeStyle call',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -575,7 +573,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
`this._renderer.removeStyle(this._element.nativeElement, 'background-color');`);
|
||||
});
|
||||
|
||||
it('should migrate calls with a variable third argument', async() => {
|
||||
it('should migrate calls with a variable third argument', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -597,7 +595,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
it('should migrate calls with a variable third argument whose value can be inferred',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -625,7 +623,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('setElementAttribute migration', () => {
|
||||
it('should migrate to calls to the __ngRendererSetElementAttributeHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererSetElementAttributeHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -651,7 +649,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'__ngRendererSetElementAttributeHelper(this._renderer, this._element.nativeElement, name);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererSetElementAttributeHelper', async() => {
|
||||
it('should declare the __ngRendererSetElementAttributeHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -680,12 +678,11 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
|
||||
expect(content).toContain(stripWhitespace('function __ngRendererSplitNamespaceHelper('));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('invokeElementMethod migration', () => {
|
||||
it('should migrate calls to a direct method call if the method name and arguments are static',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -710,7 +707,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
it('should migrate calls to a property access if the method name or arguments are dynamic',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -738,7 +735,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
`(this._element.nativeElement as any)['otherMethod'].apply(this._element.nativeElement, args);`);
|
||||
});
|
||||
|
||||
it('should handle calls without an `args` array', async() => {
|
||||
it('should handle calls without an `args` array', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -761,7 +758,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('setBindingDebugInfo migration', () => {
|
||||
it('should drop calls to setBindingDebugInfo', async() => {
|
||||
it('should drop calls to setBindingDebugInfo', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -783,7 +780,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('createViewRoot migration', () => {
|
||||
it('should replace createViewRoot calls with a reference to the first argument', async() => {
|
||||
it('should replace createViewRoot calls with a reference to the first argument', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -806,7 +803,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('createElement migration', () => {
|
||||
it('should migrate to calls to the __ngRendererCreateElementHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererCreateElementHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -833,7 +830,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'return __ngRendererCreateElementHelper(this._renderer, this._element.nativeElement, nodeName);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererCreateElementHelper', async() => {
|
||||
it('should declare the __ngRendererCreateElementHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -862,11 +859,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
|
||||
expect(content).toContain(stripWhitespace('function __ngRendererSplitNamespaceHelper('));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createText migration', () => {
|
||||
it('should migrate to calls to the __ngRendererCreateTextHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererCreateTextHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -893,7 +889,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'return __ngRendererCreateTextHelper(this._renderer, this._element.nativeElement, value);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererCreateTextHelper', async() => {
|
||||
it('should declare the __ngRendererCreateTextHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -917,11 +913,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
}
|
||||
`));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createTemplateAnchor migration', () => {
|
||||
it('should migrate to calls to the __ngRendererCreateTemplateAnchorHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererCreateTemplateAnchorHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -947,7 +942,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'return __ngRendererCreateTemplateAnchorHelper(this._renderer, this._element.nativeElement);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererCreateTemplateAnchorHelper', async() => {
|
||||
it('should declare the __ngRendererCreateTemplateAnchorHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -971,11 +966,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
}
|
||||
`));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('projectNodes migration', () => {
|
||||
it('should migrate to calls to the __ngRendererProjectNodesHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererProjectNodesHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -997,7 +991,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'__ngRendererProjectNodesHelper(this._renderer, this._element.nativeElement, nodesToProject);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererProjectNodesHelper', async() => {
|
||||
it('should declare the __ngRendererProjectNodesHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1019,11 +1013,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
}
|
||||
`));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('animate migration', () => {
|
||||
it('should migrate to calls to the __ngRendererAnimateHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererAnimateHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1043,7 +1036,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toContain('__ngRendererAnimateHelper();');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererAnimateHelper', async() => {
|
||||
it('should declare the __ngRendererAnimateHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1063,11 +1056,10 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
}
|
||||
`));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('destroyView migration', () => {
|
||||
it('should migrate to calls to the __ngRendererDestroyViewHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererDestroyViewHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1088,7 +1080,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('__ngRendererDestroyViewHelper(this._renderer, allNodes);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererDestroyViewHelper', async() => {
|
||||
it('should declare the __ngRendererDestroyViewHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1116,7 +1108,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('detachView migration', () => {
|
||||
it('should migrate to calls to the __ngRendererDetachViewHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererDetachViewHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -1137,7 +1129,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
.toContain('__ngRendererDetachViewHelper(this._renderer, rootNodes);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererDetachViewHelper', async() => {
|
||||
it('should declare the __ngRendererDetachViewHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -1166,7 +1158,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
});
|
||||
|
||||
describe('attachViewAfter migration', () => {
|
||||
it('should migrate to calls to the __ngRendererAttachViewAfterHelper', async() => {
|
||||
it('should migrate to calls to the __ngRendererAttachViewAfterHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component, ElementRef } from '@angular/core';
|
||||
|
||||
@ -1188,7 +1180,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
'__ngRendererAttachViewAfterHelper(this._renderer, this._element.nativeElement, rootNodes);');
|
||||
});
|
||||
|
||||
it('should declare the __ngRendererAttachViewAfterHelper', async() => {
|
||||
it('should declare the __ngRendererAttachViewAfterHelper', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Renderer, Component } from '@angular/core';
|
||||
|
||||
@ -1225,5 +1217,7 @@ describe('Renderer to Renderer2 migration', () => {
|
||||
return runner.runSchematicAsync('migration-v9-renderer-to-renderer2', {}, tree).toPromise();
|
||||
}
|
||||
|
||||
function stripWhitespace(contents: string) { return contents.replace(/\s/g, ''); }
|
||||
function stripWhitespace(contents: string) {
|
||||
return contents.replace(/\s/g, '');
|
||||
}
|
||||
});
|
||||
|
@ -61,7 +61,9 @@ describe('static-queries migration with template strategy', () => {
|
||||
shx.rm('-r', tmpDirPath);
|
||||
});
|
||||
|
||||
function writeFakeAngular() { writeFile('/node_modules/@angular/core/index.d.ts', ``); }
|
||||
function writeFakeAngular() {
|
||||
writeFile('/node_modules/@angular/core/index.d.ts', ``);
|
||||
}
|
||||
|
||||
function writeFakeLibrary(selectorName = 'my-lib-selector') {
|
||||
writeFile('/node_modules/my-lib/index.d.ts', `export * from './public-api';`);
|
||||
@ -105,8 +107,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
}
|
||||
|
||||
describe('ViewChild', () => {
|
||||
|
||||
it('should detect queries selecting elements through template reference', async() => {
|
||||
it('should detect queries selecting elements through template reference', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -135,7 +136,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myStaticButton', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting ng-template as static', async() => {
|
||||
it('should detect queries selecting ng-template as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -158,7 +159,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myTmpl', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting ng-template as static (BOM)', async() => {
|
||||
it('should detect queries selecting ng-template as static (BOM)', async () => {
|
||||
writeFile('/index.ts', `\uFEFF
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -181,8 +182,9 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myTmpl', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting component view providers through string token', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should detect queries selecting component view providers through string token',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
@ -211,22 +213,22 @@ describe('static-queries migration with template strategy', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile(`/my-tmpl.html`, `
|
||||
writeFile(`/my-tmpl.html`, `
|
||||
<span myDirective></span>
|
||||
<ng-template>
|
||||
<span myDirective2></span>
|
||||
</ng-template>
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild('my-token', { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild('my-token-2', { static: false }) query2: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild('my-token', { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild('my-token-2', { static: false }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting component view providers using class token', async() => {
|
||||
it('should detect queries selecting component view providers using class token', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -270,7 +272,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild(MyService2, { static: false }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting component', async() => {
|
||||
it('should detect queries selecting component', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
import {HomeComponent, HomeComponent2} from './home-comp';
|
||||
@ -316,7 +318,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild(HomeComponent2, { static: false }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting third-party component', async() => {
|
||||
it('should detect queries selecting third-party component', async () => {
|
||||
writeFakeLibrary();
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
@ -341,9 +343,10 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild(MyLibComponent, { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries selecting third-party component with multiple selectors', async() => {
|
||||
writeFakeLibrary('a-selector, test-selector');
|
||||
writeFile('/index.ts', `
|
||||
it('should detect queries selecting third-party component with multiple selectors',
|
||||
async () => {
|
||||
writeFakeLibrary('a-selector, test-selector');
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
import {MyLibComponent} from 'my-lib';
|
||||
|
||||
@ -356,20 +359,20 @@ describe('static-queries migration with template strategy', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/my-tmpl.html', `
|
||||
writeFile('/my-tmpl.html', `
|
||||
<a-selector>Match 1</a-selector>
|
||||
<ng-template>
|
||||
<test-selector>Match 2</test-selector>
|
||||
</ng-template>
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild(MyLibComponent, { static: false }) query: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@ViewChild(MyLibComponent, { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries within structural directive', async() => {
|
||||
it('should detect queries within structural directive', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -399,7 +402,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myRef2', { static: false }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect inherited queries', async() => {
|
||||
it('should detect inherited queries', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -424,7 +427,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myRef', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries declared on setter', async() => {
|
||||
it('should detect queries declared on setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -448,7 +451,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toMatch(/@ViewChild\('myRef', { static: true }\)\s+set query/);
|
||||
});
|
||||
|
||||
it('should detect queries declared on getter', async() => {
|
||||
it('should detect queries declared on getter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -473,7 +476,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toMatch(/@ViewChild\('myRef', { static: true }\)\s+get query/);
|
||||
});
|
||||
|
||||
it('should add a todo if a query is not declared in any component', async() => {
|
||||
it('should add a todo if a query is not declared in any component', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild, SomeToken} from '@angular/core';
|
||||
|
||||
@ -493,7 +496,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
/^⮑ {3}index.ts@5:11:.+could not be determined.+not declared in any component/);
|
||||
});
|
||||
|
||||
it('should add a todo if a query is used multiple times with different timing', async() => {
|
||||
it('should add a todo if a query is used multiple times with different timing', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -523,7 +526,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
|
||||
it('should be able to migrate an application with type checking failure which ' +
|
||||
'does not affect analysis',
|
||||
async() => {
|
||||
async () => {
|
||||
// Fakes the `@angular/package` by creating a `ViewChild` decorator
|
||||
// function that requires developers to specify the "static" flag.
|
||||
writeFile('/node_modules/@angular/core/index.d.ts', `
|
||||
@ -565,7 +568,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
|
||||
it('should be able to migrate applications with template type checking failure ' +
|
||||
'which does not affect analysis',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {NgModule, Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -596,7 +599,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myRef', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should notify user if project has syntax errors which can affect analysis', async() => {
|
||||
it('should notify user if project has syntax errors which can affect analysis', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -630,7 +633,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('myRef', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should gracefully exit migration if queries could not be analyzed', async() => {
|
||||
it('should gracefully exit migration if queries could not be analyzed', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -650,7 +653,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
expect(errorOutput[0]).toMatch(/Cannot determine the module for class MyComp/);
|
||||
});
|
||||
|
||||
it('should gracefully exit migration if AOT compiler throws exception', async() => {
|
||||
it('should gracefully exit migration if AOT compiler throws exception', async () => {
|
||||
writeFile('/my-component.ts', `
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -691,7 +694,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
expect(errorOutput[0]).toMatch(/^TypeError: Cannot read property 'module' of undefined/);
|
||||
});
|
||||
|
||||
it('should add a todo for content queries which are not detectable', async() => {
|
||||
it('should add a todo for content queries which are not detectable', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ContentChild} from '@angular/core';
|
||||
|
||||
@ -713,7 +716,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toMatch(/^⮑ {3}index.ts@6:11: Content queries cannot be migrated automatically\./);
|
||||
});
|
||||
|
||||
it('should add a todo if query options cannot be migrated inline', async() => {
|
||||
it('should add a todo if query options cannot be migrated inline', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||
|
||||
@ -738,7 +741,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
expect(warnOutput[0]).toMatch(/Please manually set the query timing to.*static: true/);
|
||||
});
|
||||
|
||||
it('should not normalize stylesheets which are referenced in component', async() => {
|
||||
it('should not normalize stylesheets which are referenced in component', async () => {
|
||||
writeFile('sub_dir/index.ts', `
|
||||
import {Component, NgModule, ContentChild} from '@angular/core';
|
||||
|
||||
@ -765,7 +768,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
expect(console.error).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should always use the test migration strategy for test tsconfig files', async() => {
|
||||
it('should always use the test migration strategy for test tsconfig files', async () => {
|
||||
writeFile('/src/tsconfig.spec.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
experimentalDecorators: true,
|
||||
@ -812,7 +815,7 @@ describe('static-queries migration with template strategy', () => {
|
||||
.toContain(`@ViewChild('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not fall back to test strategy if selected strategy fails', async() => {
|
||||
it('should not fall back to test strategy if selected strategy fails', async () => {
|
||||
writeFile('/src/tsconfig.spec.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
experimentalDecorators: true,
|
||||
|
@ -63,7 +63,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
describe('ViewChild', () => {
|
||||
createQueryTests('ViewChild');
|
||||
|
||||
it('should mark view queries used in "ngAfterContentInit" as static', async() => {
|
||||
it('should mark view queries used in "ngAfterContentInit" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -83,7 +83,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@ViewChild('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark view queries used in "ngAfterContentChecked" as static', async() => {
|
||||
it('should mark view queries used in "ngAfterContentChecked" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
|
||||
@ -107,7 +107,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
describe('ContentChild', () => {
|
||||
createQueryTests('ContentChild');
|
||||
|
||||
it('should not mark content queries used in "ngAfterContentInit" as static', async() => {
|
||||
it('should not mark content queries used in "ngAfterContentInit" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ContentChild} from '@angular/core';
|
||||
|
||||
@ -127,7 +127,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@ContentChild('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark content queries used in "ngAfterContentInit" as static (BOM)', async() => {
|
||||
it('should not mark content queries used in "ngAfterContentInit" as static (BOM)', async () => {
|
||||
writeFile('/index.ts', `\uFEFF
|
||||
import {Component, ContentChild} from '@angular/core';
|
||||
|
||||
@ -147,7 +147,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@ContentChild('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark content queries used in "ngAfterContentChecked" as static', async() => {
|
||||
it('should not mark content queries used in "ngAfterContentChecked" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ContentChild} from '@angular/core';
|
||||
|
||||
@ -176,8 +176,8 @@ describe('static-queries migration with usage strategy', () => {
|
||||
return runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise();
|
||||
}
|
||||
|
||||
function createQueryTests(queryType: 'ViewChild' | 'ContentChild') {
|
||||
it('should mark queries as dynamic', async() => {
|
||||
function createQueryTests(queryType: 'ViewChild'|'ContentChild') {
|
||||
it('should mark queries as dynamic', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -200,7 +200,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('dynamic', { static: false }) dynamic: any`);
|
||||
});
|
||||
|
||||
it('should mark queries used in "ngOnChanges" as static', async() => {
|
||||
it('should mark queries used in "ngOnChanges" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -220,7 +220,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark queries used in "ngOnInit" as static', async() => {
|
||||
it('should mark queries used in "ngOnInit" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -240,7 +240,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark queries used in "ngDoCheck" as static', async() => {
|
||||
it('should mark queries used in "ngDoCheck" as static', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -260,7 +260,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should keep existing query options when updating timing', async() => {
|
||||
it('should keep existing query options when updating timing', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -280,7 +280,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { /* test */ read: null, static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should add a todo for queries declared on setter', async() => {
|
||||
it('should add a todo for queries declared on setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -300,7 +300,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toMatch(/index.ts@6:11: Queries defined on accessors cannot be analyzed.$/);
|
||||
});
|
||||
|
||||
it('should add a todo for queries declared on getter', async() => {
|
||||
it('should add a todo for queries declared on getter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -321,7 +321,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toMatch(/index.ts@6:11: Queries defined on accessors cannot be analyzed.$/);
|
||||
});
|
||||
|
||||
it('should not overwrite existing explicit query timing', async() => {
|
||||
it('should not overwrite existing explicit query timing', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -337,7 +337,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', {static: /* untouched */ someVal}) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries used in deep method chain', async() => {
|
||||
it('should detect queries used in deep method chain', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -372,7 +372,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should properly exit if recursive function is analyzed', async() => {
|
||||
it('should properly exit if recursive function is analyzed', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -396,7 +396,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries used in newly instantiated classes', async() => {
|
||||
it('should detect queries used in newly instantiated classes', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -435,7 +435,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries used in parenthesized new expressions', async() => {
|
||||
it('should detect queries used in parenthesized new expressions', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -461,7 +461,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries in lifecycle hook with string literal name', async() => {
|
||||
it('should detect queries in lifecycle hook with string literal name', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -481,7 +481,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect static queries within nested inheritance', async() => {
|
||||
it('should detect static queries within nested inheritance', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -506,7 +506,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect static queries used within input setters', async() => {
|
||||
it('should detect static queries used within input setters', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, Input, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -528,7 +528,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect inputs defined in metadata', async() => {
|
||||
it('should detect inputs defined in metadata', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -554,7 +554,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect aliased inputs declared in metadata', async() => {
|
||||
it('should detect aliased inputs declared in metadata', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -577,7 +577,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark query as static if query is used in non-input setter', async() => {
|
||||
it('should not mark query as static if query is used in non-input setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -597,7 +597,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect input decorator on setter', async() => {
|
||||
it('should detect input decorator on setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Input, Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -622,7 +622,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect setter inputs in derived classes', async() => {
|
||||
it('should detect setter inputs in derived classes', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -647,7 +647,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should properly detect static query in external derived class', async() => {
|
||||
it('should properly detect static query in external derived class', async () => {
|
||||
writeFile('/src/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -680,7 +680,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries used in promises as static', async() => {
|
||||
it('should not mark queries used in promises as static', async () => {
|
||||
writeFile('/es2015.dom.d.ts', `
|
||||
interface PromiseConstructor {
|
||||
resolve(): Promise;
|
||||
@ -735,7 +735,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should handle function callbacks which statically access queries', async() => {
|
||||
it('should handle function callbacks which statically access queries', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -764,7 +764,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
});
|
||||
|
||||
it('should handle class instantiations with specified callbacks that access queries',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {External} from './external';
|
||||
@ -794,7 +794,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should handle nested functions with arguments from parent closure', async() => {
|
||||
it('should handle nested functions with arguments from parent closure', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -823,7 +823,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries used in setTimeout as static', async() => {
|
||||
it('should not mark queries used in setTimeout as static', async () => {
|
||||
writeFile('/lib.dom.d.ts', `declare function setTimeout(cb: Function);`);
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
@ -859,7 +859,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query3: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries used in "addEventListener" as static', async() => {
|
||||
it('should not mark queries used in "addEventListener" as static', async () => {
|
||||
writeFile('/lib.dom.d.ts', `
|
||||
interface HTMLElement {
|
||||
addEventListener(cb: Function);
|
||||
@ -888,7 +888,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries used in "requestAnimationFrame" as static', async() => {
|
||||
it('should not mark queries used in "requestAnimationFrame" as static', async () => {
|
||||
writeFile('/lib.dom.d.ts', `declare function requestAnimationFrame(cb: Function);`);
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ElementRef, ${queryType}} from '@angular/core';
|
||||
@ -913,8 +913,9 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark queries used in immediately-invoked function expression as static', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should mark queries used in immediately-invoked function expression as static',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
@ -934,15 +935,15 @@ describe('static-queries migration with usage strategy', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should detect static queries used in external function-like declaration', async() => {
|
||||
it('should detect static queries used in external function-like declaration', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {externalFn} from './external';
|
||||
@ -971,7 +972,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect static queries used through getter property access', async() => {
|
||||
it('should detect static queries used through getter property access', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -995,7 +996,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect static queries used through external getter access', async() => {
|
||||
it('should detect static queries used through external getter access', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {External} from './external';
|
||||
@ -1033,8 +1034,9 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries as static if a value is assigned to accessor property', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should not mark queries as static if a value is assigned to accessor property',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
@ -1052,13 +1054,13 @@ describe('static-queries migration with usage strategy', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark queries as static if non-input setter uses query', async() => {
|
||||
it('should mark queries as static if non-input setter uses query', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1083,7 +1085,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should check setter and getter when using compound assignment', async() => {
|
||||
it('should check setter and getter when using compound assignment', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1111,7 +1113,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should check getters when using comparison operator in binary expression', async() => {
|
||||
it('should check getters when using comparison operator in binary expression', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1136,7 +1138,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should check derived abstract class methods', async() => {
|
||||
it('should check derived abstract class methods', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1172,7 +1174,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries accessed through deep abstract class method', async() => {
|
||||
it('should detect queries accessed through deep abstract class method', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1204,7 +1206,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries accessed through abstract property getter', async() => {
|
||||
it('should detect queries accessed through abstract property getter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1230,7 +1232,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect queries accessed through abstract property setter', async() => {
|
||||
it('should detect queries accessed through abstract property setter', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1257,8 +1259,9 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect query usage in abstract class methods accessing inherited query', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should detect query usage in abstract class methods accessing inherited query',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
export abstract class RootBaseClass {
|
||||
@ -1287,13 +1290,13 @@ describe('static-queries migration with usage strategy', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect query usage within component template', async() => {
|
||||
it('should detect query usage within component template', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1314,8 +1317,9 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect query usage with nested property read within component template', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should detect query usage with nested property read within component template',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({templateUrl: 'my-template.html'})
|
||||
@ -1324,19 +1328,19 @@ describe('static-queries migration with usage strategy', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
writeFile(`/my-template.html`, `
|
||||
writeFile(`/my-template.html`, `
|
||||
<foo #test></foo>
|
||||
<comp [dir]="query.someProperty"></comp>
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark query as static if template has template reference with same name',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1360,7 +1364,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
});
|
||||
|
||||
it('should not mark query as static if template has property read with query name but different receiver',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1385,7 +1389,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) someProp: any;`);
|
||||
});
|
||||
|
||||
it('should ignore queries accessed within <ng-template> element', async() => {
|
||||
it('should ignore queries accessed within <ng-template> element', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1409,7 +1413,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should detect inherited queries used in templates', async() => {
|
||||
it('should detect inherited queries used in templates', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1433,7 +1437,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
});
|
||||
|
||||
it('should mark queries which could be accessed statically within third-party calls as ambiguous',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {thirdPartySync} from 'my-lib';
|
||||
@ -1458,18 +1462,18 @@ describe('static-queries migration with usage strategy', () => {
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(
|
||||
`@${queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`);
|
||||
.toContain(`@${
|
||||
queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(
|
||||
`@${queryType}('test', /* TODO: check static flag */ { static: true }) query2: any;`);
|
||||
.toContain(`@${
|
||||
queryType}('test', /* TODO: check static flag */ { static: true }) query2: any;`);
|
||||
expect(warnOutput.length).toBe(2);
|
||||
expect(warnOutput[0]).toContain('Query timing is ambiguous.');
|
||||
expect(warnOutput[1]).toContain('Query timing is ambiguous.');
|
||||
});
|
||||
|
||||
it('should mark queries which could be accessed statically within third-party new expressions as ambiguous',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {ThirdParty} from 'my-lib';
|
||||
@ -1493,15 +1497,15 @@ describe('static-queries migration with usage strategy', () => {
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(
|
||||
`@${queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`);
|
||||
.toContain(`@${
|
||||
queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`);
|
||||
expect(warnOutput.length).toBe(1);
|
||||
expect(warnOutput[0])
|
||||
.toContain(
|
||||
'Query timing is ambiguous. Please check if the query can be marked as dynamic');
|
||||
});
|
||||
|
||||
it('should properly handle multiple tsconfig files', async() => {
|
||||
it('should properly handle multiple tsconfig files', async () => {
|
||||
writeFile('/src/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@ -1526,7 +1530,7 @@ describe('static-queries migration with usage strategy', () => {
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should support function call with default parameter value', async() => {
|
||||
it('should support function call with default parameter value', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
|
@ -62,7 +62,7 @@ describe('template variable assignment migration', () => {
|
||||
return runner.runSchematicAsync('migration-v8-template-local-variables', {}, tree).toPromise();
|
||||
}
|
||||
|
||||
it('should warn for two-way data binding variable assignment', async() => {
|
||||
it('should warn for two-way data binding variable assignment', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -78,7 +78,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput[0]).toMatch(/^⮑ {3}index.ts@5:69: Found assignment/);
|
||||
});
|
||||
|
||||
it('should warn for two-way data binding assigning to "as" variable', async() => {
|
||||
it('should warn for two-way data binding assigning to "as" variable', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -100,7 +100,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput).toMatch(/^⮑ {3}tmpl.html@3:31: Found assignment/);
|
||||
});
|
||||
|
||||
it('should warn for bound event assignments to "as" variable', async() => {
|
||||
it('should warn for bound event assignments to "as" variable', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -124,7 +124,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput[1]).toMatch(/^⮑ {3}sub_dir\/tmpl.html@4:25: Found assignment/);
|
||||
});
|
||||
|
||||
it('should warn for bound event assignments to template "let" variables', async() => {
|
||||
it('should warn for bound event assignments to template "let" variables', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -148,7 +148,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput[1]).toMatch(/^⮑ {3}sub_dir\/tmpl.html@4:25: Found assignment/);
|
||||
});
|
||||
|
||||
it('should not warn for bound event assignments to component property', async() => {
|
||||
it('should not warn for bound event assignments to component property', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -166,7 +166,7 @@ describe('template variable assignment migration', () => {
|
||||
});
|
||||
|
||||
it('should not warn for bound event assignments to template variable object property',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -186,7 +186,7 @@ describe('template variable assignment migration', () => {
|
||||
});
|
||||
|
||||
it('should not warn for property writes with template variable name but different receiver',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -211,7 +211,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should warn for template variable assignments in expression conditional', async() => {
|
||||
it('should warn for template variable assignments in expression conditional', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -236,7 +236,7 @@ describe('template variable assignment migration', () => {
|
||||
});
|
||||
|
||||
it('should not warn for property writes with template variable name but different scope',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -259,7 +259,7 @@ describe('template variable assignment migration', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should not throw an error if a detected template fails parsing', async() => {
|
||||
it('should not throw an error if a detected template fails parsing', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@ -276,7 +276,7 @@ describe('template variable assignment migration', () => {
|
||||
expect(warnOutput.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should be able to report multiple templates within the same source file', async() => {
|
||||
it('should be able to report multiple templates within the same source file', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
|
@ -83,7 +83,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
`);
|
||||
}
|
||||
|
||||
it('should print a failure message base class is declared through type definition', async() => {
|
||||
it('should print a failure message base class is declared through type definition', async () => {
|
||||
writeFile('/node_modules/my-lib/package.json', JSON.stringify({
|
||||
version: '0.0.0',
|
||||
main: './index.js',
|
||||
@ -121,7 +121,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
'dependency injection. Please manually fix the following failures');
|
||||
});
|
||||
|
||||
it('should add @Directive() decorator to extended base class', async() => {
|
||||
it('should add @Directive() decorator to extended base class', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
@ -149,8 +149,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toMatch(/@Directive\(\)\nexport class BaseClass2 {/);
|
||||
});
|
||||
|
||||
it('not decorated base class multiple times if extended multiple times', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('not decorated base class multiple times if extended multiple times', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
export class BaseClass {
|
||||
@ -169,7 +169,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
|
||||
@Directive()
|
||||
export class BaseClass {
|
||||
@ -177,7 +177,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}`);
|
||||
});
|
||||
|
||||
it('should add @Injectable() decorator to extended base class', async() => {
|
||||
it('should add @Injectable() decorator to extended base class', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Injectable, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
@ -197,8 +197,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(tree.readContent('/index.ts')).toMatch(/@Injectable\(\)\nexport class BaseClass {/);
|
||||
});
|
||||
|
||||
it('should not decorate base class for decorated pipe', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should not decorate base class for decorated pipe', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule, Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({name: 'test'})
|
||||
@ -213,13 +213,13 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(errorOutput.length).toBe(0);
|
||||
expect(warnOutput.length).toBe(0);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Pipe({name: 'test'})
|
||||
export class MyPipe extends PipeTransform {}`);
|
||||
});
|
||||
|
||||
it('should not decorate base class if no constructor is inherited', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should not decorate base class if no constructor is inherited', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule, Directive} from '@angular/core';
|
||||
|
||||
export class BaseClassWithoutCtor {
|
||||
@ -238,7 +238,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
|
||||
export class BaseClassWithoutCtor {
|
||||
someUnrelatedProp = true;
|
||||
@ -252,8 +252,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should not decorate base class if directive/component/provider defines a constructor',
|
||||
async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, Injectable, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
export class BaseClass {
|
||||
@ -284,15 +284,15 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
|
||||
export class BaseClass {
|
||||
constructor(zone: NgZone) {}
|
||||
}`);
|
||||
});
|
||||
|
||||
it('should not decorate base class if it already has decorator', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should not decorate base class if it already has decorator', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, Directive, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
@Directive({selector: 'base-class'})
|
||||
@ -312,13 +312,13 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
|
||||
@Directive({selector: 'base-class'})
|
||||
export class BaseClass {`);
|
||||
});
|
||||
|
||||
it('should add a comment if the base class is declared through type definition', async() => {
|
||||
it('should add a comment if the base class is declared through type definition', async () => {
|
||||
writeFile('/node_modules/my-lib/package.json', JSON.stringify({
|
||||
version: '0.0.0',
|
||||
main: './index.js',
|
||||
@ -332,7 +332,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, Injectable, NgModule} from '@angular/core';
|
||||
import {SuperBaseClass} from 'my-lib';
|
||||
|
||||
@ -365,42 +365,42 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Directive()
|
||||
export class BaseClass extends SuperBaseClass {
|
||||
// TODO: add explicit constructor
|
||||
}`);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Injectable()
|
||||
export class BaseClass2 extends SuperBaseClass {
|
||||
// TODO: add explicit constructor
|
||||
}`);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Directive()
|
||||
export class PassThroughClass extends BaseClass {}`);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({template: ''})
|
||||
export class MyComponent extends PassThroughClass {}`);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({template: ''})
|
||||
export class MyComponent3 extends SuperBaseClass {
|
||||
// TODO: add explicit constructor
|
||||
}`);
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Injectable()
|
||||
export class MyService extends BaseClass2 {}`);
|
||||
});
|
||||
|
||||
it('should not add a comment if the base class is declared through type definition but is' +
|
||||
'decorated',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFakeLibrary();
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {BaseComponent} from 'my-lib';
|
||||
|
||||
@ -413,14 +413,14 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({template: ''})
|
||||
export class MyComponent extends BaseComponent {}`);
|
||||
});
|
||||
|
||||
it('should not decorate base class in typings if it misses an explicit constructor', async() => {
|
||||
it('should not decorate base class in typings if it misses an explicit constructor', async () => {
|
||||
writeFakeLibrary();
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {BaseDirective} from 'my-lib';
|
||||
|
||||
@ -433,16 +433,16 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({template: ''})
|
||||
export class MyComponent extends BaseDirective {}`);
|
||||
expect(tree.readContent('/node_modules/my-lib/public-api.d.ts')).not.toContain('@Directive');
|
||||
});
|
||||
|
||||
it('should detect decorated classes by respecting summary files', async() => {
|
||||
it('should detect decorated classes by respecting summary files', async () => {
|
||||
writeSummaryOnlyThirdPartyLibrary();
|
||||
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {BaseComponent} from 'my-lib';
|
||||
|
||||
@ -457,12 +457,12 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(warnOutput.length).toBe(0);
|
||||
expect(errorOutput.length).toBe(0);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({template: ''})
|
||||
export class MyComponent extends BaseComponent {}`);
|
||||
});
|
||||
|
||||
it('should decorate all undecorated directives of inheritance chain', async() => {
|
||||
it('should decorate all undecorated directives of inheritance chain', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
@ -486,7 +486,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
.toMatch(/}\s+@Directive\(\)\nexport class BaseClass extends SuperBaseClass {/);
|
||||
});
|
||||
|
||||
it('should decorate all undecorated providers of inheritance chain', async() => {
|
||||
it('should decorate all undecorated providers of inheritance chain', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Injectable, NgModule, NgZone} from '@angular/core';
|
||||
|
||||
@ -511,7 +511,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should properly update import if @Directive can be accessed through existing namespace import',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
import {BaseClass} from './base';
|
||||
@ -537,7 +537,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should properly update existing import with aliased specifier if identifier is already used',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
import {Directive} from './third_party_directive';
|
||||
@ -561,7 +561,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should properly create new import with aliased specifier if identifier is already used',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule, NgZone} from '@angular/core';
|
||||
import {BaseClass} from './base';
|
||||
@ -590,8 +590,9 @@ describe('Undecorated classes with DI migration', () => {
|
||||
.toContain(`{ Directive as Directive_1 } from "@angular/core";`);
|
||||
});
|
||||
|
||||
it('should use existing aliased import of @Directive instead of creating new import', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should use existing aliased import of @Directive instead of creating new import',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {BaseClass} from './base';
|
||||
|
||||
@ -602,7 +603,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/base.ts', `
|
||||
writeFile('/base.ts', `
|
||||
import {Directive as AliasedDir, NgZone} from '@angular/core';
|
||||
|
||||
export class BaseClass {
|
||||
@ -610,15 +611,14 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/base.ts')).toMatch(/@AliasedDir\(\)\nexport class BaseClass {/);
|
||||
});
|
||||
expect(tree.readContent('/base.ts')).toMatch(/@AliasedDir\(\)\nexport class BaseClass {/);
|
||||
});
|
||||
|
||||
describe('decorator copying', async() => {
|
||||
|
||||
it('should be able to copy the "templateUrl" field', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
describe('decorator copying', async () => {
|
||||
it('should be able to copy the "templateUrl" field', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -628,7 +628,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Directive, NgModule} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
@ -645,7 +645,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { NgModule, Directive } from '@angular/core';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Directive({
|
||||
selector: 'my-dir',
|
||||
templateUrl: './my-dir.html'
|
||||
@ -653,8 +653,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyDir extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should be able to copy the "styleUrls" field', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should be able to copy the "styleUrls" field', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -664,7 +664,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Directive, NgModule} from '@angular/core';
|
||||
|
||||
/** my comment */
|
||||
@ -680,7 +680,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@Directive({
|
||||
@ -690,8 +690,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyDir extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should be able to copy @Pipe decorator', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should be able to copy @Pipe decorator', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BasePipe} from './lib/base';
|
||||
|
||||
@ -701,7 +701,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Pipe, NgModule} from '@angular/core';
|
||||
|
||||
@Pipe({name: 'my-pipe-name'})
|
||||
@ -715,16 +715,16 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { NgModule, Pipe } from '@angular/core';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Pipe({ name: 'my-pipe-name' })
|
||||
export class MyPipe extends BasePipe {}`);
|
||||
});
|
||||
|
||||
it('should be able to copy decorator in same source file', async() => {
|
||||
it('should be able to copy decorator in same source file', async () => {
|
||||
writeFile(
|
||||
'/node_modules/@angular/cdk/table/index.d.ts',
|
||||
`export declare const CDK_TABLE_TEMPLATE = '';`);
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {CDK_TABLE_TEMPLATE} from '@angular/cdk/table';
|
||||
|
||||
@ -748,7 +748,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
template: CDK_TABLE_TEMPLATE,
|
||||
@ -757,8 +757,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyDir extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should be able to create new imports for copied identifier references', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should be able to create new imports for copied identifier references', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -772,7 +772,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
'/node_modules/@angular/cdk/table/index.d.ts',
|
||||
`export declare const CDK_TABLE_TEMPLATE = '';`);
|
||||
writeFile('/styles.ts', `export const STYLE_THROUGH_VAR = 'external';`);
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {CDK_TABLE_TEMPLATE as tableTmpl} from '@angular/cdk/table';
|
||||
import {STYLE_THROUGH_VAR} from '../styles';
|
||||
@ -798,7 +798,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
.toContain(`import { STYLE_THROUGH_VAR } from "./styles";`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { BaseClass, LOCAL_STYLE } from './lib/base';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
template: CDK_TABLE_TEMPLATE,
|
||||
@ -807,8 +807,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyDir extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should copy decorator once if directive is referenced multiple times', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should copy decorator once if directive is referenced multiple times', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -818,7 +818,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/second-module.ts', dedent `
|
||||
writeFile('/second-module.ts', dedent`
|
||||
import {NgModule, Directive} from '@angular/core';
|
||||
import {MyComp} from './index';
|
||||
|
||||
@ -829,7 +829,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MySecondModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
@ -844,7 +844,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@Component({
|
||||
@ -854,8 +854,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyComp extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should create aliased imports to avoid collisions for referenced identifiers', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should create aliased imports to avoid collisions for referenced identifiers', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -869,7 +869,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
export const MY_TEMPLATE = '';
|
||||
@ -888,7 +888,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { BaseClass, MY_TEMPLATE as MY_TEMPLATE_1 } from './lib/base';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
template: MY_TEMPLATE_1
|
||||
@ -896,8 +896,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyComp extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should add comment for metadata fields which cannot be copied', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should add comment for metadata fields which cannot be copied', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -907,7 +907,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule, Document} from '@angular/core';
|
||||
|
||||
// this variable cannot be imported automatically.
|
||||
@ -926,7 +926,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
template: '',
|
||||
@ -940,8 +940,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should add comment for metadata fields which are added through spread operator',
|
||||
async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -951,7 +951,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
export const metadataThroughVar = {
|
||||
@ -971,7 +971,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
template: '',
|
||||
@ -984,10 +984,10 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyComp extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should be able to copy fields specified through shorthand assignment', async() => {
|
||||
it('should be able to copy fields specified through shorthand assignment', async () => {
|
||||
writeFile('/hello.css', '');
|
||||
writeFile('/my-tmpl.html', '');
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -999,7 +999,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
writeFile('/lib/hello.css', '');
|
||||
writeFile('/lib/my-tmpl.html', '');
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
export const host = {};
|
||||
@ -1022,7 +1022,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { BaseClass, templateUrl, host } from './lib/base';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
selector: 'my-dir',
|
||||
templateUrl,
|
||||
@ -1036,10 +1036,10 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyComp extends BaseClass {}`);
|
||||
});
|
||||
|
||||
it('should serialize metadata from base class without source code', async() => {
|
||||
it('should serialize metadata from base class without source code', async () => {
|
||||
writeFakeLibrary();
|
||||
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseComponent, BasePipe} from 'my-lib';
|
||||
|
||||
@ -1061,7 +1061,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(
|
||||
`import { NgModule, ChangeDetectionStrategy, ViewEncapsulation, NG_VALIDATORS, Component, Pipe } from '@angular/core';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
selector: "comp-selector",
|
||||
@ -1077,7 +1077,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}
|
||||
})
|
||||
export class PassThrough extends BaseComponent {}`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
selector: "comp-selector",
|
||||
@ -1093,7 +1093,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}
|
||||
})
|
||||
export class MyComp extends PassThrough {}`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Pipe({
|
||||
pure: true,
|
||||
name: "external-pipe-name"
|
||||
@ -1101,12 +1101,12 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export class MyPipe extends BasePipe {}`);
|
||||
});
|
||||
|
||||
it('should serialize metadata with external references from class without source code', async() => {
|
||||
it('should serialize metadata with external references from class without source code', async () => {
|
||||
writeFakeLibrary({useImportedTemplate: true});
|
||||
writeFile(
|
||||
'/node_modules/@angular/cdk/table/index.d.ts',
|
||||
`export declare const CDK_TABLE_TEMPLATE = 'Template of CDK Table.';`);
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseComponent} from 'my-lib';
|
||||
|
||||
@ -1121,7 +1121,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(
|
||||
`import { NgModule, ChangeDetectionStrategy, ViewEncapsulation, NG_VALIDATORS, Component } from '@angular/core';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
selector: "comp-selector",
|
||||
@ -1140,10 +1140,10 @@ describe('Undecorated classes with DI migration', () => {
|
||||
});
|
||||
|
||||
it('should not throw if metadata from base class without source code is not serializable',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFakeLibrary({insertInvalidReference: true});
|
||||
|
||||
writeFile('/index.ts', dedent `
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseComponent} from 'my-lib';
|
||||
|
||||
@ -1159,8 +1159,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(errorOutput[0]).toMatch(/Could not resolve non-existent/);
|
||||
});
|
||||
|
||||
it('should not create imports for identifiers resolving to target source file', async() => {
|
||||
writeFile('/index.ts', dedent `
|
||||
it('should not create imports for identifiers resolving to target source file', async () => {
|
||||
writeFile('/index.ts', dedent`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {BaseClass} from './lib/base';
|
||||
|
||||
@ -1175,7 +1175,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
export {LOCAL_NAME as PUBLIC_NAME};
|
||||
`);
|
||||
|
||||
writeFile('/lib/base.ts', dedent `
|
||||
writeFile('/lib/base.ts', dedent`
|
||||
import {Directive, NgModule} from '@angular/core';
|
||||
import {SHARED_TEMPLATE_URL, PUBLIC_NAME} from '..';
|
||||
|
||||
@ -1194,7 +1194,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { NgModule, Directive } from '@angular/core';`);
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent `
|
||||
expect(tree.readContent('/index.ts')).toContain(dedent`
|
||||
@Directive({
|
||||
selector: 'my-dir',
|
||||
template: SHARED_TEMPLATE_URL,
|
||||
@ -1388,7 +1388,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
}));
|
||||
}
|
||||
|
||||
it('should not run for test tsconfig files', async() => {
|
||||
it('should not run for test tsconfig files', async () => {
|
||||
writeFile('/src/tsconfig.spec.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
lib: ['es2015'],
|
||||
@ -1429,8 +1429,8 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(errorOutput.length).toBe(0);
|
||||
});
|
||||
|
||||
describe('diagnostics', async() => {
|
||||
it('should gracefully exit migration if project fails with structural diagnostic', async() => {
|
||||
describe('diagnostics', async () => {
|
||||
it('should gracefully exit migration if project fails with structural diagnostic', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@ -1455,28 +1455,29 @@ describe('Undecorated classes with DI migration', () => {
|
||||
'TypeScript program failures');
|
||||
});
|
||||
|
||||
it('should gracefully exit migration if project fails with syntactical diagnostic', async() => {
|
||||
writeFile('/index.ts', `
|
||||
it('should gracefully exit migration if project fails with syntactical diagnostic',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule} /* missing "from" */ '@angular/core';
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
await runMigration();
|
||||
|
||||
expect(warnOutput.length).toBe(1);
|
||||
expect(warnOutput[0])
|
||||
.toMatch(/project "tsconfig.json" has syntactical errors which could cause/);
|
||||
expect(errorOutput.length).toBe(1);
|
||||
expect(errorOutput[0]).toMatch(/error TS1005: 'from' expected/);
|
||||
expect(infoOutput.join(' '))
|
||||
.toContain(
|
||||
'Some project targets could not be analyzed due to ' +
|
||||
'TypeScript program failures');
|
||||
});
|
||||
expect(warnOutput.length).toBe(1);
|
||||
expect(warnOutput[0])
|
||||
.toMatch(/project "tsconfig.json" has syntactical errors which could cause/);
|
||||
expect(errorOutput.length).toBe(1);
|
||||
expect(errorOutput[0]).toMatch(/error TS1005: 'from' expected/);
|
||||
expect(infoOutput.join(' '))
|
||||
.toContain(
|
||||
'Some project targets could not be analyzed due to ' +
|
||||
'TypeScript program failures');
|
||||
});
|
||||
|
||||
// Regression test for: https://github.com/angular/angular/issues/34985.
|
||||
it('should be able to migrate libraries with multiple source files and flat-module ' +
|
||||
'options set',
|
||||
async() => {
|
||||
async () => {
|
||||
writeFile('/tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
lib: ['es2015'],
|
||||
@ -1507,7 +1508,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(tree.readContent('/test.ts')).toMatch(/@Injectable\(\)\nexport class BaseClass {/);
|
||||
});
|
||||
|
||||
it('should not throw if resources could not be read', async() => {
|
||||
it('should not throw if resources could not be read', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@ -1529,7 +1530,7 @@ describe('Undecorated classes with DI migration', () => {
|
||||
expect(errorOutput.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not throw if tsconfig references non-existent source file', async() => {
|
||||
it('should not throw if tsconfig references non-existent source file', async () => {
|
||||
writeFile('/tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
lib: ['es2015'],
|
||||
|
@ -114,8 +114,8 @@ export class ImportManager {
|
||||
}
|
||||
|
||||
if (existingImport) {
|
||||
const propertyIdentifier = ts.createIdentifier(symbolName !);
|
||||
const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName !);
|
||||
const propertyIdentifier = ts.createIdentifier(symbolName!);
|
||||
const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName!);
|
||||
const needsGeneratedUniqueName = generatedUniqueIdentifier.text !== symbolName;
|
||||
const importName = needsGeneratedUniqueName ? generatedUniqueIdentifier : propertyIdentifier;
|
||||
|
||||
@ -186,7 +186,7 @@ export class ImportManager {
|
||||
this.updatedImports.forEach((expressions, importDecl) => {
|
||||
const sourceFile = importDecl.getSourceFile();
|
||||
const recorder = this.getUpdateRecorder(sourceFile);
|
||||
const namedBindings = importDecl.importClause !.namedBindings as ts.NamedImports;
|
||||
const namedBindings = importDecl.importClause!.namedBindings as ts.NamedImports;
|
||||
const newNamedBindings = ts.updateNamedImports(
|
||||
namedBindings,
|
||||
namedBindings.elements.concat(expressions.map(
|
||||
@ -211,8 +211,8 @@ export class ImportManager {
|
||||
name = `${baseName}_${counter++}`;
|
||||
} while (!this.isUniqueIdentifierName(sourceFile, name));
|
||||
|
||||
this._recordUsedIdentifier(sourceFile, name !);
|
||||
return ts.createIdentifier(name !);
|
||||
this._recordUsedIdentifier(sourceFile, name!);
|
||||
return ts.createIdentifier(name!);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -221,7 +221,7 @@ export class ImportManager {
|
||||
*/
|
||||
private isUniqueIdentifierName(sourceFile: ts.SourceFile, name: string) {
|
||||
if (this.usedIdentifierNames.has(sourceFile) &&
|
||||
this.usedIdentifierNames.get(sourceFile) !.indexOf(name) !== -1) {
|
||||
this.usedIdentifierNames.get(sourceFile)!.indexOf(name) !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ export class ImportManager {
|
||||
// is unique in the given declaration scope and we just return false.
|
||||
const nodeQueue: ts.Node[] = [sourceFile];
|
||||
while (nodeQueue.length) {
|
||||
const node = nodeQueue.shift() !;
|
||||
const node = nodeQueue.shift()!;
|
||||
if (ts.isIdentifier(node) && node.text === name) {
|
||||
return false;
|
||||
}
|
||||
@ -254,6 +254,6 @@ export class ImportManager {
|
||||
if (!commentRanges || !commentRanges.length) {
|
||||
return nodeEndPos;
|
||||
}
|
||||
return commentRanges[commentRanges.length - 1] !.end;
|
||||
return commentRanges[commentRanges.length - 1]!.end;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,9 @@ export interface ResolvedTemplate {
|
||||
* If the template is declared inline within a TypeScript source file, the line and
|
||||
* character are based on the full source file content.
|
||||
*/
|
||||
getCharacterAndLineOfPosition: (pos: number) => { character: number, line: number };
|
||||
getCharacterAndLineOfPosition: (pos: number) => {
|
||||
character: number, line: number
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,8 +105,8 @@ export class NgComponentTemplateVisitor {
|
||||
content: property.initializer.text,
|
||||
inline: true,
|
||||
start: templateStartIdx,
|
||||
getCharacterAndLineOfPosition:
|
||||
pos => ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx)
|
||||
getCharacterAndLineOfPosition: pos =>
|
||||
ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx)
|
||||
});
|
||||
}
|
||||
if (propertyName === 'templateUrl' && ts.isStringLiteralLike(property.initializer)) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
import * as ts from 'typescript';
|
||||
import {getCallDecoratorImport} from './typescript/decorators';
|
||||
|
||||
export type CallExpressionDecorator = ts.Decorator & {
|
||||
export type CallExpressionDecorator = ts.Decorator&{
|
||||
expression: ts.CallExpression;
|
||||
};
|
||||
|
||||
@ -30,8 +30,8 @@ export function getAngularDecorators(
|
||||
.filter(({importData}) => importData && importData.importModule.startsWith('@angular/'))
|
||||
.map(({node, importData}) => ({
|
||||
node: node as CallExpressionDecorator,
|
||||
name: importData !.name,
|
||||
moduleName: importData !.importModule,
|
||||
importNode: importData !.node
|
||||
name: importData!.name,
|
||||
moduleName: importData!.importModule,
|
||||
importNode: importData!.node
|
||||
}));
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ function getTargetTsconfigPath(project: WorkspaceProject, targetName: string): s
|
||||
*/
|
||||
function getWorkspaceConfigGracefully(tree: Tree): any {
|
||||
const path = defaultWorkspaceConfigPaths.find(filePath => tree.exists(filePath));
|
||||
const configBuffer = tree.read(path !);
|
||||
const configBuffer = tree.read(path!);
|
||||
|
||||
if (!path || !configBuffer) {
|
||||
return null;
|
||||
|
@ -30,5 +30,5 @@ export function supportsPrompt(): boolean {
|
||||
* create prompts.
|
||||
*/
|
||||
export function getInquirer(): Inquirer {
|
||||
return resolvedInquirerModule !;
|
||||
return resolvedInquirerModule!;
|
||||
}
|
||||
|
@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import {Import, getImportOfIdentifier} from './imports';
|
||||
|
||||
import {getImportOfIdentifier, Import} from './imports';
|
||||
|
||||
export function getCallDecoratorImport(
|
||||
typeChecker: ts.TypeChecker, decorator: ts.Decorator): Import|null {
|
||||
|
@ -20,7 +20,7 @@ export function isFunctionLikeDeclaration(node: ts.Node): node is ts.FunctionLik
|
||||
* parentheses or as expression. e.g. "(((({exp}))))()". The function should return the
|
||||
* TypeScript node referring to the inner expression. e.g "exp".
|
||||
*/
|
||||
export function unwrapExpression(node: ts.Expression | ts.ParenthesizedExpression): ts.Expression {
|
||||
export function unwrapExpression(node: ts.Expression|ts.ParenthesizedExpression): ts.Expression {
|
||||
if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) {
|
||||
return unwrapExpression(node.expression);
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user