UNPKG

3.71 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 /**
34 * Gets the current checking context.
35 *
36 * The return value has a flag that whether or not `this` keyword is valid.
37 * The flag is initialized when got at the first time.
38 *
39 * @returns {{valid: boolean}}
40 * an object which has a flag that whether or not `this` keyword is valid.
41 */
42 stack.getCurrent = function() {
43 const current = this[this.length - 1];
44
45 if (!current.init) {
46 current.init = true;
47 current.valid = !astUtils.isDefaultThisBinding(
48 current.node,
49 sourceCode);
50 }
51 return current;
52 };
53
54 /**
55 * Pushs new checking context into the stack.
56 *
57 * The checking context is not initialized yet.
58 * Because most functions don't have `this` keyword.
59 * When `this` keyword was found, the checking context is initialized.
60 *
61 * @param {ASTNode} node - A function node that was entered.
62 * @returns {void}
63 */
64 function enterFunction(node) {
65
66 // `this` can be invalid only under strict mode.
67 stack.push({
68 init: !context.getScope().isStrict,
69 node,
70 valid: true
71 });
72 }
73
74 /**
75 * Pops the current checking context from the stack.
76 * @returns {void}
77 */
78 function exitFunction() {
79 stack.pop();
80 }
81
82 return {
83
84 /*
85 * `this` is invalid only under strict mode.
86 * Modules is always strict mode.
87 */
88 Program(node) {
89 const scope = context.getScope(),
90 features = context.parserOptions.ecmaFeatures || {};
91
92 stack.push({
93 init: true,
94 node,
95 valid: !(
96 scope.isStrict ||
97 node.sourceType === "module" ||
98 (features.globalReturn && scope.childScopes[0].isStrict)
99 )
100 });
101 },
102
103 "Program:exit"() {
104 stack.pop();
105 },
106
107 FunctionDeclaration: enterFunction,
108 "FunctionDeclaration:exit": exitFunction,
109 FunctionExpression: enterFunction,
110 "FunctionExpression:exit": exitFunction,
111
112 // Reports if `this` of the current context is invalid.
113 ThisExpression(node) {
114 const current = stack.getCurrent();
115
116 if (current && !current.valid) {
117 context.report({ node, message: "Unexpected 'this'." });
118 }
119 }
120 };
121 }
122};