website/docs/guides/examples/eslint.mdx
import AddDepsTabs from '@site/src/components/AddDepsTabs'; import HeadingApiLink from '@site/src/components/Docs/HeadingApiLink';
<HeadingApiLink to="https://github.com/moonrepo/examples/blob/master/.moon/tasks/node.yml#L59" />In this guide, you'll learn how to integrate ESLint into moon.
Begin by installing eslint and any plugins in your root. We suggest using the same version across
the entire repository.
Since linting is a universal workflow, add a lint task to .moon/tasks/**/*
with the following parameters.
tasks:
lint:
command:
- 'eslint'
# Support other extensions
- '--ext'
- '.js,.jsx,.ts,.tsx'
# Always fix and run extra checks
- '--fix'
- '--report-unused-disable-directives'
# Dont fail if a project has nothing to lint
- '--no-error-on-unmatched-pattern'
# Do fail if we encounter a fatal error
- '--exit-on-fatal-error'
# Only 1 ignore file is supported, so use the root
- '--ignore-path'
- '@in(4)'
# Run in current dir
- '.'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Other config files
- '*.config.*'
# Project configs, any format, any depth
- '**/.eslintrc.*'
# Root configs, any format
- '/.eslintignore'
- '/.eslintrc.*'
Projects can extend this task and provide additional parameters if need be, for example.
tasks:
lint:
args:
# Enable caching for this project
- '--cache'
If you're using the @typescript-eslint packages, and want to
enable type-safety based lint rules, we suggest something similar to the official
monorepo configuration.
Create a tsconfig.eslint.json in your repository root, extend your shared compiler options (we use
tsconfig.options.json), and include all your project files.
{
"extends": "./tsconfig.options.json",
"compilerOptions": {
"emitDeclarationOnly": false,
"noEmit": true
},
"include": ["apps/**/*", "packages/**/*"]
}
Append the following inputs to your lint task.
tasks:
lint:
# ...
inputs:
# TypeScript support
- 'types/**/*'
- 'tsconfig.json'
- '/tsconfig.eslint.json'
- '/tsconfig.options.json'
And lastly, add parserOptions to your root-level config.
The root-level ESLint config is required, as ESLint traverses upwards from each file to find configurations, and this denotes the stopping point. It's also used to define rules for the entire repository.
module.exports = {
root: true, // Required!
extends: ['moon'],
rules: {
'no-console': 'error',
},
// TypeScript support
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
};
The .eslintignore file must also be defined at the root, as
only 1 ignore file
can exist in a repository. We ensure this ignore file is used by passing --ignore-path above.
node_modules/
*.min.js
*.map
*.snap
A project-level ESLint config can be utilized by creating a .eslintrc.<json|js|cjs|yml> in the
project root. This is optional, but necessary when defining rules and ignore patterns unique to the
project.
module.exports = {
// Patterns to ignore (alongside the root .eslintignore)
ignorePatterns: ['build', 'lib'],
// Project specific rules
rules: {
'no-console': 'off',
},
};
The
extendssetting should not extend the root-level config, as ESLint will automatically merge configs while traversing upwards!
To share configuration across projects, you have 3 options:
eslint-config
or eslint-plugin npm package.
This can be used in any repository.For options 2 and 3, if you're utilizing package workspaces, create a local package with the following content.
module.exports = {
extends: ['airbnb'],
};
Within your root-level ESLint config, you can extend this package to inherit the settings.
module.exports = {
extends: 'eslint-config-company',
};
When using this approach, the package must be built and symlinked into
node_modulesbefore the linter will run correctly. Take this into account when going down this path!
Unfortunately, this isn't currently possible, as the eslint binary itself requires a file or
folder path to operate on, and in the task above we pass . (current directory). If this was not
passed, then nothing would be linted.
This has the unintended side-effect of not being able to filter down lintable targets by passing arbitrary file paths. This is something we hope to resolve in the future.
To work around this limitation, you can create another lint task.
overrides?Projects should define their own rules using an ESLint config in their project root. However, if you want to avoid touching many ESLint configs (think migrations), then overrides in the root are a viable option. Otherwise, we highly encourage project-level configs.
module.exports = {
// ...
overrides: [
// Only apply to apps "foo" and "bar", but not others
{
files: ['apps/foo/**/*', 'apps/bar/**/*'],
rules: {
'no-magic-numbers': 'off',
},
},
],
};