Skip to content

Commit

Permalink
Merge pull request #279 from mgechev/template
Browse files Browse the repository at this point in the history
Automatic migration to <ng-template>
  • Loading branch information
mgechev committed Mar 31, 2017
2 parents 638e72f + 98073c0 commit 0d0e81c
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 19 deletions.
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -44,8 +44,8 @@
},
"homepage": "https://github.com/mgechev/codelyzer#readme",
"devDependencies": {
"@angular/compiler": "^4.0.0-rc.5",
"@angular/core": "^4.0.0-rc.5",
"@angular/compiler": "^4.0.0",
"@angular/core": "^4.0.0",
"@types/chai": "^3.4.33",
"@types/less": "0.0.31",
"@types/mocha": "^2.2.32",
Expand All @@ -63,7 +63,7 @@
"ts-node": "1.2.2",
"tslint": "^4.0.0",
"typescript": "^2.1.4",
"zone.js": "^0.7.2"
"zone.js": "^0.8.4"
},
"peerDependencies": {
"tslint": "^4.0.0",
Expand Down
3 changes: 1 addition & 2 deletions src/angular/templates/basicTemplateAstVisitor.ts
Expand Up @@ -116,8 +116,7 @@ export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast
element.directives.forEach(d => this.visit(d, context));
}

visitReference(ast: ast.ReferenceAst, context: any): any {
}
visitReference(ast: ast.ReferenceAst, context: any): any {}

visitVariable(ast: ast.VariableAst, context: any): any {
this._variables.push(ast.name);
Expand Down
16 changes: 11 additions & 5 deletions src/angular/templates/templateParser.ts
@@ -1,9 +1,9 @@
import { NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import * as compiler from '@angular/compiler';

import { Config, DirectiveDeclaration } from '../config';
import { SemVerDSL } from '../../util/ngVersion';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ɵConsole } from '@angular/core';

let refId = 0;

const dummyMetadataFactory = (declaration: DirectiveDeclaration) => {
Expand Down Expand Up @@ -40,6 +40,12 @@ const dummyMetadataFactory = (declaration: DirectiveDeclaration) => {
};
};

class Console {
log(message: string) {}
warn(message: string) {}
}


let defaultDirectives = [];

export const parseTemplate = (template: string, directives: DirectiveDeclaration[] = []) => {
Expand All @@ -48,7 +54,7 @@ export const parseTemplate = (template: string, directives: DirectiveDeclaration
const TemplateParser = <any>compiler.TemplateParser;
const expressionParser = new compiler.Parser(new compiler.Lexer());
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
const ngConsole = new ɵConsole();
const ngConsole = new Console();
const htmlParser =
new compiler.I18NHtmlParser(new compiler.HtmlParser());

Expand All @@ -69,12 +75,12 @@ export const parseTemplate = (template: string, directives: DirectiveDeclaration

// Make sure it works with 2.2.x & 2.3.x
const summaryKind = ((compiler as any).CompileSummaryKind || {}).Template;
const templateMetadata: compiler.CompileTemplateMetadata = {
let templateMetadata: any = {
encapsulation: 0,
template: template,
isInline: false,
templateUrl: '',
styles: [],
isInline: true,
styleUrls: [],
ngContentSelectors: [],
animations: [],
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -18,4 +18,5 @@ export { Rule as UseInputPropertyDecoratorRule } from './useInputPropertyDecorat
export { Rule as UseLifeCycleInterfaceRule } from './useLifeCycleInterfaceRule';
export { Rule as UseOutputPropertyDecoratorRule } from './useOutputPropertyDecoratorRule';
export { Rule as UsePipeTransformInterfaceRule } from './usePipeTransformInterfaceRule';
export { Rule as TemplateToNgTemplateRule } from './templateToNgTemplateRule';
export * from './angular/config';
83 changes: 83 additions & 0 deletions src/templateToNgTemplateRule.ts
@@ -0,0 +1,83 @@
import * as Lint from 'tslint';
import * as ts from 'typescript';
import {stringDistance} from './util/utils';
import {getDeclaredProperties, getDeclaredMethods} from './util/classDeclarationUtils';
import {Ng2Walker} from './angular/ng2Walker';
import {RecursiveAngularExpressionVisitor} from './angular/templates/recursiveAngularExpressionVisitor';
import * as e from '@angular/compiler/src/expression_parser/ast';
import { EmbeddedTemplateAst, ElementAst } from '@angular/compiler';
import { BasicTemplateAstVisitor } from './angular/templates/basicTemplateAstVisitor';
import { Fix } from 'tslint';
import SyntaxKind = require('./util/syntaxKind');

const ErrorMessage = 'You should use <ng-template/> instead of <template/>';
const TemplateStart = '<template';
const TemplateEnd = '</template>';
const TemplateEndRe = /<\/template>/i;

const set = new Set<EmbeddedTemplateAst>();

class TemplateToNgTemplateVisitor extends BasicTemplateAstVisitor {
private _prevClosing: number = 0;
private _fix: Fix;
private _visitedElements = new Set<EmbeddedTemplateAst>();

visitEmbeddedTemplate(element: EmbeddedTemplateAst, ctx: any) {
if (this._visitedElements.has(element)) {
return;
} else {
this._visitedElements.add(element);
}

const sp = element.sourceSpan;
const content = sp.start.file.content;
const subtemplate = content.substring(sp.start.offset, sp.end.offset);

if (subtemplate.startsWith(TemplateStart)) {
const replacement = this.createReplacement(sp.start.offset, TemplateStart.length, '<ng-template');
if (!this._fix) {
this._fix = this.createFix(replacement);
} else {
this._fix.replacements.push(replacement);
}
this.addFailure(this.createFailure(sp.start.offset, sp.end.offset - sp.start.offset, ErrorMessage, this._fix));
}

super.visitEmbeddedTemplate(element, ctx);

const subcontent = content.substring(this._prevClosing, content.length);
const matches = TemplateEndRe.exec(subcontent);
if (this._fix && matches && typeof matches.index === 'number') {
this._fix.replacements.push(this.createReplacement(matches.index + this._prevClosing, TemplateEnd.length, '</ng-template>'));
this._prevClosing = matches.index + this._prevClosing + TemplateEnd.length;
const rest = content.substring(this._prevClosing, content.length);
if (!TemplateEndRe.test(rest)) {
this._fix = null;
this._prevClosing = 0;
this._visitedElements = new Set();
}
}
}
}

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
ruleName: 'templates-use-public-rule',
type: 'functionality',
description: `Ensure that properties and methods accessed from the template are public.`,
rationale: `When Angular compiles the templates, it has to access these propertes from outside the class.`,
options: null,
optionsDescription: `Not configurable.`,
typescriptOnly: true,
};

static FAILURE: string = 'The %s "%s" that you\'re trying to access does not exist in the class declaration.';

public apply(sourceFile:ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(
new Ng2Walker(sourceFile,
this.getOptions(), {
templateVisitorCtrl: TemplateToNgTemplateVisitor
}));
}
}

0 comments on commit 0d0e81c

Please sign in to comment.