Search code examples
eslintprettier-eslintbabel-eslint

Eslint rule is running multiple times


I'm trying to write an eslint rule that enforces making sure the name property is defined on any classes that extend from other Error/Exception named classes (and fixes them).

As far as I can tell, it works in the astexplorer.net individually, but when I'm running it alongside other rules, it ends up getting ran multiple times, so the name property ends up being repeated multiple times in the resulting "fixed" file.

Is there anything in particular I can do to prevent it being run multiple times? I'm assuming what's happening is that it's inserting my name = 'ClassName';, then prettier is needing to reformat the code, which it does, but then maybe it's re-running my rule? I'm not sure.

Rule/fix code shown below. I've tried things like using *fix and yield, but that doesn't seem to help either (see commented code below, based on information in the eslint documentation)

module.exports = {
    meta: {
        hasSuggestions: true,
        type: 'suggestion',
        docs: {},
        fixable: 'code',
        schema: [], // no options,
    },
    create: function (context) {
        return {
            ClassDeclaration: function (node) {
                const regex = /.*(Error|Exception)$/;
                // If the parent/superClass is has "Error" or "Exception" in the name
                if (node.superClass && regex.test(node.superClass.name)) {
                    let name = null;
                    const className = node.id.name;
                    // Test class object name
                    if (!regex.test(className)) {
                        context.report({
                            node: node,
                            message: 'Error extensions must end with "Error" or "Exception".',
                        });
                    }

                    // Find name ClassProperty
                    node.body.body.some(function (a) {
                        if (a.type === 'ClassProperty' && a.key.name === 'name') {
                            name = a.value.value;
                            return true;
                        }
                    });

                    // Name property is required
                    if (!name) {
                        context.report({
                            node: node,
                            message: 'Error extensions should have a descriptive name',
                            fix(fixer) {
                                return fixer.replaceTextRange(
                                    [node.body.range[0]+1, node.body.range[0]+1],
                                    `name = '${className}';`
                                );
                            },

                            // *fix(fixer) {
                            //     name = className;
                            //     yield fixer.replaceTextRange(
                            //         [node.body.range[0]+1, node.body.range[0]+1],
                            //         `name = '${className}';`
                            //     );
                            //
                            //     // extend range of the fix to the range of `node.parent`
                            //     yield fixer.insertTextBefore(node.body, '');
                            //     yield fixer.insertTextAfter(node.body, '');
                            // },
                        });
                    }
                }
            },
        };
    },
};

Solution

  • Turns out I had the AST Explorer set to the wrong parser, so it was showing me the wrong string name for the ClassProperty node. I should have been using PropertyDefinition instead.