UNPKG

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