@nextcloud/eslint-config 9.0.0 → 9.0.1

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/CHANGELOG.md CHANGED
@@ -4,8 +4,15 @@
4
4
  -->
5
5
  # Changelog
6
6
 
7
- ## [v9.0.0](https://github.com/nextcloud-libraries/eslint-config/tree/v9.0.0) (2026-06-26)
7
+ ## [v9.0.1](https://github.com/nextcloud-libraries/eslint-config/tree/v9.0.1) (2026-07-07)
8
+ ### Fixed
9
+ * fix(vue): error on `vue/attributes-order` and `vue/order-in-components` instead of warn [\#1445](https://github.com/nextcloud-libraries/eslint-config/pull/1445) \([susnux](https://github.com/susnux)\)
10
+ * fix: `no-deprecated-library-*` rules incorrectly behave for `nextcloud/vue` syntax [\#1452](https://github.com/nextcloud-libraries/eslint-config/pull/1452) \([Antreesy](https://github.com/Antreesy)\)
11
+ * fix(no-deprecated-library-props): support win32 filesystem and different `@nextcloud/vue` directory structures [\#1454](https://github.com/nextcloud-libraries/eslint-config/pull/1454) \([ShGKme](https://github.com/ShGKme)\)
12
+ * fix(no-deprecated-library-props): support camelCase attributes [\#1453](https://github.com/nextcloud-libraries/eslint-config/pull/1453) \([Antreesy](https://github.com/Antreesy)\)
13
+ * fix: increase min. Node version to 22.14 [\#1455](https://github.com/nextcloud-libraries/eslint-config/pull/1455) \([susnux](https://github.com/susnux)\)
8
14
 
15
+ ## [v9.0.0](https://github.com/nextcloud-libraries/eslint-config/tree/v9.0.0) (2026-06-26)
9
16
  ### Breaking
10
17
  This package now is using ESLint v10 and requires ESLint flat configurations.
11
18
  Please refer to the README on how to adjust your configuration for flat config.
@@ -123,7 +123,11 @@ export function vue(options) {
123
123
  'vue/padding-line-between-blocks': 'error',
124
124
  // Prefer separated static and dynamic class attributes
125
125
  'vue/prefer-separate-static-class': 'error',
126
+ // For consistent layout of the template
127
+ 'vue/attributes-order': 'error',
126
128
  // For consistent layout of components
129
+ 'vue/order-in-components': 'error',
130
+ // For consistent layout of script-setup components
127
131
  'vue/define-macros-order': [
128
132
  'error',
129
133
  {
@@ -26,7 +26,11 @@ const rule = {
26
26
  const versionSatisfies = createLibVersionValidator(context);
27
27
  const isVersionValidForDist = versionSatisfies('8.23.0');
28
28
  const oldPattern = '@nextcloud/vue/dist/([^/]+)/([^/.]+)';
29
- const mixinPattern = '@nextcloud/vue/mixins/([^/.]+)';
29
+ // Matches both the legacy `dist/Mixins/*` path and a bare `mixins/*` path.
30
+ // There is no clean `@nextcloud/vue/mixins/*` subpath export (only components,
31
+ // composables, directives and functions), so mixins must be migrated to
32
+ // composables rather than have their import path rewritten.
33
+ const mixinPattern = '@nextcloud/vue/(?:dist/)?mixins/([^/.]+)';
30
34
  const isVersionValidForTooltip = versionSatisfies('8.25.0');
31
35
  const tooltipPattern = '@nextcloud/vue/directives/Tooltip';
32
36
  const isVersionValidForNcSettingsInputText = versionSatisfies('8.31.0');
@@ -73,6 +77,10 @@ const rule = {
73
77
  context.report({ node, messageId: 'outdatedVueLibrary' });
74
78
  return;
75
79
  }
80
+ // Mixins removed in v9 and already reported as `deprecatedMixin`
81
+ if (match[1].toLowerCase() === 'mixins') {
82
+ return;
83
+ }
76
84
  const newImportPath = `'@nextcloud/vue/${match[1].toLowerCase()}/${match[2]}'`;
77
85
  context.report({
78
86
  node,
@@ -4,6 +4,37 @@
4
4
  */
5
5
  import { createLibVersionValidator } from "../utils/lib-version-parser.js";
6
6
  import { defineTemplateBodyVisitor } from "../utils/vue-template-visitor.js";
7
+ /*
8
+ * Deprecated props can be authored in either kebab-case or camelCase:
9
+ * - camelCase is forced by `vue/attribute-hyphenation: never` shipped rule;
10
+ * - kebab-case might come from legacy code and other presets.
11
+ * A single `VAttribute` visitor normalizes the raw attribute name with `toCamelCase()`
12
+ * and dispatches to the handler registered under the canonical camelCase name.
13
+ */
14
+ /**
15
+ * Convert an attribute name to camelCase, matching Vue's own prop normalization,
16
+ * so a template attribute written in either casing maps to a single canonical name.
17
+ *
18
+ * @param name - the raw attribute name as written in the template
19
+ */
20
+ const toCamelCase = (name) => name.replace(/-(\w)/g, (_, char) => char.toUpperCase());
21
+ /**
22
+ * Resolve the raw (case-preserving) name of a template attribute: the argument
23
+ * for a directive binding (`:foo` / `v-model:foo`), or the key otherwise.
24
+ * Returns `undefined` when there is no static name (e.g. a dynamic argument).
25
+ *
26
+ * Must use `rawName`, not `name`: Vue lowercases `name` in the AST (`focusTrap`
27
+ * becomes `focustrap`), which `toCamelCase()` cannot restore, so it would no
28
+ * longer match the camelCase handler key. `rawName` keeps the original casing.
29
+ *
30
+ * @param node - the attribute or directive node
31
+ */
32
+ function getRawName(node) {
33
+ if (node.key.type === 'VDirectiveKey') {
34
+ return node.key.argument?.type === 'VIdentifier' ? node.key.argument.rawName : undefined;
35
+ }
36
+ return node.key.rawName;
37
+ }
7
38
  export default {
8
39
  meta: {
9
40
  docs: {
@@ -28,7 +59,7 @@ export default {
28
59
  useNoFocusTrapInstead: 'Using `focus-trap` is deprecated - use `no-focus-trap` instead',
29
60
  useKeepOpenInstead: 'Using `close-on-select` is deprecated - use `keep-open` instead',
30
61
  useNcSelectUsersInstead: 'Using `user-select` is deprecated - use `NcSelectUsers` component instead',
31
- useArrowEndInstead: 'Using `arrow-right` is deprecated - use `arrow-end` instead',
62
+ useArrowEndInstead: 'Using `arrowRight` as `trailing-button-icon` value is deprecated - use `arrowEnd` instead',
32
63
  removeAriaHidden: 'Using `aria-hidden` is deprecated - remove prop from components, otherwise root element will inherit incorrect attribute.',
33
64
  removeLimitWidth: 'Using `limit-width` is deprecated - remove prop from components, otherwise root element will inherit incorrect attribute.',
34
65
  removeExact: 'Using `exact` is deprecated - consult Vue Router documentation for alternatives.',
@@ -48,7 +79,7 @@ export default {
48
79
  const isDateTimePickerFormatValid = versionSatisfies('8.25.0'); // #6738
49
80
  const isNcSelectKeepOpenValid = versionSatisfies('8.25.0'); // #6791
50
81
  const isNcPopoverNoFocusTrapValid = versionSatisfies('8.26.0'); // #6808
51
- const isNcSelectUsersValid = versionSatisfies('8.27.1'); // #7032
82
+ const isNcSelectUsersValid = versionSatisfies('8.25.0'); // #6791
52
83
  const isNcTextFieldArrowEndValid = versionSatisfies('8.28.0'); // #7002
53
84
  const isCloseButtonOutsideValid = versionSatisfies('8.32.0'); // #7553
54
85
  const legacyTypes = [
@@ -60,8 +91,13 @@ export default {
60
91
  'tertiary',
61
92
  'tertiary-no-background',
62
93
  ];
63
- return defineTemplateBodyVisitor(context, {
64
- 'VElement VAttribute:has(VIdentifier[name="type"])': function (node) {
94
+ /*
95
+ * Deprecated prop handlers keyed by their canonical camelCase name. Each
96
+ * handler receives the matched attribute node and verifies the owning
97
+ * component and installed library version itself.
98
+ */
99
+ const handlers = {
100
+ type: function (node) {
65
101
  if (![
66
102
  'ncactions',
67
103
  'ncappnavigationnew',
@@ -75,8 +111,10 @@ export default {
75
111
  context.report({ node, messageId: 'outdatedVueLibrary' });
76
112
  return;
77
113
  }
78
- const hasNativeType = node.parent.attributes.find((attr) => (attr.key.name === 'native-type'
79
- || (attr.key.type === 'VDirectiveKey' && attr.key.argument && attr.key.argument.type === 'VIdentifier' && attr.key.argument.name === 'native-type')));
114
+ const hasNativeType = node.parent.attributes.some((attr) => {
115
+ const rawName = getRawName(attr);
116
+ return rawName !== undefined && toCamelCase(rawName) === 'nativeType';
117
+ });
80
118
  const isLiteral = node.value.type === 'VLiteral' && legacyTypes.includes(node.value.value);
81
119
  const isExpression = node.value.type === 'VExpressionContainer'
82
120
  && node.value.expression.type === 'ConditionalExpression'
@@ -102,7 +140,7 @@ export default {
102
140
  });
103
141
  }
104
142
  },
105
- 'VElement VAttribute:has(VIdentifier[name="native-type"])': function (node) {
143
+ nativeType: function (node) {
106
144
  if (![
107
145
  'ncbutton',
108
146
  'ncdialogbutton',
@@ -126,7 +164,7 @@ export default {
126
164
  },
127
165
  });
128
166
  },
129
- 'VElement VAttribute:has(VIdentifier[name="aria-hidden"])': function (node) {
167
+ ariaHidden: function (node) {
130
168
  if (node.parent.parent.name.startsWith('ncaction')
131
169
  || node.parent.parent.name === 'ncbutton') {
132
170
  if (!isAriaHiddenValid) {
@@ -139,7 +177,10 @@ export default {
139
177
  });
140
178
  }
141
179
  },
142
- 'VElement[name="ncappcontent"] VAttribute:has(VIdentifier[name="allow-swipe-navigation"])': function (node) {
180
+ allowSwipeNavigation: function (node) {
181
+ if (node.parent.parent.name !== 'ncappcontent') {
182
+ return;
183
+ }
143
184
  if (!isDisableSwipeValid) {
144
185
  context.report({ node, messageId: 'outdatedVueLibrary' });
145
186
  return;
@@ -149,7 +190,10 @@ export default {
149
190
  messageId: 'useDisableSwipeForNavInstead',
150
191
  });
151
192
  },
152
- 'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="show-user-status"])': function (node) {
193
+ showUserStatus: function (node) {
194
+ if (node.parent.parent.name !== 'ncavatar') {
195
+ return;
196
+ }
153
197
  if (!isDefaultBooleanFalseValid) {
154
198
  context.report({ node, messageId: 'outdatedVueLibrary' });
155
199
  return;
@@ -159,7 +203,10 @@ export default {
159
203
  messageId: 'useHideStatusInstead',
160
204
  });
161
205
  },
162
- 'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="show-user-status-compact"])': function (node) {
206
+ showUserStatusCompact: function (node) {
207
+ if (node.parent.parent.name !== 'ncavatar') {
208
+ return;
209
+ }
163
210
  if (!isDefaultBooleanFalseValid) {
164
211
  context.report({ node, messageId: 'outdatedVueLibrary' });
165
212
  return;
@@ -169,7 +216,10 @@ export default {
169
216
  messageId: 'useVerboseStatusInstead',
170
217
  });
171
218
  },
172
- 'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="allow-placeholder"])': function (node) {
219
+ allowPlaceholder: function (node) {
220
+ if (node.parent.parent.name !== 'ncavatar') {
221
+ return;
222
+ }
173
223
  if (!isDefaultBooleanFalseValid) {
174
224
  context.report({ node, messageId: 'outdatedVueLibrary' });
175
225
  return;
@@ -179,7 +229,10 @@ export default {
179
229
  messageId: 'useNoPlaceholderInstead',
180
230
  });
181
231
  },
182
- 'VElement[name="ncdatetimepicker"] VAttribute:has(VIdentifier[name="formatter"])': function (node) {
232
+ formatter: function (node) {
233
+ if (node.parent.parent.name !== 'ncdatetimepicker') {
234
+ return;
235
+ }
183
236
  if (!isDateTimePickerFormatValid) {
184
237
  context.report({ node, messageId: 'outdatedVueLibrary' });
185
238
  return;
@@ -189,7 +242,10 @@ export default {
189
242
  messageId: 'useFormatInstead',
190
243
  });
191
244
  },
192
- 'VElement[name="ncdatetimepicker"] VAttribute:has(VIdentifier[name="lang"])': function (node) {
245
+ lang: function (node) {
246
+ if (node.parent.parent.name !== 'ncdatetimepicker') {
247
+ return;
248
+ }
193
249
  if (!isVue3Valid) {
194
250
  // Do not throw for v8.X.X
195
251
  return;
@@ -199,7 +255,10 @@ export default {
199
255
  messageId: 'useLocaleInstead',
200
256
  });
201
257
  },
202
- 'VElement[name="ncdatetimepicker"] VAttribute:has(VIdentifier[name="range"])': function (node) {
258
+ range: function (node) {
259
+ if (node.parent.parent.name !== 'ncdatetimepicker') {
260
+ return;
261
+ }
203
262
  if (!isDateTimePickerFormatValid) {
204
263
  context.report({ node, messageId: 'outdatedVueLibrary' });
205
264
  return;
@@ -209,7 +268,7 @@ export default {
209
268
  messageId: 'useTypeDateRangeInstead',
210
269
  });
211
270
  },
212
- 'VElement VAttribute:has(VIdentifier[name="can-close"])': function (node) {
271
+ canClose: function (node) {
213
272
  if (![
214
273
  'ncdialog',
215
274
  'ncmodal',
@@ -225,7 +284,10 @@ export default {
225
284
  messageId: 'useNoCloseInstead',
226
285
  });
227
286
  },
228
- 'VElement[name="ncpopover"] VAttribute:has(VIdentifier[name="close-on-click-outside"])': function (node) {
287
+ closeOnClickOutside: function (node) {
288
+ if (node.parent.parent.name !== 'ncpopover') {
289
+ return;
290
+ }
229
291
  if (!isVue3Valid) {
230
292
  // Do not throw for v8.X.X
231
293
  return;
@@ -235,7 +297,10 @@ export default {
235
297
  messageId: 'useNoCloseOnClickOutsideInstead',
236
298
  });
237
299
  },
238
- 'VElement[name="ncmodal"] VAttribute:has(VIdentifier[name="enable-swipe"])': function (node) {
300
+ enableSwipe: function (node) {
301
+ if (node.parent.parent.name !== 'ncmodal') {
302
+ return;
303
+ }
239
304
  if (!isDisableSwipeValid) {
240
305
  context.report({ node, messageId: 'outdatedVueLibrary' });
241
306
  return;
@@ -245,7 +310,10 @@ export default {
245
310
  messageId: 'useDisableSwipeForModalInstead',
246
311
  });
247
312
  },
248
- 'VElement[name="ncmodal"] VAttribute:has(VIdentifier[name="close-button-contained"])': function (node) {
313
+ closeButtonContained: function (node) {
314
+ if (node.parent.parent.name !== 'ncmodal') {
315
+ return;
316
+ }
249
317
  if (!isCloseButtonOutsideValid) {
250
318
  context.report({ node, messageId: 'outdatedVueLibrary' });
251
319
  return;
@@ -255,7 +323,10 @@ export default {
255
323
  messageId: 'useCloseButtonOutsideInstead',
256
324
  });
257
325
  },
258
- 'VElement[name="ncpopover"] VAttribute:has(VIdentifier[name="focus-trap"])': function (node) {
326
+ focusTrap: function (node) {
327
+ if (node.parent.parent.name !== 'ncpopover') {
328
+ return;
329
+ }
259
330
  if (!isNcPopoverNoFocusTrapValid) {
260
331
  context.report({ node, messageId: 'outdatedVueLibrary' });
261
332
  return;
@@ -265,7 +336,10 @@ export default {
265
336
  messageId: 'useNoFocusTrapInstead',
266
337
  });
267
338
  },
268
- 'VElement[name="ncselect"] VAttribute:has(VIdentifier[name="close-on-select"])': function (node) {
339
+ closeOnSelect: function (node) {
340
+ if (node.parent.parent.name !== 'ncselect') {
341
+ return;
342
+ }
269
343
  if (!isNcSelectKeepOpenValid) {
270
344
  context.report({ node, messageId: 'outdatedVueLibrary' });
271
345
  return;
@@ -275,7 +349,10 @@ export default {
275
349
  messageId: 'useKeepOpenInstead',
276
350
  });
277
351
  },
278
- 'VElement[name="ncselect"] VAttribute:has(VIdentifier[name="user-select"])': function (node) {
352
+ userSelect: function (node) {
353
+ if (node.parent.parent.name !== 'ncselect') {
354
+ return;
355
+ }
279
356
  if (!isNcSelectUsersValid) {
280
357
  context.report({ node, messageId: 'outdatedVueLibrary' });
281
358
  return;
@@ -285,7 +362,7 @@ export default {
285
362
  messageId: 'useNcSelectUsersInstead',
286
363
  });
287
364
  },
288
- 'VElement VAttribute:has(VIdentifier[name="trailing-button-icon"])': function (node) {
365
+ trailingButtonIcon: function (node) {
289
366
  if (node.parent.parent.name !== 'nctextfield') {
290
367
  return;
291
368
  }
@@ -320,14 +397,17 @@ export default {
320
397
  });
321
398
  }
322
399
  },
323
- 'VElement[name="ncsettingssection"] VAttribute:has(VIdentifier[name="limit-width"])': function (node) {
324
- // This was deprecated in 8.13.0 (Nextcloud 30+), before first supported version by plugin
400
+ limitWidth: function (node) {
401
+ if (node.parent.parent.name !== 'ncsettingssection') {
402
+ return;
403
+ }
404
+ // This was deprecated in 8.12.0 (Nextcloud 30+), before first supported version by plugin
325
405
  context.report({
326
406
  node,
327
407
  messageId: 'removeLimitWidth',
328
408
  });
329
409
  },
330
- 'VElement VAttribute:has(VIdentifier[name="exact"])': function (node) {
410
+ exact: function (node) {
331
411
  if (![
332
412
  'ncactionrouter',
333
413
  'ncappnavigationitem',
@@ -346,7 +426,7 @@ export default {
346
426
  messageId: 'removeExact',
347
427
  });
348
428
  },
349
- 'VElement VAttribute:has(VIdentifier[name="checked"])': function (node) {
429
+ checked: function (node) {
350
430
  if (![
351
431
  'ncactioncheckbox',
352
432
  'ncactionradio',
@@ -379,7 +459,7 @@ export default {
379
459
  },
380
460
  });
381
461
  },
382
- 'VElement VAttribute:has(VIdentifier[name="value"])': function (node) {
462
+ value: function (node) {
383
463
  if (![
384
464
  'ncactioninput',
385
465
  'ncactiontexteditable',
@@ -424,6 +504,15 @@ export default {
424
504
  },
425
505
  });
426
506
  },
507
+ };
508
+ return defineTemplateBodyVisitor(context, {
509
+ VAttribute(node) {
510
+ const rawName = getRawName(node);
511
+ if (rawName === undefined) {
512
+ return;
513
+ }
514
+ handlers[toCamelCase(rawName)]?.(node);
515
+ },
427
516
  });
428
517
  },
429
518
  };
@@ -5,13 +5,12 @@
5
5
  * @param options Options
6
6
  * @param options.cwd The current working directory
7
7
  * @param options.physicalFilename The real filename where ESLint is linting currently
8
- * @param importResolve Optional custom import resolver function (for testing purposes)
9
8
  * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
10
9
  */
11
10
  export declare function createLibVersionValidator({ cwd, physicalFilename }: {
12
- cwd: any;
13
- physicalFilename: any;
14
- }, importResolve?: (specifier: string, parent?: string | import("node:url").URL) => string): ((version: string) => boolean);
11
+ cwd: string;
12
+ physicalFilename: string;
13
+ }): ((version: string) => boolean);
15
14
  /**
16
15
  * Clear the module cache
17
16
  */
@@ -3,8 +3,9 @@
3
3
  * SPDX-License-Identifier: AGPL-3.0-or-later
4
4
  */
5
5
  import { readFileSync } from 'node:fs';
6
- import { isAbsolute, join, resolve } from 'node:path';
7
- import { fileURLToPath, pathToFileURL } from 'node:url';
6
+ import { findPackageJSON } from 'node:module';
7
+ import { isAbsolute, resolve } from 'node:path';
8
+ import { pathToFileURL } from 'node:url';
8
9
  import { gte } from 'semver';
9
10
  /**
10
11
  * Cached map of paths: Reco <path_to_package.json, validator>
@@ -17,29 +18,29 @@ const cachedMap = new Map();
17
18
  * @param options Options
18
19
  * @param options.cwd The current working directory
19
20
  * @param options.physicalFilename The real filename where ESLint is linting currently
20
- * @param importResolve Optional custom import resolver function (for testing purposes)
21
21
  * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
22
22
  */
23
- export function createLibVersionValidator({ cwd, physicalFilename }, importResolve = import.meta.resolve) {
23
+ export function createLibVersionValidator({ cwd, physicalFilename }) {
24
24
  // Try to find package.json of the nextcloud-vue package
25
25
  const sourceFile = isAbsolute(physicalFilename)
26
26
  ? resolve(physicalFilename)
27
27
  : resolve(cwd, physicalFilename);
28
- let packageJsonDir;
28
+ let packageJsonPath;
29
29
  try {
30
- const modulePath = fileURLToPath(importResolve('@nextcloud/vue', pathToFileURL(sourceFile)));
31
- const idx = modulePath.lastIndexOf('/dist/');
32
- packageJsonDir = modulePath.substring(0, idx);
30
+ packageJsonPath = findPackageJSON('@nextcloud/vue', pathToFileURL(sourceFile));
33
31
  }
34
32
  catch {
33
+ packageJsonPath = undefined;
34
+ }
35
+ if (!packageJsonPath) {
35
36
  return () => false;
36
37
  }
37
- if (!cachedMap[packageJsonDir]) {
38
- const json = JSON.parse(readFileSync(join(packageJsonDir, 'package.json'), 'utf-8'));
38
+ if (!cachedMap.has(packageJsonPath)) {
39
+ const json = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
39
40
  const libVersion = json.version;
40
- cachedMap[packageJsonDir] = (version) => gte(libVersion, version);
41
+ cachedMap.set(packageJsonPath, (version) => gte(libVersion, version));
41
42
  }
42
- return cachedMap[packageJsonDir];
43
+ return cachedMap.get(packageJsonPath);
43
44
  }
44
45
  /**
45
46
  * Clear the module cache
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextcloud/eslint-config",
3
- "version": "9.0.0",
3
+ "version": "9.0.1",
4
4
  "description": "Eslint shared config for nextcloud apps and libraries",
5
5
  "keywords": [
6
6
  "eslint",
@@ -48,19 +48,19 @@
48
48
  "@stylistic/eslint-plugin": "^5.10.0",
49
49
  "eslint-config-flat-gitignore": "^2.3.0",
50
50
  "eslint-plugin-antfu": "^3.2.3",
51
- "eslint-plugin-jsdoc": "^63.0.7",
51
+ "eslint-plugin-jsdoc": "^63.0.10",
52
52
  "eslint-plugin-perfectionist": "^5.9.1",
53
53
  "eslint-plugin-vue": "^10.9.2",
54
54
  "fast-xml-parser": "^5.9.3",
55
55
  "globals": "^17.7.0",
56
56
  "semver": "^7.8.5",
57
57
  "sort-package-json": "^4.0.0",
58
- "typescript-eslint": "^8.62.0"
58
+ "typescript-eslint": "^8.62.1"
59
59
  },
60
60
  "devDependencies": {
61
- "@types/node": "^26.0.0",
61
+ "@types/node": "^26.0.1",
62
62
  "@types/semver": "^7.7.1",
63
- "eslint": "^10.5.0",
63
+ "eslint": "^10.6.0",
64
64
  "memfs": "^4.57.8",
65
65
  "vitest": "^4.1.9"
66
66
  },
@@ -68,7 +68,7 @@
68
68
  "eslint": ">=10"
69
69
  },
70
70
  "engines": {
71
- "node": "^22.13 || ^24 || >=26"
71
+ "node": "^22.14 || ^24 || >=26"
72
72
  },
73
73
  "devEngines": {
74
74
  "packageManager": [