Skip to content

Instantly share code, notes, and snippets.

@milichev
Last active August 30, 2016 15:55
Show Gist options
  • Save milichev/ee687279027b349cf2ce0fbc6e8c86db to your computer and use it in GitHub Desktop.
Save milichev/ee687279027b349cf2ce0fbc6e8c86db to your computer and use it in GitHub Desktop.
TsList ng2-intfs rule: Requires the class to declare Angular 2 interfaces if implements their methods
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var ts = require('typescript');
var Lint = require('tslint/lib/lint');
var abstractRule_1 = require('tslint/lib/language/rule/abstractRule');
var Rule = (function (_super) {
__extends(Rule, _super);
function Rule() {
_super.apply(this, arguments);
}
Rule.prototype.apply = function (sourceFile) {
return this.applyWithWalker(new Ng2InterfacesWalker(sourceFile, this.getOptions()));
};
Rule.metadata = {
ruleName: 'ng2-intfs',
type: 'typescript',
description: 'Requires the class to declare Angular 2 interfaces if implements their methods',
rationale: '',
options: undefined,
optionExamples: ['true']
};
return Rule;
}(abstractRule_1.AbstractRule));
exports.Rule = Rule;
var memberDefs = {
OnChanges: [{ name: 'ngOnChanges', kind: ts.SyntaxKind.MethodDeclaration }],
OnInit: [{ name: 'ngOnInit', kind: ts.SyntaxKind.MethodDeclaration }],
DoCheck: [{ name: 'ngDoCheck', kind: ts.SyntaxKind.MethodDeclaration }],
AfterContentInit: [{ name: 'ngAfterContentInit', kind: ts.SyntaxKind.MethodDeclaration }],
AfterContentChecked: [{ name: 'ngAfterContentChecked', kind: ts.SyntaxKind.MethodDeclaration }],
AfterViewInit: [{ name: 'ngAfterViewInit', kind: ts.SyntaxKind.MethodDeclaration }],
AfterViewChecked: [{ name: 'ngAfterViewChecked', kind: ts.SyntaxKind.MethodDeclaration }],
OnDestroy: [{ name: 'ngOnDestroy', kind: ts.SyntaxKind.MethodDeclaration }]
};
var memberMap = new Map();
Object.keys(memberDefs).forEach(function (nm) {
memberDefs[nm].forEach(function (d) { return memberMap.set(d.name, {
type: nm,
kind: d.kind
}); });
});
var Ng2InterfacesWalker = (function (_super) {
__extends(Ng2InterfacesWalker, _super);
function Ng2InterfacesWalker() {
_super.apply(this, arguments);
}
Ng2InterfacesWalker.prototype.visitClassDeclaration = function (node) {
var _this = this;
var implementsClause = node.heritageClauses &&
node.heritageClauses.filter(function (cl) { return cl.token === ts.SyntaxKind.ImplementsKeyword; })[0] || { types: [] };
var extendsClause = node.heritageClauses &&
node.heritageClauses.filter(function (cl) { return cl.token === ts.SyntaxKind.ExtendsKeyword; })[0] || { types: [] };
var implementsNames = new Set(implementsClause.types.map(function (t) { return t.expression.text; }));
var extendsNames = new Set(extendsClause.types.map(function (t) { return t.expression.text; }));
node.members
.filter(function (m) { return m.kind === ts.SyntaxKind.MethodDeclaration; })
.forEach(function (m) {
var meth = m;
var def = memberMap.get(meth.name.text);
if (def && def.kind === meth.kind) {
if (!implementsNames.has(def.type)) {
_this.addFailure(_this.createFailure(meth.name.pos + 4, meth.name.end - meth.name.pos, "The class " + node.name.text + " has member " + meth.name.text + ", but does not specify " + def.type + " type in implements clause"));
}
}
});
_super.prototype.visitClassDeclaration.call(this, node);
};
return Ng2InterfacesWalker;
}(Lint.RuleWalker));
import * as ts from 'typescript';
import * as Lint from 'tslint/lib/lint';
import {AbstractRule} from 'tslint/lib/language/rule/abstractRule';
import {RuleType} from 'tslint/lib/language/rule/rule';
export class Rule extends AbstractRule {
public static metadata = {
ruleName: 'ng2-intfs',
type: <RuleType>'typescript',
description: 'Requires the class to declare Angular 2 interfaces if implements their methods',
rationale: '',
options: undefined,
optionExamples: ['true']
};
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new Ng2InterfacesWalker(sourceFile, this.getOptions()));
}
}
const memberDefs = {
OnChanges: [{name: 'ngOnChanges', kind: ts.SyntaxKind.MethodDeclaration}],
OnInit: [{name: 'ngOnInit', kind: ts.SyntaxKind.MethodDeclaration}],
DoCheck: [{name: 'ngDoCheck', kind: ts.SyntaxKind.MethodDeclaration}],
AfterContentInit: [{name: 'ngAfterContentInit', kind: ts.SyntaxKind.MethodDeclaration}],
AfterContentChecked: [{name: 'ngAfterContentChecked', kind: ts.SyntaxKind.MethodDeclaration}],
AfterViewInit: [{name: 'ngAfterViewInit', kind: ts.SyntaxKind.MethodDeclaration}],
AfterViewChecked: [{name: 'ngAfterViewChecked', kind: ts.SyntaxKind.MethodDeclaration}],
OnDestroy: [{name: 'ngOnDestroy', kind: ts.SyntaxKind.MethodDeclaration}]
};
let memberMap = new Map();
Object.keys(memberDefs).forEach(nm => {
memberDefs[nm].forEach(d => memberMap.set(d.name, {
type: nm,
kind: d.kind
}));
});
class Ng2InterfacesWalker extends Lint.RuleWalker {
public visitClassDeclaration(node: ts.ClassDeclaration) {
let implementsClause = node.heritageClauses &&
node.heritageClauses.filter(cl => cl.token === ts.SyntaxKind.ImplementsKeyword)[0] || {types: []};
let extendsClause = node.heritageClauses &&
node.heritageClauses.filter(cl => cl.token === ts.SyntaxKind.ExtendsKeyword)[0] || {types: []};
let implementsNames = new Set(implementsClause.types.map(t => t.expression.text));
let extendsNames = new Set(extendsClause.types.map(t => t.expression.text));
node.members
.filter(m => m.kind === ts.SyntaxKind.MethodDeclaration)
.forEach(m => {
let meth = <ts.ClassElement> m;
let def = memberMap.get(meth.name.text);
if (def && def.kind === meth.kind) {
if (!implementsNames.has(def.type)) {
this.addFailure(this.createFailure(
meth.name.pos + 4, meth.name.end - meth.name.pos,
`The class ${node.name.text} has member ${meth.name.text}, but does not specify ${
def.type} type in implements clause`));
}
}
});
super.visitClassDeclaration(node);
}
}
{
"rules": {
"ng2-intfs": true
},
"rulesDirectory": ["tslint/"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment