UNPKG

eslint-plugin-codelyzer

Version:

143 lines (142 loc) 6.88 kB
"use strict"; 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); } } } } } }; } });