-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy patheslint.config.mjs
More file actions
236 lines (229 loc) Β· 10.7 KB
/
Copy patheslint.config.mjs
File metadata and controls
236 lines (229 loc) Β· 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2026 Conduction B.V.
//
// eslint 10 + @nextcloud/eslint-config 9 β the same stack Nextcloud's own apps
// run (nextcloud/forms is the reference). Flat config, ESM.
//
// This file is the fleet's canonical shape. Copy it verbatim into an app; the
// only parts that should ever differ are the last two blocks (app-specific
// globals and file-scoped exemptions).
//
// WHY `.mjs` AND NOT `"type": "module"` IN package.json
// -----------------------------------------------------
// `@nextcloud/eslint-config@9` is `"type": "module"`, so the config importing it
// must be ESM. forms achieves that by making the whole package ESM; these apps
// cannot β `webpack.config.js`, `vitest.config.js` and the `tests/**` CLI
// checkers are CommonJS and would stop parsing. Naming the config `.mjs` scopes
// the module system to the one file that needs it.
//
// π΄ NODE 22 IS REQUIRED, NOT PREFERRED
// -------------------------------------
// `@nextcloud/eslint-config@9` declares `engines.node: ^22.14 || ^24 || >=26`
// and imports `findPackageJSON` from `node:module`, an API that first exists in
// 22.14. On Node 20 eslint dies before linting a single file with
// `SyntaxError: β¦ does not provide an export named 'findPackageJSON'`, and npm
// reports the mismatch only as an EBADENGINE warning it continues past.
//
// π΄ THE PEER DEPENDENCIES ARE LOAD-BEARING
// -----------------------------------------
// `vue-eslint-parser` is NOT a dependency of `@nextcloud/eslint-config` β it is
// a peer of the `eslint-plugin-vue@10` it bundles, so the APP must supply it,
// at `^10`. If a stale `eslint-plugin-vue@^9` / `vue-eslint-parser@^9` is left
// in devDependencies it hoists over the bundled copy, `vue/base/setup-for-vue`
// then supplies NO parser, and `typescript-eslint/base` β which also claims
// `**/*.vue` β parses every SFC as TypeScript. Every `.vue` file fails with
// `Parsing error: Expression expected`, and because eslint reports a parse
// failure as ONE finding and lints nothing else in that file, the whole Vue
// layer goes unchecked while the problem count looks small.
// `@typescript-eslint/parser` must likewise be resolvable from the top level:
// `vue-eslint-parser` requires it by name for `<script>` blocks.
//
// π΄ AND package.json CARRIES AN `overrides` ENTRY FOR THIS (no comments are
// possible there, so it is explained here):
//
// "overrides": { "@conduction/nextcloud-vue": { "eslint": "$eslint" } }
//
// `@conduction/nextcloud-vue` declares `eslint: "^8.56.0 || ^9.0.0"` as a peer.
// It is marked OPTIONAL, which means npm will not INSTALL it β but if eslint is
// present anyway, the version must still satisfy that range, so a fresh
// `npm install` fails with ERESOLVE against eslint 10. The peer exists only
// because nc-vue ships an eslint preset (`@conduction/nextcloud-vue/eslint`),
// which this config no longer imports, so the constraint is inert for us.
// `$eslint` pins it to whatever the root declares. Drop the override once
// nc-vue widens the range to include ^10 (ConductionNL/nextcloud-vue).
//
// β οΈ An EXISTING lockfile hides this: `npm ci` does not re-resolve peers, so an
// app can look fine until someone adds a dependency.
//
// WHAT THIS FILE NO LONGER NEEDS
// ------------------------------
// `conductionVue3Fixes` and the `FlatCompat` bridge to the eslintrc-era
// `@nextcloud` config are GONE. That preset existed to patch a Vue-2 ruleset.
// Measured against v9's `recommended` with `--print-config`, every one of its
// jobs is already done: 21 of 21 `vue/no-deprecated-*` rules are enabled, and
// `vue/no-v-model-argument` / `vue/no-v-for-template-key` β the two rules the
// preset had to switch off because they are inverted under Vue 3 β are not
// enabled at all, so there is nothing left to disable.
import { recommended } from '@nextcloud/eslint-config'
import eslintConfigPrettier from 'eslint-config-prettier'
export default [
...recommended,
{
// π΄ SCOPED, and it must be. Flat config resolves a rule's plugin from the
// config object the rule sits in, and v9 also lints `.json` (it ships
// `@eslint/json`), where `jsdoc` is not registered. An unscoped `jsdoc/*`
// override aborts the entire run with "The jsdoc plugin is not defined in
// your configuration file" β a hard config error, not a lint finding.
//
// π΄ THE `ignores` ARE PART OF THAT SCOPE, NOT AN OPINION. v9 registers the
// jsdoc plugin ONLY inside `nextcloud/documentation/*`, and every one of
// those blocks carries exactly this ignore list β Nextcloud does not
// require JSDoc in tests. Referencing a `jsdoc/*` rule for a test file
// therefore names a plugin that is not registered there, and eslint
// refuses to run at all. Measured: with `lint: "eslint src tests"` this
// took out ALL 12 files under tests/ while src/ was fine.
files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.tsx', '**/*.vue'],
ignores: [
'**/*.test.*',
'**/*.spec.*',
'**/*.cy.*',
'**/test/**',
'**/tests/**',
'**/__tests__/**',
'**/__mocks__/**',
],
rules: {
// `@spec` (hydra gate-16 / gate-19 traceability) and `@visual` (the
// visual-coverage gate) are this fleet's own JSDoc tags. v9 configures
// `jsdoc/check-tag-names` with no `definedTags`, so without this every
// annotation reports as an invalid tag name.
//
// It must be passed as RULE OPTIONS: once a preset has configured the
// rule, it reads `definedTags` from its own options object and
// `settings.jsdoc.definedTags` is ignored.
'jsdoc/check-tag-names': ['error', { definedTags: ['spec', 'visual'] }],
},
},
{
// `t` and `n` are imported for translation wiring that is not always called
// yet. For `.ts`/`.vue` v9 turns the CORE `no-unused-vars` off and drives
// `@typescript-eslint/no-unused-vars` instead, so the pattern belongs on
// the TS rule.
//
// π΄ SCOPED for the same reason as the block above: in an unscoped object
// this applies to plain `.js` too, where v9 has NOT registered
// `@typescript-eslint`, and eslint then refuses to run at all with
// "could not find plugin @typescript-eslint".
//
// β οΈ The swap is per-file-type, not global: `--print-config` on a `.js`
// file reports `@typescript-eslint/no-unused-vars: undefined` and core
// `no-unused-vars: [2, β¦]`. Plain `.js` therefore gets no ignore pattern
// from here, which is deliberate β widening it would hide dead bindings.
files: ['**/*.ts', '**/*.tsx', '**/*.vue'],
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
varsIgnorePattern: '^(t|n)$',
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
},
},
{
// Node-side CLI checkers under tests/ legitimately use console and
// process.exit, and ship as plain JS with no shebang. A GLOB, not a file
// list: an explicit list silently stopped covering every new checker.
files: ['tests/**/*.js', 'tests/**/*.mjs', 'tests/**/*.ts'],
rules: {
'no-console': 'off',
'n/no-process-exit': 'off',
'n/hashbang': 'off',
// `_` / `__` as a deliberate throwaway binding β `catch (_)`, a
// discarded destructuring slot. Narrow on purpose: the pattern matches
// UNDERSCORES ONLY, so a real name that happens to start with `_` is
// still reported. v9 drives plain `.js` through the CORE rule (the
// `@typescript-eslint` swap is per-file-type), so it is set here.
'no-unused-vars': [
'error',
{
// vars/caught: UNDERSCORES ONLY, so a real name that merely
// starts with `_` is still reported.
varsIgnorePattern: '^_+$',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_+$',
// args: leading underscore, which is what NC's own TypeScript
// block uses (`argsIgnorePattern: '^_'`) β a positional
// parameter often has to keep a descriptive name to document
// the signature even when the body ignores it.
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
// Tests import devDependencies by definition; this rule is about what
// ships in the published package, which tests/ never does.
'n/no-unpublished-import': 'off',
},
},
{
// Test globals. Several apps keep their spec files INSIDE `src/`, which the
// lint script scans, and neither `@nextcloud/eslint-config` nor the runner
// declares the framework globals. Without this, `no-undef` reports every
// `describe` / `it` / `expect` as undefined: 1203 findings in openregister
// alone, all from 7 identifiers.
//
// This is describing the environment, not relaxing a rule β the same reason
// a webpack `require.context` file declares `require`. It matters that they
// are declared rather than suppressed: 1203 fake findings would bury any
// REAL `no-undef` in the same app, and `no-undef` is the rule that catches
// a genuine typo'd identifier.
files: [
'**/*.{test,spec}.{js,mjs,ts,tsx,vue}',
'**/{test,tests,__tests__,__mocks__}/**/*.{js,mjs,ts,tsx,vue}',
],
languageOptions: {
globals: {
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
vi: 'readonly',
jest: 'readonly',
suite: 'readonly',
},
},
},
// eslint-config-prettier LAST OF THE PRESETS, and it has to be: it only turns
// rules OFF, and what it turns off is everything prettier owns β including the
// `@stylistic/*` family v9 introduces (`indent`, `quotes`, `semi`).
//
// Those three AGREE with @nextcloud/prettier-config (tab / single / never),
// which is why Nextcloud ships both packages. Agreement is not the point: two
// tools formatting the same bytes is the unfixable state this fleet already
// hit with php-cs-fixer and PHPCS, so exactly one of them is allowed an
// opinion and prettier is it. `prettier --check` runs as its own CI job.
//
// NOTE: forms additionally uses `eslint-plugin-prettier/recommended`, which
// reports prettier violations AS eslint errors. Deliberately not adopted β
// this fleet already runs `prettier --check` separately, and doing both means
// one defect reported twice in two places.
eslintConfigPrettier,
{
// AFTER eslint-config-prettier, because prettier's config does NOT cover
// this rule and it has to stay off. `@stylistic/exp-list-style` rewrites a
// wrapped expression list to put a trailing comma before the closing paren
// (`β¦ : v,)`), which prettier immediately reformats back β the two tools
// fight over the same bytes forever. `nextcloud/forms` disables exactly
// this rule in its own flat config, so switching it off is matching
// Nextcloud's resolution rather than diverging from it.
name: 'conduction/prettier-jurisdiction',
rules: {
'@stylistic/exp-list-style': 'off',
},
},
]