Skip to content

Instantly share code, notes, and snippets.

@projected1
Last active January 29, 2025 10:14
Show Gist options
  • Save projected1/972fb3fa1ccc99ac8edca1d290267b4b to your computer and use it in GitHub Desktop.
Save projected1/972fb3fa1ccc99ac8edca1d290267b4b to your computer and use it in GitHub Desktop.
Linter config for Node.js projects

Linter Configuration

This guide describes the recommended linter configuration for Node.js projects (back/front) + CloudFormation.

Prerequisites

The following dependencies needs to be installed manually:

Upgrade the above Python packages periodically by running:

$ pip install --upgrade cfn-lint cloudformation-cli cloudformation-cli-python-plugin

CFN Lint Plugins

There are IDE plugins available to get direct linter feedback from you favorite editor:

EditorConfig Plugins

EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.

EditorConfig Backend Config

Copy the following configuration to your .editorconfig file:

root = true

[*]

# Change these settings to your own preference
indent_style = space
indent_size = 4

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

[*.{json,xml,yml,yaml}]
indent_size = 2

EditorConfig Frontend Config

Copy the following configuration to your .editorconfig file:

root = true

[*]

# Change these settings to your own preference
indent_style = space
indent_size = 2

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

Eslint Backend Config

Install NPM dependencies:

npm install --save-dev \
    @eslint/js \
    eslint-plugin-eslint-comments \
    eslint-plugin-json \
    eslint-plugin-mocha \
    eslint-plugin-promise \
    eslint-plugin-yml \
    globals

Copy the following configuration to your eslint.config.js file:

import js from '@eslint/js';
import globals from 'globals';
import eslintYml from 'eslint-plugin-yml';
import eslintJson from 'eslint-plugin-json';
import eslintMocha from 'eslint-plugin-mocha';
import eslintComments from 'eslint-plugin-eslint-comments';

export default [
    js.configs.recommended,

    {
        languageOptions: {
            ecmaVersion: 2022,
            sourceType: 'module',
            globals: {
                ...globals.node,
                document: true,
            },
            parserOptions: {
                sourceType: 'module',
                ecmaVersion: 'latest',
            },
        },
        plugins: {
            yml: eslintYml,
            json: eslintJson,
            mocha: eslintMocha,
            'eslint-comments': eslintComments,
        },
        rules: {
            semi: 'error',
            curly: 'error',
            strict: ['error', 'safe'],
            eqeqeq: 'error',
            indent: ['error', 4, { SwitchCase: 1 }],
            quotes: ['error', 'single', { avoidEscape: true }],
            camelcase: ['error', { properties: 'never' }],
            'array-bracket-spacing': ['error', 'never'],
            'arrow-spacing': 'error',
            'comma-dangle': ['error', 'always-multiline'],
            'eol-last': 'error',
            'func-call-spacing': 'error',
            'max-depth': ['error', 3],
            'max-len': ['error', { code: 100 }],
            'max-nested-callbacks': ['error', 3],
            'max-statements-per-line': ['error', { max: 1 }],
            'newline-per-chained-call': ['error', { ignoreChainWithDepth: 3 }],
            'no-duplicate-imports': 'error',
            'no-multi-assign': 'error',
            'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }],
            'no-nested-ternary': 'error',
            'no-trailing-spaces': 'error',
            'no-unneeded-ternary': 'error',
            'no-useless-constructor': 'error',
            'no-var': 'error',
            'object-curly-spacing': ['error', 'always'],
            'prefer-const': 'error',
            'space-before-blocks': 'error',
            'brace-style': 'error',
            'keyword-spacing': 'error',
            'space-before-function-paren': ['error', {
                named: 'never',
                anonymous: 'never',
                asyncArrow: 'always',
            }],
            'spaced-comment': ['error', 'always'],
            'no-whitespace-before-property': 'error',
            'arrow-parens': ['error', 'always'],

            // plugin:yml rules
            'yml/quotes': ['error', {
                prefer: 'single',
                avoidEscape: true,
            }],

            // plugin:eslint-comments rules
            'eslint-comments/no-use': 'error',
        },

        ignores: [
            'node_modules/**',
            'coverage/**',
            '.aws-sam/**',
            '.next/**',
            '.dist/**',
            'dist/**',
            'out/**',
            '!.github/**',
        ],
    },

    // Test files configuration
    {
        files: ['test/**/*'],
        languageOptions: {
            globals: {
                describe: 'readonly',
                it: 'readonly',
                before: 'readonly',
                after: 'readonly',
                beforeEach: 'readonly',
                afterEach: 'readonly',
            },
        },
        rules: {
            'max-nested-callbacks': ['error', 10],
            'mocha/no-mocha-arrows': 'off',
            'mocha/no-skipped-tests': 'error',
            'mocha/no-exclusive-tests': 'error',
        },
    },
];

Eslint Frontend Config

Install NPM dependencies:

npm install --save-dev \
    @eslint/js \
    eslint \
    eslint-plugin-eslint-comments \
    eslint-plugin-json \
    eslint-plugin-jsx-a11y \
    eslint-plugin-promise \
    eslint-plugin-react \
    eslint-plugin-react-hooks \
    eslint-plugin-react-refresh \
    eslint-plugin-yml \
    globals

Copy the following configuration to your eslint.config.js file:

import js from '@eslint/js';
import globals from 'globals';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';

export default [
  { ignores: ['dist'] },
  {
    files: ['**/*.{js,jsx}'],
    languageOptions: {
      ecmaVersion: 2022,
      globals: {
        ...globals.browser,
        __dirname: true,
      },
      parserOptions: {
        sourceType: 'module',
        ecmaVersion: 'latest',
        ecmaFeatures: { jsx: true },
      },
    },
    settings: { react: { version: 'detect' } },
    plugins: {
      react,
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...react.configs.recommended.rules,
      ...react.configs['jsx-runtime'].rules,
      ...reactHooks.configs.recommended.rules,
      'react/prop-types': 'off',
      'react/jsx-no-target-blank': 'off',
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
      semi: ['error', 'always'],
      'semi-spacing': ['error', {
        before: false,
        after: true
      }],
      'semi-style': ['error', 'last'],
      'no-extra-semi': 'error',
      'no-unexpected-multiline': 'error'
    },
  },
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment