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,110 @@
/**
* @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 {writeFileSync} from 'fs';
import {dirname, join} from 'path';
import * as shx from 'shelljs';
import {Configuration, Linter} from 'tslint';
describe('Google3 noTemplateVariableAssignment TSLint rule', () => {
const rulesDirectory = dirname(require.resolve(
'../../migrations/template-var-assignment/google3/noTemplateVariableAssignmentRule'));
let tmpDir: string;
beforeEach(() => {
tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test');
shx.mkdir('-p', tmpDir);
writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}}));
});
afterEach(() => shx.rm('-r', tmpDir));
/** Runs TSLint with the no-template-variable TSLint rule.*/
function runTSLint() {
const program = Linter.createProgram(join(tmpDir, 'tsconfig.json'));
const linter = new Linter({fix: false, rulesDirectory: [rulesDirectory]}, program);
const config = Configuration.parseConfigFile(
{rules: {'no-template-variable-assignment': true}, linterOptions: {typeCheck: true}});
program.getRootFileNames().forEach(fileName => {
linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config);
});
return linter;
}
/** Writes a file to the current temporary directory. */
function writeFile(fileName: string, content: string) {
writeFileSync(join(tmpDir, fileName), content);
}
it('should create failure for detected two-way data binding assignment', () => {
writeFile('index.ts', `
import {Component} from '@angular/core';
@Component({template: '<span *ngFor="let i of options" [(a)]="i"></span>'})
export class MyComp {}
`);
const linter = runTSLint();
const failures = linter.getResult().failures;
expect(failures.length).toBe(1);
expect(failures[0].getFileName()).toContain('index.ts');
expect(failures[0].getStartPosition().getLineAndCharacter()).toEqual({line: 3, character: 68});
expect(failures[0].getEndPosition().getLineAndCharacter()).toEqual({line: 3, character: 69});
expect(failures[0].getFailure()).toMatch(/^Found assignment to template variable./);
});
it('should create failure with correct offsets for external templates', () => {
writeFile('index.ts', `
import {Component} from '@angular/core';
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {}
`);
writeFile(`my-tmpl.html`, `
<span *ngFor="let option of options" [(a)]="option"></span>
`);
const linter = runTSLint();
const failures = linter.getResult().failures;
expect(failures.length).toBe(1);
expect(failures[0].getFileName()).toContain('my-tmpl.html');
expect(failures[0].getStartPosition().getLineAndCharacter()).toEqual({line: 1, character: 50});
expect(failures[0].getEndPosition().getLineAndCharacter()).toEqual({line: 1, character: 56});
expect(failures[0].getFailure()).toMatch(/^Found assignment to template variable./);
});
it('should create failure for template variable assignment within output', () => {
writeFile('index.ts', `
import {Component} from '@angular/core';
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {}
`);
writeFile(`my-tmpl.html`, `
<!-- Comment -->
<span *ngFor="let option of options" (click)="option = true"></span>
`);
const linter = runTSLint();
const failures = linter.getResult().failures;
expect(failures.length).toBe(1);
expect(failures[0].getFileName()).toContain('my-tmpl.html');
expect(failures[0].getStartPosition().getLineAndCharacter()).toEqual({line: 2, character: 52});
expect(failures[0].getEndPosition().getLineAndCharacter()).toEqual({line: 2, character: 65});
expect(failures[0].getFailure()).toMatch(/^Found assignment to template variable./);
});
});