@planfredapp/eslint-config 1.0.59 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -70
- package/base.js +3 -2
- package/package.json +14 -12
- package/promise-param-names.js +65 -0
package/README.md
CHANGED
|
@@ -3,40 +3,98 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@planfredapp/eslint-config)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
|
|
6
|
-
Shared ESLint
|
|
6
|
+
Shared ESLint 10 flat configs used in Planfred projects.
|
|
7
|
+
|
|
8
|
+
The package ships its plugins and parsers as dependencies. In consuming projects, `eslint` is the only required peer dependency.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- Node `^20.19.0 || ^22.13.0 || >=24`
|
|
13
|
+
- `eslint >= 10`
|
|
14
|
+
- An ESM flat config file such as `eslint.config.js` or `eslint.config.mjs`
|
|
7
15
|
|
|
8
16
|
## Installation
|
|
9
17
|
|
|
10
18
|
```bash
|
|
11
|
-
npm install --save-dev @planfredapp/eslint-config
|
|
19
|
+
npm install --save-dev eslint @planfredapp/eslint-config
|
|
12
20
|
```
|
|
13
21
|
|
|
14
|
-
##
|
|
22
|
+
## Available Configs
|
|
23
|
+
|
|
24
|
+
| Config | Import Path | What It Adds |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| Base | `@planfredapp/eslint-config` or `@planfredapp/eslint-config/base` | `@eslint/js`, import rules, promise rules, Node rules, stylistic rules, browser and Node globals |
|
|
27
|
+
| Vue | `@planfredapp/eslint-config/vue` | Base config, `eslint-plugin-vue` recommended flat config, Vue-specific style and correctness rules |
|
|
28
|
+
| Cypress | `@planfredapp/eslint-config/cypress` | Base config, `typescript-eslint` recommended rules, Cypress globals, `.only()` protection |
|
|
29
|
+
| Test | `@planfredapp/eslint-config/test` | Base config, Vitest recommended rules, test globals, `.only()` protection |
|
|
30
|
+
|
|
31
|
+
### Vue-Specific Assumptions
|
|
32
|
+
|
|
33
|
+
The Vue config contains a few project-structure assumptions:
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| Vue | `@planfredapp/eslint-config/vue` | Vue 3 components, extends base |
|
|
20
|
-
| Cypress | `@planfredapp/eslint-config/cypress` | E2E tests with TypeScript |
|
|
21
|
-
| Test | `@planfredapp/eslint-config/test` | Vitest unit tests |
|
|
35
|
+
- `src/**/*` must use `@` imports instead of relative imports
|
|
36
|
+
- `src/components/**/*` enforces component/file-name matching
|
|
37
|
+
- The config is intended for Vue 3 single-file components
|
|
22
38
|
|
|
23
39
|
## Usage
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
Use the entrypoint that matches the project you are linting.
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
// Base config
|
|
29
|
-
import baseConfig from '@planfredapp/eslint-config/base'
|
|
30
|
-
export default [...baseConfig]
|
|
43
|
+
### Base
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
import
|
|
34
|
-
|
|
45
|
+
```js
|
|
46
|
+
import config from '@planfredapp/eslint-config'
|
|
47
|
+
|
|
48
|
+
export default [
|
|
49
|
+
...config,
|
|
50
|
+
]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Vue
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import config from '@planfredapp/eslint-config/vue'
|
|
57
|
+
|
|
58
|
+
export default [
|
|
59
|
+
...config,
|
|
60
|
+
]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Cypress
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import config from '@planfredapp/eslint-config/cypress'
|
|
67
|
+
|
|
68
|
+
export default [
|
|
69
|
+
...config,
|
|
70
|
+
]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Vitest
|
|
35
74
|
|
|
36
|
-
|
|
75
|
+
```js
|
|
76
|
+
import config from '@planfredapp/eslint-config/test'
|
|
77
|
+
|
|
78
|
+
export default [
|
|
79
|
+
...config,
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Customizing
|
|
84
|
+
|
|
85
|
+
Append your own config objects after the shared config:
|
|
86
|
+
|
|
87
|
+
```js
|
|
37
88
|
import vueConfig from '@planfredapp/eslint-config/vue'
|
|
89
|
+
|
|
38
90
|
export default [
|
|
39
91
|
...vueConfig,
|
|
92
|
+
{
|
|
93
|
+
ignores: [
|
|
94
|
+
'dist/**',
|
|
95
|
+
'coverage/**',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
40
98
|
{
|
|
41
99
|
rules: {
|
|
42
100
|
'no-console': 'warn',
|
|
@@ -45,68 +103,43 @@ export default [
|
|
|
45
103
|
]
|
|
46
104
|
```
|
|
47
105
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
### Base Config
|
|
51
|
-
- `@eslint/js` - ESLint recommended
|
|
52
|
-
- `@stylistic/eslint-plugin` - Code formatting
|
|
53
|
-
- `eslint-plugin-import` - Import/export validation
|
|
54
|
-
- `eslint-plugin-n` - Node.js rules
|
|
55
|
-
- `eslint-plugin-promise` - Promise handling
|
|
106
|
+
If you combine multiple specialized configs in one root `eslint.config.js`, scope the test-oriented ones to the files they should apply to:
|
|
56
107
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
108
|
+
```js
|
|
109
|
+
import baseConfig from '@planfredapp/eslint-config'
|
|
110
|
+
import cypressConfig from '@planfredapp/eslint-config/cypress'
|
|
111
|
+
import testConfig from '@planfredapp/eslint-config/test'
|
|
60
112
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
113
|
+
const scope = (configs, files) => configs.map(config => ({
|
|
114
|
+
...config,
|
|
115
|
+
files,
|
|
116
|
+
}))
|
|
64
117
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
118
|
+
export default [
|
|
119
|
+
...baseConfig,
|
|
120
|
+
...scope(cypressConfig, [ 'cypress/**/*.{js,ts}' ]),
|
|
121
|
+
...scope(testConfig, [ 'test/**/*.{js,ts}' ]),
|
|
122
|
+
]
|
|
123
|
+
```
|
|
68
124
|
|
|
69
|
-
##
|
|
125
|
+
## What The Base Config Enforces
|
|
70
126
|
|
|
71
|
-
### Stylistic
|
|
72
127
|
- 2-space indentation
|
|
73
|
-
- Single quotes
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
### Imports
|
|
80
|
-
- Alphabetized, grouped by type
|
|
81
|
-
- Newlines between groups
|
|
82
|
-
- No relative paths in Vue (use `@/`)
|
|
83
|
-
|
|
84
|
-
### Vue-Specific
|
|
85
|
-
- Template-first block order
|
|
86
|
-
- Component name matching filename
|
|
87
|
-
- No unused refs/props/emits
|
|
88
|
-
- PascalCase component names in templates
|
|
89
|
-
|
|
90
|
-
## Peer Dependencies
|
|
91
|
-
|
|
92
|
-
```json
|
|
93
|
-
{
|
|
94
|
-
"eslint": "9.0.0"
|
|
95
|
-
}
|
|
96
|
-
```
|
|
128
|
+
- Single quotes
|
|
129
|
+
- No semicolons
|
|
130
|
+
- Trailing commas for multiline arrays, objects, imports, exports, and function params
|
|
131
|
+
- Blank lines between most statements
|
|
132
|
+
- Import ordering with grouped and alphabetized imports
|
|
133
|
+
- `no-var`, `prefer-const`, and a broad set of core correctness rules
|
|
97
134
|
|
|
98
|
-
##
|
|
135
|
+
## Development
|
|
99
136
|
|
|
137
|
+
```bash
|
|
138
|
+
npm run lint
|
|
139
|
+
npm run test:run
|
|
100
140
|
```
|
|
101
|
-
base.js - Core configuration
|
|
102
|
-
vue.js - Vue.js (extends base)
|
|
103
|
-
cypress.js - Cypress E2E (extends base)
|
|
104
|
-
test.js - Vitest unit tests (extends base)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Changelog
|
|
108
141
|
|
|
109
|
-
|
|
142
|
+
More details are in [TESTING.md](TESTING.md). Release history is in [CHANGELOG.md](CHANGELOG.md).
|
|
110
143
|
|
|
111
144
|
## License
|
|
112
145
|
|
package/base.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** @type {import('eslint').Linter.Config[]} */
|
|
2
2
|
import js from '@eslint/js'
|
|
3
3
|
import stylisticPlugin from '@stylistic/eslint-plugin'
|
|
4
|
-
import importPlugin from 'eslint-plugin-import'
|
|
4
|
+
import importPlugin from 'eslint-plugin-import-x'
|
|
5
5
|
import nodePlugin from 'eslint-plugin-n'
|
|
6
|
-
import promisePlugin from 'eslint-plugin-promise'
|
|
7
6
|
import globals from 'globals'
|
|
8
7
|
|
|
8
|
+
import promisePlugin from './promise-param-names.js'
|
|
9
|
+
|
|
9
10
|
export default [
|
|
10
11
|
js.configs.recommended,
|
|
11
12
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planfredapp/eslint-config",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Shared ESLint configurations for Planfred monorepo",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Shared ESLint 10 configurations for Planfred monorepo",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
7
7
|
"eslint-config",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"files": [
|
|
34
34
|
"*.js"
|
|
35
35
|
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": "^20.19.0 || ^22.13.0 || >=24"
|
|
38
|
+
},
|
|
36
39
|
"scripts": {
|
|
37
40
|
"lint": "eslint .",
|
|
38
41
|
"lint:fix": "eslint . --fix",
|
|
@@ -45,27 +48,26 @@
|
|
|
45
48
|
"version:patch": "./scripts/version.sh patch"
|
|
46
49
|
},
|
|
47
50
|
"dependencies": {
|
|
48
|
-
"@eslint/js": "
|
|
51
|
+
"@eslint/js": "10.0.1",
|
|
49
52
|
"@stylistic/eslint-plugin": "5.10.0",
|
|
50
|
-
"@vitest/eslint-plugin": "1.6.
|
|
51
|
-
"eslint-plugin-cypress": "6.2.
|
|
52
|
-
"eslint-plugin-import": "
|
|
53
|
+
"@vitest/eslint-plugin": "1.6.13",
|
|
54
|
+
"eslint-plugin-cypress": "6.2.1",
|
|
55
|
+
"eslint-plugin-import-x": "4.16.2",
|
|
53
56
|
"eslint-plugin-n": "17.24.0",
|
|
54
57
|
"eslint-plugin-no-only-tests": "3.3.0",
|
|
55
58
|
"eslint-plugin-no-relative-import-paths": "1.6.1",
|
|
56
|
-
"eslint-plugin-promise": "7.2.1",
|
|
57
59
|
"eslint-plugin-vue": "10.8.0",
|
|
58
60
|
"globals": "17.4.0",
|
|
59
|
-
"typescript-eslint": "8.
|
|
61
|
+
"typescript-eslint": "8.57.2",
|
|
62
|
+
"vue-eslint-parser": "10.4.0"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
|
-
"eslint": "
|
|
65
|
+
"eslint": "10.1.0",
|
|
63
66
|
"typescript": "5.9.3",
|
|
64
|
-
"vitest": "4.1.
|
|
65
|
-
"vue-eslint-parser": "10.4.0"
|
|
67
|
+
"vitest": "4.1.1"
|
|
66
68
|
},
|
|
67
69
|
"peerDependencies": {
|
|
68
|
-
"eslint": ">=
|
|
70
|
+
"eslint": ">=10.0.0"
|
|
69
71
|
},
|
|
70
72
|
"overrides": {
|
|
71
73
|
"js-yaml": "4.1.1"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const PROMISE_EXECUTOR_NAMES = [
|
|
2
|
+
'resolve',
|
|
3
|
+
'reject',
|
|
4
|
+
]
|
|
5
|
+
|
|
6
|
+
const getIdentifier = (param) => {
|
|
7
|
+
if (param?.type === 'Identifier') {
|
|
8
|
+
return param
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (param?.type === 'AssignmentPattern' && param.left?.type === 'Identifier') {
|
|
12
|
+
return param.left
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
rules: {
|
|
20
|
+
'param-names': {
|
|
21
|
+
meta: {
|
|
22
|
+
type: 'suggestion',
|
|
23
|
+
docs: {
|
|
24
|
+
description: 'Enforce standard Promise executor parameter names',
|
|
25
|
+
},
|
|
26
|
+
schema: [],
|
|
27
|
+
messages: {
|
|
28
|
+
unexpectedName: 'Promise executor parameter {{index}} should be named "{{expected}}".',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
create (context) {
|
|
32
|
+
return {
|
|
33
|
+
'NewExpression[callee.type="Identifier"][callee.name="Promise"]' (node) {
|
|
34
|
+
const executor = node.arguments?.[0]
|
|
35
|
+
|
|
36
|
+
if (!executor || ![
|
|
37
|
+
'ArrowFunctionExpression',
|
|
38
|
+
'FunctionExpression',
|
|
39
|
+
].includes(executor.type)) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
PROMISE_EXECUTOR_NAMES.forEach((expected, index) => {
|
|
44
|
+
const param = executor.params[index]
|
|
45
|
+
const identifier = getIdentifier(param)
|
|
46
|
+
|
|
47
|
+
if (!param || identifier?.name === expected) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
context.report({
|
|
52
|
+
node: param,
|
|
53
|
+
messageId: 'unexpectedName',
|
|
54
|
+
data: {
|
|
55
|
+
index: index + 1,
|
|
56
|
+
expected,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}
|