@planfredapp/eslint-config 1.2.12 → 1.3.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
@@ -1,5 +1,7 @@
1
1
  # @planfredapp/eslint-config
2
2
 
3
+ [View package on npm](https://www.npmjs.com/package/@planfredapp/eslint-config)
4
+
3
5
  [![npm version](https://img.shields.io/npm/v/@planfredapp/eslint-config.svg)](https://www.npmjs.com/package/@planfredapp/eslint-config)
4
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
7
 
@@ -9,7 +11,7 @@ The package ships its plugins and parsers as dependencies. In consuming projects
9
11
 
10
12
  ## Requirements
11
13
 
12
- - Node `^20.19.0 || ^22.13.0 || >=24`
14
+ - Node `>=26`
13
15
  - `eslint >= 10`
14
16
  - An ESM flat config file such as `eslint.config.js` or `eslint.config.mjs`
15
17
 
@@ -30,12 +32,16 @@ npm install --save-dev eslint @planfredapp/eslint-config
30
32
  | Cypress | `@planfredapp/eslint-config/cypress` | Base config, `typescript-eslint` recommended rules, Cypress globals, `.only()` protection |
31
33
  | Test | `@planfredapp/eslint-config/test` | Base config, Vitest recommended rules, test globals, `.only()` protection |
32
34
 
35
+ Every specialized entrypoint already includes the complete base config and adds its own rules on top without loosening base's options. Cypress swaps the core unused-code rules for typescript-eslint's versions with the same options; in `.ts` files, typescript-eslint also switches off the core rules that the TypeScript compiler covers, such as `no-undef`, so those checks depend on running `tsc` over the files (unreachable code is only a compiler error with `allowUnreachableCode: false`). Use the base entrypoint by itself for plain JavaScript, or select a specialized entrypoint when it applies to the whole project.
36
+
37
+ All entrypoints lint `.js`, `.mjs`, and `.cjs` files (`.cjs` is parsed as CommonJS). The Vue entrypoint adds `.vue` files. Only the Cypress entrypoint parses TypeScript; it uses the TypeScript parser for every file, so it doesn't reject TypeScript syntax in `.js` files or ESM syntax in `.cjs` files.
38
+
33
39
  ### Vue-Specific Assumptions
34
40
 
35
41
  The Vue config contains a few project-structure assumptions:
36
42
 
37
43
  - `src/**/*` must use `@` imports instead of relative imports
38
- - `src/components/**/*` enforces component/file-name matching
44
+ - `src/components/**/*` enforces component/file-name matching and a `name` property on Options API components
39
45
  - The config is intended for Vue 3 single-file components
40
46
 
41
47
  ## Usage
@@ -105,43 +111,75 @@ export default [
105
111
  ]
106
112
  ```
107
113
 
108
- If you combine multiple specialized configs in one root `eslint.config.js`, scope the test-oriented ones to the files they should apply to:
114
+ If one root `eslint.config.js` combines multiple specialized configs, apply the base config once and scope only each specialized config's additional entries with `defineConfig`'s `extends`:
109
115
 
110
116
  ```js
111
117
  import baseConfig from '@planfredapp/eslint-config'
112
118
  import cypressConfig from '@planfredapp/eslint-config/cypress'
113
119
  import testConfig from '@planfredapp/eslint-config/test'
120
+ import vueConfig from '@planfredapp/eslint-config/vue'
121
+ import { defineConfig } from 'eslint/config'
114
122
 
115
- const scope = (configs, files) => configs.map(config => ({
116
- ...config,
117
- files,
118
- }))
123
+ const withoutBase = config => config.slice(baseConfig.length)
119
124
 
120
- export default [
121
- ...baseConfig,
122
- ...scope(cypressConfig, [ 'cypress/**/*.{js,ts}' ]),
123
- ...scope(testConfig, [ 'test/**/*.{js,ts}' ]),
124
- ]
125
+ export default defineConfig([
126
+ baseConfig,
127
+ {
128
+ files: [ 'src/**/*.{js,vue}' ],
129
+ extends: [ withoutBase(vueConfig) ],
130
+ },
131
+ {
132
+ files: [ 'cypress/**/*.{js,ts}' ],
133
+ extends: [ withoutBase(cypressConfig) ],
134
+ },
135
+ {
136
+ files: [ 'test/**/*.js' ],
137
+ extends: [ withoutBase(testConfig) ],
138
+ },
139
+ ])
125
140
  ```
126
141
 
142
+ This avoids applying the base entries more than once. The specialized entrypoints intentionally begin with the same base config entries. Widen the `files` patterns if those folders also hold `.mjs`, `.cjs`, or other file types.
143
+
144
+ Vue's path-scoped entries (`src/**` and `src/components/**`) are relative to the config's base path. For a Vue app in a subfolder, set `basePath` instead of prefixing `files`, or those entries silently match nothing: `{ basePath: 'apps/web', files: [ 'src/**/*.{js,vue}' ], extends: [ withoutBase(vueConfig) ] }`.
145
+
146
+ Don't scope entries by overwriting their `files` (`config.map(entry => ({ ...entry, files }))`). Some entries carry their own `files`, and overwriting them widens their scope: typescript-eslint's `.ts`-only overrides would switch off `no-undef` for Cypress `.js` files, and Vue's component-only rules would apply to every file. `extends` intersects the patterns instead.
147
+
127
148
  ## What The Base Config Enforces
128
149
 
129
- - 2-space indentation
130
- - Single quotes
131
- - No semicolons
132
- - Trailing commas for multiline arrays, objects, imports, exports, and function params
133
- - Blank lines between most statements
134
- - Import ordering with grouped and alphabetized imports
135
- - `no-var`, `prefer-const`, and a broad set of core correctness rules
150
+ Each item below is covered by a valid and an invalid example in [`test/configs.test.js`](test/configs.test.js).
151
+
152
+ - 2-space indentation, single quotes, no semicolons
153
+ - Trailing commas for multiline arrays, objects, imports, exports, function params, and call arguments; commas at the end of the line, never on a line of their own
154
+ - Spaces inside array brackets (`[ 1 ]`); arrays with 2+ items put each item on its own line
155
+ - Object literals with 2+ properties break after `{` and before `}`; inside, either all properties share one line or each gets its own line. Object destructuring may stay on one line; array destructuring follows the array rules
156
+ - A space before function parentheses (`function f ()`, `method ()`)
157
+ - A blank line between statements, including before `return`. Consecutive imports, consecutive `const`/`let`/`var` declarations, and consecutive expression statements may be adjacent; exported and function declarations are always separated
158
+ - Chains of 2+ calls put each call on its own line (`list.map(f).join()` → one call per line); a single call stays inline
159
+ - Imports in groups separated by a blank line: Node builtins and packages together, then relative paths, each group alphabetized. Without an import resolver, aliases and subpath imports (`@/`, `#`, `~/`) form a last group after relative paths; an `import-x/internal-regex` setting that marks them internal moves them between packages and relative paths
160
+ - `no-var`, `prefer-const`, `eqeqeq` (except `== null`), and a broad set of core correctness rules
161
+ - Unused function arguments, unused caught errors, and rest siblings used to omit properties are allowed; unused variables are not. An unused first parameter named `err` or `error` is reported, because `n/handle-callback-err` requires handling it
162
+ - Promise executor parameters named `resolve` and `reject`
163
+
164
+ ### What The Vue Config Adds
165
+
166
+ - `@/` imports inside `src/` through the bundled `no-relative-import-paths` rule, with an autofix for relative imports
167
+ - Template expressions follow their own style: no spaces inside array brackets, arrays may stay on one line (`:items="[1, 2]"`), and multi-property objects put each property on its own line
168
+ - A blank line between sibling template elements
169
+ - Blocks ordered `<template>`, `<script>`, `<style>`, blank lines around multi-line component options, no `v-html`
170
+
171
+ ## Package Contents
172
+
173
+ The npm package contains the four public config entrypoints plus the two internal custom-rule modules they use. Repository-only ESLint and Vitest config files are excluded from the published tarball.
136
174
 
137
175
  ## Development
138
176
 
139
177
  ```bash
140
- npm run lint
141
- npm run test:run
178
+ pnpm lint
179
+ pnpm vitest run test/<relevant-spec>.test.js
142
180
  ```
143
181
 
144
- More details are in [TESTING.md](TESTING.md). Release history is in [CHANGELOG.md](CHANGELOG.md).
182
+ Run the complete test suite through GitHub Actions. More details are in [TESTING.md](TESTING.md). Release history is in [CHANGELOG.md](CHANGELOG.md).
145
183
 
146
184
  ## License
147
185
 
package/base.js CHANGED
@@ -17,7 +17,6 @@ export default [
17
17
  stylistic: stylisticPlugin,
18
18
  },
19
19
  languageOptions: {
20
- sourceType: 'module',
21
20
  globals: {
22
21
  ...globals.node,
23
22
  ...globals.browser,
@@ -55,10 +54,6 @@ export default [
55
54
  ignoreGlobals: true,
56
55
  },
57
56
  ],
58
- 'comma-style': [
59
- 'error',
60
- 'last',
61
- ],
62
57
  'constructor-super': 'error',
63
58
  curly: [
64
59
  'error',
@@ -136,10 +131,10 @@ export default [
136
131
  'no-useless-catch': 'error',
137
132
  'no-new': 'error',
138
133
  'no-new-func': 'error',
139
- 'no-new-object': 'error',
140
- 'no-new-symbol': 'error',
134
+ 'no-new-native-nonconstructor': 'error',
141
135
  'no-new-wrappers': 'error',
142
136
  'no-obj-calls': 'error',
137
+ 'no-object-constructor': 'error',
143
138
  'no-octal': 'error',
144
139
  'no-octal-escape': 'error',
145
140
  'no-proto': 'error',
@@ -352,6 +347,10 @@ export default [
352
347
  after: true,
353
348
  },
354
349
  ],
350
+ 'stylistic/comma-style': [
351
+ 'error',
352
+ 'last',
353
+ ],
355
354
  'stylistic/computed-property-spacing': [
356
355
  'error',
357
356
  'never',
package/cypress.js CHANGED
@@ -6,9 +6,17 @@ import tsEslint from 'typescript-eslint'
6
6
 
7
7
  import baseConfig from './base.js'
8
8
 
9
+ // typescript-eslint replaces these core rules with its own versions, so carry over the base options
10
+ const baseRule = name => baseConfig.findLast(config => !config.files && config.rules?.[name]).rules[name]
11
+
9
12
  export default [
10
13
  ...baseConfig,
11
14
  ...tsEslint.configs.recommended,
15
+ {
16
+ // typescript-eslint sets sourceType: 'module' for every file; restore ESLint's default for .cjs
17
+ files: [ '**/*.cjs' ],
18
+ languageOptions: { sourceType: 'commonjs' },
19
+ },
12
20
  cypressPlugin.configs.recommended,
13
21
  {
14
22
  plugins: {
@@ -23,6 +31,8 @@ export default [
23
31
  },
24
32
  },
25
33
  rules: {
34
+ '@typescript-eslint/no-unused-expressions': baseRule('no-unused-expressions'),
35
+ '@typescript-eslint/no-unused-vars': baseRule('no-unused-vars'),
26
36
  '@typescript-eslint/no-require-imports': 'off',
27
37
  'noOnlyTestsPlugin/no-only-tests': 'error',
28
38
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@planfredapp/eslint-config",
3
- "version": "1.2.12",
4
- "description": "Shared ESLint 10 configurations for Planfred monorepo",
3
+ "version": "1.3.0",
4
+ "description": "Shared ESLint 10 flat configurations for Planfred projects",
5
5
  "keywords": [
6
6
  "eslint",
7
7
  "eslint-config",
@@ -29,9 +29,14 @@
29
29
  "./cypress": "./cypress.js",
30
30
  "./test": "./test.js"
31
31
  },
32
- "main": "index.js",
32
+ "main": "./base.js",
33
33
  "files": [
34
- "*.js"
34
+ "base.js",
35
+ "cypress.js",
36
+ "no-relative-import-paths.js",
37
+ "promise-param-names.js",
38
+ "test.js",
39
+ "vue.js"
35
40
  ],
36
41
  "engines": {
37
42
  "node": ">=26"
@@ -39,20 +44,20 @@
39
44
  "dependencies": {
40
45
  "@eslint/js": "10.0.1",
41
46
  "@stylistic/eslint-plugin": "5.10.0",
42
- "@vitest/eslint-plugin": "1.6.17",
43
- "eslint-plugin-cypress": "6.4.1",
44
- "eslint-plugin-import-x": "4.16.2",
45
- "eslint-plugin-n": "18.0.1",
47
+ "@vitest/eslint-plugin": "1.6.28",
48
+ "eslint-plugin-cypress": "7.0.2",
49
+ "eslint-plugin-import-x": "4.17.1",
50
+ "eslint-plugin-n": "18.5.0",
46
51
  "eslint-plugin-no-only-tests": "3.4.0",
47
- "eslint-plugin-vue": "10.9.1",
48
- "globals": "17.6.0",
49
- "typescript-eslint": "8.59.3",
50
- "vue-eslint-parser": "10.4.0"
52
+ "eslint-plugin-vue": "10.11.1",
53
+ "globals": "17.13.0",
54
+ "typescript-eslint": "8.72.0",
55
+ "vue-eslint-parser": "10.4.1"
51
56
  },
52
57
  "devDependencies": {
53
- "eslint": "10.4.0",
58
+ "eslint": "10.13.0",
54
59
  "typescript": "6.0.3",
55
- "vitest": "4.1.6"
60
+ "vitest": "5.0.5"
56
61
  },
57
62
  "peerDependencies": {
58
63
  "eslint": ">=10.0.0"
package/vue.js CHANGED
@@ -7,6 +7,11 @@ import noRelativeImportPathsPlugin from './no-relative-import-paths.js'
7
7
  export default [
8
8
  ...baseConfig,
9
9
  ...vuePlugin.configs['flat/recommended'],
10
+ {
11
+ // eslint-plugin-vue sets sourceType: 'module' for every file; restore ESLint's default for .cjs
12
+ files: [ '**/*.cjs' ],
13
+ languageOptions: { sourceType: 'commonjs' },
14
+ },
10
15
  {
11
16
  plugins: {
12
17
  vue: vuePlugin,
package/eslint.config.js DELETED
@@ -1,12 +0,0 @@
1
- import baseConfig from './base.js'
2
-
3
- export default [
4
- ...baseConfig,
5
- {
6
- files: [ '**/*.js' ],
7
- ignores: [
8
- 'node_modules/**',
9
- 'dist/**',
10
- ],
11
- },
12
- ]
package/vitest.config.js DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- },
8
- })