UNPKG

6.87 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to control usage of strict mode directives.
3 * @author Brandon Mills
4 * @copyright 2015 Brandon Mills. All rights reserved.
5 * @copyright 2013-2014 Nicholas C. Zakas. All rights reserved.
6 * @copyright 2013 Ian Christian Myers. All rights reserved.
7 */
8
9"use strict";
10
11//------------------------------------------------------------------------------
12// Requirements
13//------------------------------------------------------------------------------
14
15var assign = require("object-assign");
16
17//------------------------------------------------------------------------------
18// Helpers
19//------------------------------------------------------------------------------
20
21var 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 */
37function 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// Rule Definition
60//------------------------------------------------------------------------------
61
62module.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 * Report a slice of an array of nodes with a given message.
77 * @param {ASTNode[]} nodes Nodes.
78 * @param {string} start Index to start from.
79 * @param {string} end Index to end before.
80 * @param {string} message Message to display.
81 * @returns {void}
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 * Report all nodes in an array with a given message.
93 * @param {ASTNode[]} nodes Nodes.
94 * @param {string} message Message to display.
95 * @returns {void}
96 */
97 function reportAll(nodes, message) {
98 reportSlice(nodes, 0, nodes.length, message);
99 }
100
101 /**
102 * Report all nodes in an array, except the first, with a given message.
103 * @param {ASTNode[]} nodes Nodes.
104 * @param {string} message Message to display.
105 * @returns {void}
106 */
107 function reportAllExceptFirst(nodes, message) {
108 reportSlice(nodes, 1, nodes.length, message);
109 }
110
111 /**
112 * Entering a function in 'function' mode pushes a new nested scope onto the
113 * stack. The new scope is true if the nested function is strict mode code.
114 * @param {ASTNode} node The function declaration or expression.
115 * @param {ASTNode[]} useStrictDirectives The Use Strict Directives of the node.
116 * @returns {void}
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 * Exiting a function in 'function' mode pops its scope off the stack.
141 * @returns {void}
142 */
143 function exitFunctionInFunctionMode() {
144 scopes.pop();
145 }
146
147 /**
148 * Enter a function and either:
149 * - Push a new nested scope onto the stack (in 'function' mode).
150 * - Report all the Use Strict Directives (in the other modes).
151 * @param {ASTNode} node The function declaration or expression.
152 * @returns {void}
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 // Inside of class bodies are always strict mode.
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
207module.exports.schema = [
208 {
209 "enum": ["never", "global", "function", "safe"]
210 }
211];