@planfredapp/eslint-config 1.0.60 → 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 CHANGED
@@ -3,40 +3,98 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@planfredapp/eslint-config.svg)](https://www.npmjs.com/package/@planfredapp/eslint-config)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
5
 
6
- Shared ESLint configurations for the Planfred monorepo. Uses ESLint 9 flat config format.
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 eslint
19
+ npm install --save-dev eslint @planfredapp/eslint-config
12
20
  ```
13
21
 
14
- ## Configurations
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
- | Config | Import Path | Description |
17
- |--------|-------------|-------------|
18
- | Base | `@planfredapp/eslint-config/base` | Core JS rules, imports, Node.js, stylistic |
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
- Create `eslint.config.js` in your project root:
41
+ Use the entrypoint that matches the project you are linting.
26
42
 
27
- ```javascript
28
- // Base config
29
- import baseConfig from '@planfredapp/eslint-config/base'
30
- export default [...baseConfig]
43
+ ### Base
31
44
 
32
- // Vue project
33
- import vueConfig from '@planfredapp/eslint-config/vue'
34
- export default [...vueConfig]
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
- // With overrides
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
- ## Included Plugins
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
- ### Vue Config
58
- - `eslint-plugin-vue` - Vue 3 rules
59
- - `eslint-plugin-no-relative-import-paths` - Enforce `@/` imports
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
- ### Cypress Config
62
- - `eslint-plugin-cypress` - Cypress globals and best practices
63
- - `typescript-eslint` - TypeScript support
113
+ const scope = (configs, files) => configs.map(config => ({
114
+ ...config,
115
+ files,
116
+ }))
64
117
 
65
- ### Test Config
66
- - `@vitest/eslint-plugin` - Vitest globals and rules
67
- - `eslint-plugin-no-only-tests` - Prevent `.only()` in CI
118
+ export default [
119
+ ...baseConfig,
120
+ ...scope(cypressConfig, [ 'cypress/**/*.{js,ts}' ]),
121
+ ...scope(testConfig, [ 'test/**/*.{js,ts}' ]),
122
+ ]
123
+ ```
68
124
 
69
- ## Key Rules
125
+ ## What The Base Config Enforces
70
126
 
71
- ### Stylistic
72
127
  - 2-space indentation
73
- - Single quotes, no semicolons
74
- - Trailing commas in multiline
75
- - Spaces inside brackets: `[ 'a', 'b' ]`
76
- - Newlines for arrays/objects with 2+ items
77
- - Blank lines between statements
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
- ## Files
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
- See [CHANGELOG.md](CHANGELOG.md) for version history.
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.60",
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": "9.39.2",
51
+ "@eslint/js": "10.0.1",
49
52
  "@stylistic/eslint-plugin": "5.10.0",
50
- "@vitest/eslint-plugin": "1.6.12",
51
- "eslint-plugin-cypress": "6.2.0",
52
- "eslint-plugin-import": "2.32.0",
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.57.1"
61
+ "typescript-eslint": "8.57.2",
62
+ "vue-eslint-parser": "10.4.0"
60
63
  },
61
64
  "devDependencies": {
62
- "eslint": "9.39.2",
65
+ "eslint": "10.1.0",
63
66
  "typescript": "5.9.3",
64
- "vitest": "4.1.0",
65
- "vue-eslint-parser": "10.4.0"
67
+ "vitest": "4.1.1"
66
68
  },
67
69
  "peerDependencies": {
68
- "eslint": ">=9.0.0"
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
+ }