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