This guide describes the recommended linter configuration for Node.js projects (back/front) + CloudFormation.
The following dependencies needs to be installed manually:
- Install Docker Desktop
- Install Node.js 22.x
- Upgrade NPM to the most recent version:
$ npm install --global npm@latest
- Install Python 3.x
- Install CloudFormation linter:
$ pip install cfn-lint
- Install CloudFormation CLI:
$ pip install cloudformation-cli cloudformation-cli-python-plugin
Upgrade the above Python packages periodically by running:
$ pip install --upgrade cfn-lint cloudformation-cli cloudformation-cli-python-plugin
There are IDE plugins available to get direct linter feedback from you favorite editor:
- Atom
- Emacs
- NeoVim 0.2.0+/Vim 8
- SublimeText
- Visual Studio Code
- IntelliJ IDEA
EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.
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
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
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',
},
},
];
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'
},
},
];