1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | "use strict";
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var assign = require("object-assign");
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | var messages = {
|
22 | function: "Use the function form of 'use strict'.",
|
23 | global: "Use the global form of 'use strict'.",
|
24 | multiple: "Multiple 'use strict' directives.",
|
25 | never: "Strict mode is not permitted.",
|
26 | unnecessary: "Unnecessary 'use strict' directive.",
|
27 | module: "'use strict' is unnecessary inside of modules.",
|
28 | unnecessaryInClasses: "'use strict' is unnecessary inside of classes."
|
29 | };
|
30 |
|
31 | /**
|
32 | * Gets all of the Use Strict Directives in the Directive Prologue of a group of
|
33 | * statements.
|
34 | * @param {ASTNode[]} statements Statements in the program or function body.
|
35 | * @returns {ASTNode[]} All of the Use Strict Directives.
|
36 | */
|
37 | function getUseStrictDirectives(statements) {
|
38 | var directives = [],
|
39 | i, statement;
|
40 |
|
41 | for (i = 0; i < statements.length; i++) {
|
42 | statement = statements[i];
|
43 |
|
44 | if (
|
45 | statement.type === "ExpressionStatement" &&
|
46 | statement.expression.type === "Literal" &&
|
47 | statement.expression.value === "use strict"
|
48 | ) {
|
49 | directives[i] = statement;
|
50 | } else {
|
51 | break;
|
52 | }
|
53 | }
|
54 |
|
55 | return directives;
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | module.exports = function(context) {
|
63 |
|
64 | var mode = context.options[0] || "safe",
|
65 | scopes = [],
|
66 | classScopes = [],
|
67 | rule;
|
68 |
|
69 | if (mode === "safe") {
|
70 | mode = context.parserOptions.ecmaFeatures &&
|
71 | context.parserOptions.ecmaFeatures.globalReturn ?
|
72 | "global" : "function";
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | function reportSlice(nodes, start, end, message) {
|
84 | var i;
|
85 |
|
86 | for (i = start; i < end; i++) {
|
87 | context.report(nodes[i], message);
|
88 | }
|
89 | }
|
90 |
|
91 | |
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | function reportAll(nodes, message) {
|
98 | reportSlice(nodes, 0, nodes.length, message);
|
99 | }
|
100 |
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | function reportAllExceptFirst(nodes, message) {
|
108 | reportSlice(nodes, 1, nodes.length, message);
|
109 | }
|
110 |
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 | function enterFunctionInFunctionMode(node, useStrictDirectives) {
|
119 | var isInClass = classScopes.length > 0,
|
120 | isParentGlobal = scopes.length === 0 && classScopes.length === 0,
|
121 | isParentStrict = scopes.length > 0 && scopes[scopes.length - 1],
|
122 | isStrict = useStrictDirectives.length > 0;
|
123 |
|
124 | if (isStrict) {
|
125 | if (isParentStrict) {
|
126 | context.report(useStrictDirectives[0], messages.unnecessary);
|
127 | } else if (isInClass) {
|
128 | context.report(useStrictDirectives[0], messages.unnecessaryInClasses);
|
129 | }
|
130 |
|
131 | reportAllExceptFirst(useStrictDirectives, messages.multiple);
|
132 | } else if (isParentGlobal) {
|
133 | context.report(node, messages.function);
|
134 | }
|
135 |
|
136 | scopes.push(isParentStrict || isStrict);
|
137 | }
|
138 |
|
139 | |
140 |
|
141 |
|
142 |
|
143 | function exitFunctionInFunctionMode() {
|
144 | scopes.pop();
|
145 | }
|
146 |
|
147 | |
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | function enterFunction(node) {
|
155 | var isBlock = node.body.type === "BlockStatement",
|
156 | useStrictDirectives = isBlock ?
|
157 | getUseStrictDirectives(node.body.body) : [];
|
158 |
|
159 | if (mode === "function") {
|
160 | enterFunctionInFunctionMode(node, useStrictDirectives);
|
161 | } else {
|
162 | reportAll(useStrictDirectives, messages[mode]);
|
163 | }
|
164 | }
|
165 |
|
166 | rule = {
|
167 | "Program": function(node) {
|
168 | var useStrictDirectives = getUseStrictDirectives(node.body);
|
169 |
|
170 | if (node.sourceType === "module") {
|
171 | mode = "module";
|
172 | }
|
173 |
|
174 | if (mode === "global") {
|
175 | if (node.body.length > 0 && useStrictDirectives.length === 0) {
|
176 | context.report(node, messages.global);
|
177 | }
|
178 | reportAllExceptFirst(useStrictDirectives, messages.multiple);
|
179 | } else {
|
180 | reportAll(useStrictDirectives, messages[mode]);
|
181 | }
|
182 | },
|
183 | "FunctionDeclaration": enterFunction,
|
184 | "FunctionExpression": enterFunction,
|
185 | "ArrowFunctionExpression": enterFunction
|
186 | };
|
187 |
|
188 | if (mode === "function") {
|
189 | assign(rule, {
|
190 |
|
191 | "ClassBody": function() {
|
192 | classScopes.push(true);
|
193 | },
|
194 | "ClassBody:exit": function() {
|
195 | classScopes.pop();
|
196 | },
|
197 |
|
198 | "FunctionDeclaration:exit": exitFunctionInFunctionMode,
|
199 | "FunctionExpression:exit": exitFunctionInFunctionMode,
|
200 | "ArrowFunctionExpression:exit": exitFunctionInFunctionMode
|
201 | });
|
202 | }
|
203 |
|
204 | return rule;
|
205 | };
|
206 |
|
207 | module.exports.schema = [
|
208 | {
|
209 | "enum": ["never", "global", "function", "safe"]
|
210 | }
|
211 | ];
|