refactor(core): add tslint rule entry-point for static-query migration (#29258)
In order to be able to use the static-query migration logic within Google, we need to provide a TSLint rule entry-point that wires up the schematic logic and provides reporting and automatic fixes. PR Close #29258
This commit is contained in:

committed by
Matias Niemelä

parent
7b70760c8d
commit
8ef46f38f4
@ -0,0 +1,12 @@
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "google3",
|
||||
srcs = glob(["**/*.ts"]),
|
||||
tsconfig = "//packages/core/schematics:tsconfig.json",
|
||||
visibility = ["//packages/core/schematics/test:__pkg__"],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/static-queries",
|
||||
"@npm//tslint",
|
||||
],
|
||||
)
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Replacement, RuleFailure, Rules} from 'tslint';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {analyzeNgQueryUsage} from '../angular/analyze_query_usage';
|
||||
import {NgQueryResolveVisitor} from '../angular/ng_query_visitor';
|
||||
import {QueryTiming} from '../angular/query-definition';
|
||||
import {getTransformedQueryCallExpr} from '../transform';
|
||||
|
||||
/**
|
||||
* Rule that reports if an Angular "ViewChild" or "ContentChild" query is not explicitly
|
||||
* specifying its timing. The rule also provides TSLint automatic replacements that can
|
||||
* be applied in order to automatically migrate to the explicit query timing API.
|
||||
*/
|
||||
export class Rule extends Rules.TypedRule {
|
||||
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const queryVisitor = new NgQueryResolveVisitor(program.getTypeChecker());
|
||||
const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f) !);
|
||||
const printer = ts.createPrinter();
|
||||
const failures: RuleFailure[] = [];
|
||||
|
||||
// Analyze source files by detecting queries and class relations.
|
||||
rootSourceFiles.forEach(sourceFile => queryVisitor.visitNode(sourceFile));
|
||||
|
||||
const {resolvedQueries, classMetadata} = queryVisitor;
|
||||
const queries = resolvedQueries.get(sourceFile);
|
||||
|
||||
// No queries detected for the given source file.
|
||||
if (!queries) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Compute the query usage for all resolved queries and update the
|
||||
// query definitions to explicitly declare the query timing (static or dynamic)
|
||||
queries.forEach(q => {
|
||||
const queryExpr = q.decorator.node.expression;
|
||||
const timing = analyzeNgQueryUsage(q, classMetadata, typeChecker);
|
||||
const transformedNode = getTransformedQueryCallExpr(q, timing);
|
||||
|
||||
if (!transformedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newText = printer.printNode(ts.EmitHint.Unspecified, transformedNode, sourceFile);
|
||||
|
||||
// Replace the existing query decorator call expression with the
|
||||
// updated call expression node.
|
||||
const fix = new Replacement(queryExpr.getStart(), queryExpr.getWidth(), newText);
|
||||
const timingStr = timing === QueryTiming.STATIC ? 'static' : 'dynamic';
|
||||
|
||||
failures.push(new RuleFailure(
|
||||
sourceFile, queryExpr.getStart(), queryExpr.getWidth(),
|
||||
`Query is not explicitly marked as "${timingStr}"`, this.ruleName, fix));
|
||||
});
|
||||
|
||||
return failures;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user