UNPKG

eslint-plugin-codelyzer

Version:

152 lines (151 loc) 8.21 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 __()); }; })(); var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; Object.defineProperty(exports, "__esModule", { value: true }); var _a, _b, _c; var rules_1 = require("tslint/lib/rules"); var utils_1 = require("tslint/lib/utils"); var ngWalker_1 = require("./angular/ngWalker"); var basicTemplateAstVisitor_1 = require("./angular/templates/basicTemplateAstVisitor"); var isChildNodeOf_1 = require("./util/isChildNodeOf"); var objectKeys_1 = require("./util/objectKeys"); var OPTION_CONTROL_COMPONENTS = 'controlComponents'; var OPTION_LABEL_ATTRIBUTES = 'labelAttributes'; var OPTION_LABEL_COMPONENTS = 'labelComponents'; var OPTION_SCHEMA_VALUE = { properties: { items: { type: 'string' }, type: 'array', uniqueItems: true }, type: 'object' }; var DEFAULT_CONTROL_COMPONENTS = ['button', 'input', 'meter', 'output', 'progress', 'select', 'textarea']; var DEFAULT_LABEL_ATTRIBUTES = ['for', 'htmlFor']; var DEFAULT_LABEL_COMPONENTS = ['label']; var getReadableItems = function (items) { var itemsLength = items.length; if (itemsLength === 1) return "\"" + items[0] + "\""; return items .map(function (x) { return "\"" + x + "\""; }) .slice(0, itemsLength - 1) .join(', ') + " and \"" + items.slice().pop() + "\""; }; var Rule = (function (_super) { __extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.prototype.apply = function (sourceFile) { var walkerConfig = { templateVisitorCtrl: TemplateVisitorCtrl }; var walker = new ngWalker_1.NgWalker(sourceFile, this.getOptions(), walkerConfig); return this.applyWithWalker(walker); }; Rule.prototype.isEnabled = function () { return _super.prototype.isEnabled.call(this) && this.areOptionsValid(); }; Rule.prototype.areOptionsValid = function () { var ruleArgumentsLength = this.ruleArguments.length; if (ruleArgumentsLength === 0) return true; if (ruleArgumentsLength > 1) return false; var ruleOptions = Rule.metadata.options; var ruleArgument = this.ruleArguments[0]; var ruleArgumentsKeys = objectKeys_1.objectKeys(ruleArgument); var propertiesKeys = objectKeys_1.objectKeys(ruleOptions.properties); return (ruleArgumentsKeys.every(function (argumentKey) { return propertiesKeys.includes(argumentKey); }) && ruleArgumentsKeys .map(function (argumentKey) { return ruleArgument[argumentKey]; }) .every(function (argumentValue) { return Array.isArray(argumentValue) && argumentValue.length > 0; })); }; Rule.metadata = { description: 'Checks if a label component is associated with a form element', optionExamples: [ true, [ true, (_a = {}, _a[OPTION_CONTROL_COMPONENTS] = ['app-input'], _a) ], [ true, (_b = {}, _b[OPTION_CONTROL_COMPONENTS] = ['app-input', 'app-select'], _b[OPTION_LABEL_ATTRIBUTES] = ['id'], _b[OPTION_LABEL_COMPONENTS] = ['app-label'], _b) ] ], options: { additionalProperties: false, properties: (_c = {}, _c[OPTION_CONTROL_COMPONENTS] = OPTION_SCHEMA_VALUE, _c[OPTION_LABEL_ATTRIBUTES] = OPTION_SCHEMA_VALUE, _c[OPTION_LABEL_COMPONENTS] = OPTION_SCHEMA_VALUE, _c), type: 'object' }, optionsDescription: utils_1.dedent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n An optional object with optional `", "`, `", "` and `", "` properties.\n\n * `", "` - components that must be inside a label component. Default and non overridable values are\n ", ".\n * `", "` - attributes that must be set on label components. Default and non overridable values are\n ", ".\n * `", "` - components that act like a label. Default and non overridable values are\n ", ".\n "], ["\n An optional object with optional \\`", "\\`, \\`", "\\` and \\`", "\\` properties.\n\n * \\`", "\\` - components that must be inside a label component. Default and non overridable values are\n ", ".\n * \\`", "\\` - attributes that must be set on label components. Default and non overridable values are\n ", ".\n * \\`", "\\` - components that act like a label. Default and non overridable values are\n ", ".\n "])), OPTION_CONTROL_COMPONENTS, OPTION_LABEL_ATTRIBUTES, OPTION_LABEL_COMPONENTS, OPTION_CONTROL_COMPONENTS, getReadableItems(DEFAULT_CONTROL_COMPONENTS), OPTION_LABEL_ATTRIBUTES, getReadableItems(DEFAULT_LABEL_ATTRIBUTES), OPTION_LABEL_COMPONENTS, getReadableItems(DEFAULT_LABEL_COMPONENTS)), ruleName: 'template-accessibility-label-for', type: 'functionality', typescriptOnly: true }; Rule.FAILURE_STRING = 'A label component must be associated with a form element'; return Rule; }(rules_1.AbstractRule)); exports.Rule = Rule; var TemplateVisitorCtrl = (function (_super) { __extends(TemplateVisitorCtrl, _super); function TemplateVisitorCtrl(sourceFile, options, context, templateStart) { var _this = _super.call(this, sourceFile, options, context, templateStart) || this; var _a = (options.ruleArguments[0] || {}), controlComponents = _a.controlComponents, labelAttributes = _a.labelAttributes, labelComponents = _a.labelComponents; _this.controlComponents = new Set(DEFAULT_CONTROL_COMPONENTS.concat(controlComponents)); _this.labelAttributes = new Set(DEFAULT_LABEL_ATTRIBUTES.concat(labelAttributes)); _this.labelComponents = new Set(DEFAULT_LABEL_COMPONENTS.concat(labelComponents)); return _this; } TemplateVisitorCtrl.prototype.visitElement = function (element, context) { this.validateElement(element); _super.prototype.visitElement.call(this, element, context); }; TemplateVisitorCtrl.prototype.hasControlComponentInsideElement = function (element) { return Array.from(this.controlComponents).some(function (controlComponentName) { return isChildNodeOf_1.isChildNodeOf(element, controlComponentName); }); }; TemplateVisitorCtrl.prototype.hasValidAttrOrInput = function (element) { var _this = this; return element.attrs.concat(element.inputs).map(function (attrOrInput) { return attrOrInput.name; }) .some(function (attrOrInputName) { return _this.labelAttributes.has(attrOrInputName); }); }; TemplateVisitorCtrl.prototype.isLabelComponent = function (element) { return this.labelComponents.has(element.name); }; TemplateVisitorCtrl.prototype.validateElement = function (element) { if (!this.isLabelComponent(element) || this.hasValidAttrOrInput(element) || this.hasControlComponentInsideElement(element)) { return; } var _a = element.sourceSpan, endOffset = _a.end.offset, startOffset = _a.start.offset; this.addFailureFromStartToEnd(startOffset, endOffset, Rule.FAILURE_STRING); }; return TemplateVisitorCtrl; }(basicTemplateAstVisitor_1.BasicTemplateAstVisitor)); var templateObject_1;