UNPKG

4.26 kBJavaScriptView Raw
1/**
2 * @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("../ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 docs: {
21 description: "disallow `this` keywords outside of classes or class-like objects",
22 category: "Best Practices",
23 recommended: false
24 },
25
26 schema: []
27 },
28
29 create(context) {
30 const stack = [],
31 sourceCode = context.getSourceCode();
32
33 let insideClassProperty = false;
34
35 /**
36 * Gets the current checking context.
37 *
38 * The return value has a flag that whether or not `this` keyword is valid.
39 * The flag is initialized when got at the first time.
40 *
41 * @returns {{valid: boolean}}
42 * an object which has a flag that whether or not `this` keyword is valid.
43 */
44 stack.getCurrent = function() {
45 const current = this[this.length - 1];
46
47 if (!current.init) {
48 current.init = true;
49 current.valid = !astUtils.isDefaultThisBinding(
50 current.node,
51 sourceCode);
52 }
53 return current;
54 };
55
56 /**
57 * `this` should be fair game anywhere inside a class property.
58 *
59 * @returns {void}
60 */
61 function enterClassProperty() {
62 insideClassProperty = true;
63 }
64
65 /**
66 * Back to the normal check.
67 * @returns {void}
68 */
69 function exitClassProperty() {
70 insideClassProperty = false;
71 }
72
73 /**
74 * Pushs new checking context into the stack.
75 *
76 * The checking context is not initialized yet.
77 * Because most functions don't have `this` keyword.
78 * When `this` keyword was found, the checking context is initialized.
79 *
80 * @param {ASTNode} node - A function node that was entered.
81 * @returns {void}
82 */
83 function enterFunction(node) {
84
85 // `this` can be invalid only under strict mode.
86 stack.push({
87 init: !context.getScope().isStrict,
88 node,
89 valid: true
90 });
91 }
92
93 /**
94 * Pops the current checking context from the stack.
95 * @returns {void}
96 */
97 function exitFunction() {
98 stack.pop();
99 }
100
101 return {
102
103 /*
104 * `this` is invalid only under strict mode.
105 * Modules is always strict mode.
106 */
107 Program(node) {
108 const scope = context.getScope(),
109 features = context.parserOptions.ecmaFeatures || {};
110
111 stack.push({
112 init: true,
113 node,
114 valid: !(
115 scope.isStrict ||
116 node.sourceType === "module" ||
117 (features.globalReturn && scope.childScopes[0].isStrict)
118 )
119 });
120 },
121
122 "Program:exit"() {
123 stack.pop();
124 },
125
126 ClassProperty: enterClassProperty,
127 "ClassProperty:exit": exitClassProperty,
128 FunctionDeclaration: enterFunction,
129 "FunctionDeclaration:exit": exitFunction,
130 FunctionExpression: enterFunction,
131 "FunctionExpression:exit": exitFunction,
132
133 // Reports if `this` of the current context is invalid.
134 ThisExpression(node) {
135 const current = stack.getCurrent();
136
137 if (!insideClassProperty && current && !current.valid) {
138 context.report(node, "Unexpected 'this'.");
139 }
140 }
141 };
142 }
143};