eslint-plugin-codelyzer
Version:
143 lines (142 loc) • 6.88 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var compiler = require("@angular/compiler");
var ts = require("typescript");
var angular_1 = require("../../angular");
var ngWalkerFactoryUtils_1 = require("../../angular/ngWalkerFactoryUtils");
var recursiveAngularExpressionVisitor_1 = require("../../angular/templates/recursiveAngularExpressionVisitor");
var referenceCollectorVisitor_1 = require("../../angular/templates/referenceCollectorVisitor");
var templateParser_1 = require("../../angular/templates/templateParser");
var utils_1 = require("../../util/utils");
var create_eslint_rule_1 = require("../util/create-eslint-rule");
exports.RULE_NAME = 'template-banana-in-a-box';
var INVALID_PATTERN = /\[(.*)\]/;
var VALID_CLOSE_BOX = ')]';
var VALID_OPEN_BOX = '[(';
exports.default = create_eslint_rule_1.createESLintRule({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: "Ensures that the two-way data binding syntax is correct",
category: 'Best Practices',
recommended: false
},
fixable: 'code',
schema: [],
messages: {
templateBananaInABox: 'Invalid binding syntax. Use [(expr)] instead'
}
},
defaultOptions: [],
create: function (context) {
var parserServices = create_eslint_rule_1.getParserServices(context);
var metadataReader = ngWalkerFactoryUtils_1.ngWalkerFactoryUtils.defaultMetadataReader();
function getContextSourceFile(path, content) {
if (!path) {
return parserServices.program.getSourceFile(context.getFilename());
}
var sf = ts.createSourceFile(path, "`" + content + "`", ts.ScriptTarget.ES5);
var original = sf.getFullText;
sf.getFullText = function () {
var text = original.apply(sf);
return text.substring(1, text.length - 1);
};
return sf;
}
var getPosition = function (node) {
var pos = 0;
if (node) {
pos = node.pos + 1;
try {
pos = node.getStart() + 1;
}
catch (_a) { }
}
return pos;
};
var TemplateVisitorCtrl = (function (_super) {
__extends(TemplateVisitorCtrl, _super);
function TemplateVisitorCtrl() {
return _super !== null && _super.apply(this, arguments) || this;
}
TemplateVisitorCtrl.prototype.visitEvent = function (ast, context) {
this.validateEvent(ast);
_super.prototype.visitEvent.call(this, ast, context);
};
TemplateVisitorCtrl.prototype.validateEvent = function (ast) {
var matches = ast.name.match(INVALID_PATTERN);
if (!matches)
return;
var startOffset = ast.sourceSpan.start.offset;
var text = matches[1];
var absoluteStartPosition = this.getSourcePosition(startOffset);
var absoluteEndPosition = absoluteStartPosition + VALID_OPEN_BOX.length + text.length + VALID_CLOSE_BOX.length;
var newText = "" + VALID_OPEN_BOX + text + VALID_CLOSE_BOX;
var start = ts.getLineAndCharacterOfPosition(this.getSourceFile(), absoluteStartPosition);
var end = ts.getLineAndCharacterOfPosition(this.getSourceFile(), absoluteEndPosition);
context.report({
messageId: 'templateBananaInABox',
loc: {
start: {
line: start.line + 1,
column: start.character + 1
},
end: {
line: end.line + 1,
column: end.character + 1
}
},
fix: function (fixer) { return fixer.replaceTextRange([absoluteStartPosition, absoluteEndPosition], newText); }
});
};
return TemplateVisitorCtrl;
}(angular_1.BasicTemplateAstVisitor));
function visitNgTemplateHelper(roots, componentMetadataContext, baseStart) {
if (!roots || !roots.length) {
return;
}
var sourceFile = getContextSourceFile(componentMetadataContext.template.url, componentMetadataContext.template.template.source);
var referenceVisitor = new referenceCollectorVisitor_1.ReferenceCollectorVisitor();
var visitor = new TemplateVisitorCtrl(sourceFile, {}, componentMetadataContext, baseStart, recursiveAngularExpressionVisitor_1.RecursiveAngularExpressionVisitor);
compiler.templateVisitAll(referenceVisitor, roots, null);
visitor._variables = referenceVisitor.variables;
roots.forEach(function (r) { return visitor.visit(r, componentMetadataContext.controller); });
}
return {
ClassDeclaration: function (node) {
var tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
if (utils_1.getClassName(tsNode)) {
var metadata = metadataReader.read(tsNode);
if (metadata instanceof angular_1.ComponentMetadata) {
var template = metadata.template;
if (template && template.template) {
try {
var templateAst = templateParser_1.parseTemplate(template.template.code, angular_1.Config.predefinedDirectives);
visitNgTemplateHelper(templateAst, metadata, getPosition(template.node));
}
catch (e) {
var name_1 = metadata.controller.name;
var text = name_1 && ts.isIdentifier(name_1) ? name_1.text : '';
console.error('Cannot parse the template of', text, e);
}
}
}
}
}
};
}
});