Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows this in EventEmitter's callbacks (no-invalid-this) #4

Open
mysticatea opened this issue Dec 13, 2015 · 2 comments
Open

Allows this in EventEmitter's callbacks (no-invalid-this) #4

mysticatea opened this issue Dec 13, 2015 · 2 comments

Comments

@mysticatea
Copy link
Owner

No description provided.

@Antonius-S
Copy link

Made quick & dirty change to no-invalid-this that seems to work for me (add isListener function and call it together with astUtils.isDefaultThisBinding)

/**
 * @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
 * @author Toru Nagashima
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "disallow `this` keywords outside of classes or class-like objects",
            category: "Best Practices",
            recommended: false,
            url: "https://eslint.org/docs/rules/no-invalid-this"
        },

        schema: [
            {
                type: "object",
                properties: {
                    capIsConstructor: {
                        type: "boolean",
                        default: true
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unexpectedThis: "Unexpected 'this'."
        }
    },

    create(context) {
        const options = context.options[0] || {};
        const capIsConstructor = options.capIsConstructor !== false;
        const stack = [],
            sourceCode = context.getSourceCode();

        /**
         * Gets the current checking context.
         *
         * The return value has a flag that whether or not `this` keyword is valid.
         * The flag is initialized when got at the first time.
         * @returns {{valid: boolean}}
         *   an object which has a flag that whether or not `this` keyword is valid.
         */
        stack.getCurrent = function() {
            const current = this[this.length - 1];

            if (!current.init) {
                current.init = true;
                current.valid = !astUtils.isDefaultThisBinding(
                    current.node,
                    sourceCode,
                    { capIsConstructor }
                ) || isListener(current.node);
            }
            return current;
        };

        /**
         * Checks if function node is used in NodeJS listener addition.
         * @param {ASTNode} node A function node
         * @returns {boolean} A function node is used in NodeJS listener addition.
         */
        function isListener(node)
        {
            const listenersArray = ["on", "once", "addListener", "prependListener", "prependOnceListener"];
            if (node.type !== "FunctionExpression") return false;
            if (!node.parent || node.parent.type !== "CallExpression") return false;
            const callee = node.parent.callee;
            if (!callee || callee.type !== "MemberExpression") return false;
            return (listenersArray.indexOf(callee.property.name) != -1);
        }

        /**
         * Pushs new checking context into the stack.
         *
         * The checking context is not initialized yet.
         * Because most functions don't have `this` keyword.
         * When `this` keyword was found, the checking context is initialized.
         * @param {ASTNode} node A function node that was entered.
         * @returns {void}
         */
        function enterFunction(node) {

            // `this` can be invalid only under strict mode.
            stack.push({
                init: !context.getScope().isStrict,
                node,
                valid: true
            });
        }

        /**
         * Pops the current checking context from the stack.
         * @returns {void}
         */
        function exitFunction() {
            stack.pop();
        }

        return {

            /*
             * `this` is invalid only under strict mode.
             * Modules is always strict mode.
             */
            Program(node) {
                const scope = context.getScope(),
                    features = context.parserOptions.ecmaFeatures || {};

                stack.push({
                    init: true,
                    node,
                    valid: !(
                        scope.isStrict ||
                        node.sourceType === "module" ||
                        (features.globalReturn && scope.childScopes[0].isStrict)
                    )
                });
            },

            "Program:exit"() {
                stack.pop();
            },

            FunctionDeclaration: enterFunction,
            "FunctionDeclaration:exit": exitFunction,
            FunctionExpression: enterFunction,
            "FunctionExpression:exit": exitFunction,

            // Reports if `this` of the current context is invalid.
            ThisExpression(node) {
                const current = stack.getCurrent();

                if (current && !current.valid) {
                    context.report({
                        node,
                        messageId: "unexpectedThis"
                    });
                }
            }
        };
    }
};

@Antonius-S
Copy link

Reworked this rule along with several others to use as custom rule set in https://github.com/Antonius-S/ESLint-custom-rules

nazrhyn pushed a commit to nazrhyn/eslint-plugin-node that referenced this issue Mar 23, 2022
* add support for Node.js ESM resolution

* Update lib/rules/no-hide-core-modules.js

Co-authored-by: 唯然 <weiran.zsd@outlook.com>

* Update lib/util/import-target.js

* invalidate ESM imports that were valid in Node 10

* Update scripts/update.js

Co-authored-by: 唯然 <weiran.zsd@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants