1 | "use strict";
|
2 | var __extends = (this && this.__extends) || (function () {
|
3 | var extendStatics = Object.setPrototypeOf ||
|
4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
6 | return function (d, b) {
|
7 | extendStatics(d, b);
|
8 | function __() { this.constructor = d; }
|
9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
10 | };
|
11 | })();
|
12 | Object.defineProperty(exports, "__esModule", { value: true });
|
13 | var ts = require("typescript");
|
14 | var Lint = require("tslint");
|
15 | var AstUtils_1 = require("./utils/AstUtils");
|
16 | var JsxAttribute_1 = require("./utils/JsxAttribute");
|
17 | var TypeGuard_1 = require("./utils/TypeGuard");
|
18 | var aria = require('./utils/attributes/ariaSchema.json');
|
19 | function getFailureString(propName, expectedType, permittedValues) {
|
20 | switch (expectedType) {
|
21 | case 'tristate':
|
22 | return "The value for " + propName + " must be a boolean or the string 'mixed'.";
|
23 | case 'token':
|
24 | return "The value for " + propName + " must be a single token from the following: " + permittedValues + ".";
|
25 | case 'tokenlist':
|
26 | return "The value for " + propName + " must be a list of one or more tokens from the following: " + permittedValues + ".";
|
27 | case 'boolean':
|
28 | case 'string':
|
29 | case 'integer':
|
30 | case 'number':
|
31 | default:
|
32 | return "The value for " + propName + " must be a " + expectedType + ".";
|
33 | }
|
34 | }
|
35 | exports.getFailureString = getFailureString;
|
36 | var Rule = (function (_super) {
|
37 | __extends(Rule, _super);
|
38 | function Rule() {
|
39 | return _super !== null && _super.apply(this, arguments) || this;
|
40 | }
|
41 | Rule.prototype.apply = function (sourceFile) {
|
42 | return sourceFile.languageVariant === ts.LanguageVariant.JSX
|
43 | ? this.applyWithWalker(new ReactA11yProptypesWalker(sourceFile, this.getOptions()))
|
44 | : [];
|
45 | };
|
46 | Rule.metadata = {
|
47 | ruleName: 'react-a11y-proptypes',
|
48 | type: 'maintainability',
|
49 | description: 'Enforce ARIA state and property values are valid.',
|
50 | options: null,
|
51 | optionsDescription: '',
|
52 | typescriptOnly: true,
|
53 | issueClass: 'Non-SDL',
|
54 | issueType: 'Warning',
|
55 | severity: 'Important',
|
56 | level: 'Opportunity for Excellence',
|
57 | group: 'Accessibility'
|
58 | };
|
59 | return Rule;
|
60 | }(Lint.Rules.AbstractRule));
|
61 | exports.Rule = Rule;
|
62 | var ReactA11yProptypesWalker = (function (_super) {
|
63 | __extends(ReactA11yProptypesWalker, _super);
|
64 | function ReactA11yProptypesWalker() {
|
65 | return _super !== null && _super.apply(this, arguments) || this;
|
66 | }
|
67 | ReactA11yProptypesWalker.prototype.visitJsxAttribute = function (node) {
|
68 | var propName = JsxAttribute_1.getPropName(node).toLowerCase();
|
69 | if (!aria[propName]) {
|
70 | return;
|
71 | }
|
72 | var allowUndefined = aria[propName].allowUndefined != null
|
73 | ? aria[propName].allowUndefined
|
74 | : false;
|
75 | var expectedType = aria[propName].type;
|
76 | var permittedValues = aria[propName].values;
|
77 | var propValue = JsxAttribute_1.getStringLiteral(node) || String(JsxAttribute_1.getBooleanLiteral(node));
|
78 | if (this.isUndefined(node.initializer)) {
|
79 | if (!allowUndefined) {
|
80 | this.addFailureAt(node.getStart(), node.getWidth(), getFailureString(propName, expectedType, permittedValues));
|
81 | }
|
82 | return;
|
83 | }
|
84 | else if (this.isComplexType(node.initializer)) {
|
85 | return;
|
86 | }
|
87 | if (!this.validityCheck(node.initializer, propValue, expectedType, permittedValues)) {
|
88 | this.addFailureAt(node.getStart(), node.getWidth(), getFailureString(propName, expectedType, permittedValues));
|
89 | }
|
90 | };
|
91 | ReactA11yProptypesWalker.prototype.validityCheck = function (propValueExpression, propValue, expectedType, permittedValues) {
|
92 | switch (expectedType) {
|
93 | case 'boolean': return this.isBoolean(propValueExpression);
|
94 | case 'tristate': return this.isBoolean(propValueExpression) || this.isMixed(propValueExpression);
|
95 | case 'integer': return this.isInteger(propValueExpression);
|
96 | case 'number': return this.isNumber(propValueExpression);
|
97 | case 'string': return this.isString(propValueExpression);
|
98 | case 'token':
|
99 | return (this.isString(propValueExpression) || this.isBoolean(propValueExpression)) &&
|
100 | permittedValues.indexOf(propValue.toLowerCase()) > -1;
|
101 | case 'tokenlist':
|
102 | return (this.isString(propValueExpression) || this.isBoolean(propValueExpression)) &&
|
103 | propValue.split(' ').every(function (token) { return permittedValues.indexOf(token.toLowerCase()) > -1; });
|
104 | default:
|
105 | return false;
|
106 | }
|
107 | };
|
108 | ReactA11yProptypesWalker.prototype.isUndefined = function (node) {
|
109 | if (!node) {
|
110 | return true;
|
111 | }
|
112 | else if (TypeGuard_1.isJsxExpression(node)) {
|
113 | var expression = node.expression;
|
114 | if (!expression) {
|
115 | return true;
|
116 | }
|
117 | else if (AstUtils_1.AstUtils.isUndefined(expression)) {
|
118 | return true;
|
119 | }
|
120 | else if (TypeGuard_1.isNullKeyword(expression)) {
|
121 | return true;
|
122 | }
|
123 | }
|
124 | return false;
|
125 | };
|
126 | ReactA11yProptypesWalker.prototype.isComplexType = function (node) {
|
127 | return !this.isUndefined(node) && TypeGuard_1.isJsxExpression(node) && !AstUtils_1.AstUtils.isConstant(node.expression);
|
128 | };
|
129 | ReactA11yProptypesWalker.prototype.isBoolean = function (node) {
|
130 | if (TypeGuard_1.isStringLiteral(node)) {
|
131 | var propValue = node.text.toLowerCase();
|
132 | return propValue === 'true' || propValue === 'false';
|
133 | }
|
134 | else if (TypeGuard_1.isJsxExpression(node)) {
|
135 | var expression = node.expression;
|
136 | if (TypeGuard_1.isStringLiteral(expression)) {
|
137 | var propValue = expression.text.toLowerCase();
|
138 | return propValue === 'true' || propValue === 'false';
|
139 | }
|
140 | else {
|
141 | return TypeGuard_1.isFalseKeyword(expression) || TypeGuard_1.isTrueKeyword(expression);
|
142 | }
|
143 | }
|
144 | return false;
|
145 | };
|
146 | ReactA11yProptypesWalker.prototype.isMixed = function (node) {
|
147 | if (TypeGuard_1.isStringLiteral(node)) {
|
148 | return node.text.toLowerCase() === 'mixed';
|
149 | }
|
150 | else if (TypeGuard_1.isJsxExpression(node)) {
|
151 | var expression = node.expression;
|
152 | return TypeGuard_1.isStringLiteral(expression) && expression.text.toLowerCase() === 'mixed';
|
153 | }
|
154 | return false;
|
155 | };
|
156 | ReactA11yProptypesWalker.prototype.isNumber = function (node) {
|
157 | if (TypeGuard_1.isStringLiteral(node)) {
|
158 | return !isNaN(Number(node.text));
|
159 | }
|
160 | else if (TypeGuard_1.isJsxExpression(node)) {
|
161 | var expression = node.expression;
|
162 | if (TypeGuard_1.isStringLiteral(expression)) {
|
163 | return !isNaN(Number(expression.text));
|
164 | }
|
165 | else {
|
166 | return TypeGuard_1.isNumericLiteral(expression);
|
167 | }
|
168 | }
|
169 | return false;
|
170 | };
|
171 | ReactA11yProptypesWalker.prototype.isInteger = function (node) {
|
172 | if (TypeGuard_1.isStringLiteral(node)) {
|
173 | var value = Number(node.text);
|
174 | return !isNaN(value) && Math.round(value) === value;
|
175 | }
|
176 | else if (TypeGuard_1.isJsxExpression(node)) {
|
177 | var expression = node.expression;
|
178 | if (TypeGuard_1.isStringLiteral(expression)) {
|
179 | var value = Number(expression.text);
|
180 | return !isNaN(value) && Math.round(value) === value;
|
181 | }
|
182 | else if (TypeGuard_1.isNumericLiteral(expression)) {
|
183 | var value = Number(expression.text);
|
184 | return Math.round(value) === value;
|
185 | }
|
186 | return false;
|
187 | }
|
188 | return false;
|
189 | };
|
190 | ReactA11yProptypesWalker.prototype.isString = function (node) {
|
191 | return TypeGuard_1.isStringLiteral(node) || (TypeGuard_1.isJsxExpression(node) && TypeGuard_1.isStringLiteral(node.expression));
|
192 | };
|
193 | return ReactA11yProptypesWalker;
|
194 | }(Lint.RuleWalker));
|
195 |
|
\ | No newline at end of file |