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:

committed by
Jason Aden

parent
15eb1e0ce1
commit
7c8f4e3202
63
packages/core/schematics/utils/line_mappings.ts
Normal file
63
packages/core/schematics/utils/line_mappings.ts
Normal 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;
|
||||
}
|
@ -11,7 +11,7 @@ import {getCallDecoratorImport} from './typescript/decorators';
|
||||
|
||||
export type CallExpressionDecorator = ts.Decorator & {
|
||||
expression: ts.CallExpression;
|
||||
}
|
||||
};
|
||||
|
||||
export interface NgDecorator {
|
||||
name: string;
|
||||
|
8
packages/core/schematics/utils/tslint/BUILD.bazel
Normal file
8
packages/core/schematics/utils/tslint/BUILD.bazel
Normal 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__"],
|
||||
)
|
@ -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;
|
||||
}
|
Reference in New Issue
Block a user