Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,46 @@ Shared ESLint config used in Ionic and Capacitor projects.

This is meant to be used alongside Prettier (with [`@ionic/prettier-config`](https://github.com/ionic-team/prettier-config/)).

> **v0.5.0 requires ESLint 10** and a flat config file. Staying on ESLint 8 or 9? Keep using
> `@ionic/eslint-config@0.4.0`.

## Usage

1. Remove existing `.eslintrc.*` file, if present.
1. Install `eslint` and the config.
1. Install `eslint` and the config:

```
npm install -D eslint @ionic/eslint-config
```

1. Add the following to `package.json`:
2. Create an `eslint.config.cjs` in your project root:

```
"eslintConfig": {
"extends": "@ionic/eslint-config/recommended"
}
```js
const ionic = require('@ionic/eslint-config/recommended');

module.exports = [
{ ignores: ['dist/**', 'build/**'] },
...ionic,
];
```

From an ESM config (`eslint.config.mjs`), import `'@ionic/eslint-config/recommended.js'`.

:memo: You can also use the base rule set: `@ionic/eslint-config`

Both rule sets apply only to TypeScript files (`.ts`, `.tsx`, `.mts`, `.cts`). To lint
JavaScript too, add your own config block.

### With Prettier and `@ionic/prettier-config`

1. Set up Prettier and [`@ionic/prettier-config`](https://github.com/ionic-team/prettier-config/).
1. When using with Prettier and `@ionic/prettier-config`, ESLint should run first. Set up your scripts in `package.json` like this:
2. When using with Prettier and `@ionic/prettier-config`, ESLint should run first. Set up your scripts in `package.json` like this:

```json
"scripts": {
"lint": "npm run eslint && npm run prettier -- --check",
"fmt": "npm run eslint -- --fix && npm run prettier -- --write",
"prettier": "prettier \"**/*.ts\"",
"eslint": "eslint . --ext .ts",
"eslint": "eslint"
}
```

Expand All @@ -42,6 +52,22 @@ This is meant to be used alongside Prettier (with [`@ionic/prettier-config`](htt

:memo: Not every rule in this configuration is autofixable, so `npm run fmt` may continue failing until lint issues are addressed manually.

## Migrating from 0.4 or earlier

1. Delete the `eslintConfig` block from `package.json` (or any `.eslintrc` file).
2. Add an `eslint.config.cjs` as shown above.
3. Move `.eslintignore` entries into `ignores`, then delete the file.
4. Drop `--ext ts` from lint scripts; flat config ignores the flag.

New reports to expect:

- `no-unused-vars` now flags unused `catch (e)` bindings. Use `catch {}` instead.
- `no-var-requires` was renamed to `no-require-imports`. Update any disable comments.
- `import/*` rules are now [`import-x/*`](https://github.com/un-ts/eslint-plugin-import-x). Update any disable comments.
- `prefer-optional-chain` is no longer in `recommended`; it now requires
[typed linting](https://typescript-eslint.io/getting-started/typed-linting/). Re-enable it
in your own config if you use typed linting.

### With Husky

1. Install [husky](https://github.com/typicode/husky):
Expand All @@ -50,7 +76,7 @@ This is meant to be used alongside Prettier (with [`@ionic/prettier-config`](htt
npm install -D husky
```

1. Add the following to `package.json`:
2. Add the following to `package.json`:

```
"husky": {
Expand Down
36 changes: 23 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
'import'
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:import/typescript',
],
const js = require('@eslint/js');
const ts = require('@typescript-eslint/eslint-plugin');
const prettier = require('eslint-config-prettier/flat');
const importX = require('eslint-plugin-import-x');

const overrides = {
name: '@ionic/eslint-config/overrides',
rules: {
// https://eslint.org/docs/rules/
'no-fallthrough': 'off', // https://github.com/ionic-team/eslint-config/issues/7
Expand All @@ -19,5 +14,20 @@ module.exports = {
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': ['warn', { 'allowArgumentsExplicitlyTypedAsAny': true }],
// interface signatures often force unused parameters; an underscore marks them intentional
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_', 'varsIgnorePattern': '^_' }],
},
};
};

// Flat config ignores `--ext`, so scope everything to TypeScript. Keep in sync with recommended.js.
const TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];

module.exports = [
js.configs.recommended,
...ts.configs['flat/recommended'],
// import-x's TypeScript preset needs a resolver package whose peers cap ESLint at v9,
// so use the resolver import-x bundles instead.
{ name: '@ionic/eslint-config/import-x', plugins: { 'import-x': importX }, settings: { 'import-x/resolver-next': [importX.createNodeResolver()] } },
prettier,
overrides,
].map((config) => (config.files ? config : { ...config, files: TS_FILES }));
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/eslint-config",
"version": "0.4.0",
"version": "0.5.0",
"description": "Common eslint rules/preferences for Ionic.",
"main": "index.js",
"repository": {
Expand All @@ -21,12 +21,13 @@
},
"homepage": "https://github.com/ionic-team/eslint-config#readme",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.0"
"@eslint/js": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.56.0",
"@typescript-eslint/parser": "^8.56.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import-x": "^4.17.1"
},
"peerDependencies": {
"eslint": ">=7"
"eslint": "^10.0.0"
}
}
29 changes: 14 additions & 15 deletions recommended.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
module.exports = {
extends: [
'./index',
'plugin:import/typescript',
],
plugins: [
'import',
],
const base = require('./index');

const overrides = {
name: '@ionic/eslint-config/recommended',
// Keep in sync with index.js.
files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
rules: {
// ./index.js
'@typescript-eslint/explicit-module-boundary-types': [
Expand All @@ -17,20 +15,21 @@ module.exports = {
'@typescript-eslint/consistent-type-assertions': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/prefer-optional-chain': 'error',

// https://github.com/benmosher/eslint-plugin-import
'import/first': 'error',
'import/order': [
// https://github.com/un-ts/eslint-plugin-import-x
'import-x/first': 'error',
'import-x/order': [
'error',
{
'alphabetize': { order: 'asc', caseInsensitive: false },
'groups': [['builtin', 'external'], 'parent', ['sibling', 'index']],
'newlines-between': 'always',
},
],
'import/newline-after-import': 'error',
'import/no-duplicates': 'error',
'import/no-mutable-exports': 'error',
'import-x/newline-after-import': 'error',
'import-x/no-duplicates': 'error',
'import-x/no-mutable-exports': 'error',
},
};

module.exports = [...base, overrides];