feat(core): template-var-assignment update schematic (#29608)

Introduces a new update schematic called "template-var-assignment"
that is responsible for analyzing template files in order to warn
developers if template variables are assigned to values.

The schematic also comes with a driver for `tslint` so that the
check can be used wtihin Google.

PR Close #29608
This commit is contained in:
Paul Gschwendtner
2019-03-30 12:48:21 +01:00
committed by Jason Aden
parent 15eb1e0ce1
commit 7c8f4e3202
17 changed files with 849 additions and 2 deletions

View File

@ -0,0 +1,63 @@
/**
* @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
*/
const LF_CHAR = 10;
const CR_CHAR = 13;
const LINE_SEP_CHAR = 8232;
const PARAGRAPH_CHAR = 8233;
/** Gets the line and character for the given position from the line starts map. */
export function getLineAndCharacterFromPosition(lineStartsMap: number[], position: number) {
const lineIndex = findClosestLineStartPosition(lineStartsMap, position);
return {character: position - lineStartsMap[lineIndex], line: lineIndex};
}
/**
* Computes the line start map of the given text. This can be used in order to
* retrieve the line and character of a given text position index.
*/
export function computeLineStartsMap(text: string): number[] {
const result: number[] = [0];
let pos = 0;
while (pos < text.length) {
const char = text.charCodeAt(pos++);
// Handles the "CRLF" line break. In that case we peek the character
// after the "CR" and check if it is a line feed.
if (char === CR_CHAR) {
if (text.charCodeAt(pos) === LF_CHAR) {
pos++;
}
result.push(pos);
} else if (char === LF_CHAR || char === LINE_SEP_CHAR || char === PARAGRAPH_CHAR) {
result.push(pos);
}
}
result.push(pos);
return result;
}
/** Finds the closest line start for the given position. */
function findClosestLineStartPosition<T>(
linesMap: T[], position: T, low = 0, high = linesMap.length - 1) {
while (low <= high) {
const pivotIdx = Math.floor((low + high) / 2);
const pivotEl = linesMap[pivotIdx];
if (pivotEl === position) {
return pivotIdx;
} else if (position > pivotEl) {
low = pivotIdx + 1;
} else {
high = pivotIdx - 1;
}
}
// In case there was no exact match, return the closest "lower" line index. We also
// subtract the index by one because want the index of the previous line start.
return low - 1;
}

View File

@ -11,7 +11,7 @@ import {getCallDecoratorImport} from './typescript/decorators';
export type CallExpressionDecorator = ts.Decorator & {
expression: ts.CallExpression;
}
};
export interface NgDecorator {
name: string;

View File

@ -0,0 +1,8 @@
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "tslint",
srcs = glob(["**/*.ts"]),
tsconfig = "//packages/core/schematics:tsconfig.json",
visibility = ["//packages/core/schematics/migrations/template-var-assignment/google3:__pkg__"],
)

View File

@ -0,0 +1,37 @@
/**
* @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 * as ts from 'typescript';
/**
* Creates a fake TypeScript source file that can contain content of templates or stylesheets.
* The fake TypeScript source file then can be passed to TSLint in combination with a rule failure.
*/
export function createHtmlSourceFile(filePath: string, content: string): ts.SourceFile {
const sourceFile = ts.createSourceFile(filePath, `\`${content}\``, ts.ScriptTarget.ES5);
// Subtract two characters because the string literal quotes are only needed for parsing
// and are not part of the actual source file.
sourceFile.end = sourceFile.end - 2;
// Note: This does not affect the way TSLint applies replacements for external resource files.
// At the time of writing, TSLint loads files manually if the actual rule source file is not
// equal to the source file of the replacement. This means that the replacements need proper
// offsets without the string literal quote symbols.
sourceFile.getFullText = function() {
return sourceFile.text.substring(1, sourceFile.text.length - 1);
};
sourceFile.getText = sourceFile.getFullText;
// Update the "text" property to be set to the template content without quotes. This is
// necessary so that the TypeScript line starts map is properly computed.
sourceFile.text = sourceFile.getFullText();
return sourceFile;
}