@nextcloud/eslint-config 9.0.0-rc.2 → 9.0.0-rc.4
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 +10 -0
- package/dist/configs/codeStyle.js +3 -6
- package/dist/configs/filesystem.js +10 -5
- package/dist/configs/imports.js +3 -11
- package/dist/configs/vue.js +7 -4
- package/dist/plugins/import-extensions/index.d.ts +14 -0
- package/dist/plugins/import-extensions/index.js +13 -0
- package/dist/plugins/import-extensions/rules/extensions.d.ts +11 -0
- package/dist/plugins/import-extensions/rules/extensions.js +143 -0
- package/dist/plugins/import-extensions/rules/index.d.ts +7 -0
- package/dist/plugins/import-extensions/rules/index.js +8 -0
- package/dist/plugins/l10n/index.js +6 -6
- package/dist/plugins/l10n/rules/enforce-ellipsis-vue.d.ts +7 -0
- package/dist/plugins/l10n/rules/enforce-ellipsis-vue.js +13 -0
- package/dist/plugins/l10n/rules/enforce-ellipsis.js +19 -13
- package/dist/plugins/l10n/rules/index.d.ts +10 -0
- package/dist/plugins/l10n/rules/index.js +14 -0
- package/dist/plugins/l10n/rules/non-breaking-space-vue.d.ts +7 -0
- package/dist/plugins/l10n/rules/non-breaking-space-vue.js +13 -0
- package/dist/plugins/l10n/rules/non-breaking-space.js +4 -1
- package/dist/plugins/nextcloud-vue/index.d.ts +13 -0
- package/dist/plugins/nextcloud-vue/rules/index.d.ts +13 -0
- package/dist/plugins/nextcloud-vue/rules/no-deprecated-exports.js +9 -6
- package/dist/plugins/nextcloud-vue/rules/no-deprecated-props.d.ts +13 -0
- package/dist/plugins/nextcloud-vue/rules/no-deprecated-props.js +177 -2
- package/dist/utils.d.ts +1 -0
- package/package.json +21 -18
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,10 @@ Please refer to the README on how to adjust your configuration for flat config.
|
|
|
22
22
|
* feat(vue): add `vue/no-useless-v-bind` rule [#1063](https://github.com/nextcloud-libraries/eslint-config/pull/1063) ([susnux](https://github.com/susnux))
|
|
23
23
|
* feat(vue): add `vue/prefer-separate-static-class` rule [#1065](https://github.com/nextcloud-libraries/eslint-config/pull/1065) ([susnux](https://github.com/susnux))
|
|
24
24
|
* feat(vue3): add script-setup releated rules [#1064](https://github.com/nextcloud-libraries/eslint-config/pull/1064) ([susnux](https://github.com/susnux))
|
|
25
|
+
* feat(no-deprecated-props): extend existing rules to support other components [\#1069](https://github.com/nextcloud-libraries/eslint-config/pull/1069) \([Antreesy](https://github.com/Antreesy)\)
|
|
26
|
+
* feat(imports): add custom plugin to suggest file extensions [\#1110](https://github.com/nextcloud-libraries/eslint-config/pull/1110) \([susnux](https://github.com/susnux)\)
|
|
27
|
+
* feat(filesystem): ignore all files within the `.gitignore` [\#1108](https://github.com/nextcloud-libraries/eslint-config/pull/1108) \([susnux](https://github.com/susnux)\)
|
|
28
|
+
* feat(l10n-plugin): also handle vue templates by @susnux in https://github.com/nextcloud-libraries/eslint-config/pull/1113
|
|
25
29
|
|
|
26
30
|
### Fixed
|
|
27
31
|
* fix(codestyle): do not require splitting chains [\#951](https://github.com/nextcloud-libraries/eslint-config/pull/951)
|
|
@@ -46,6 +50,12 @@ Please refer to the README on how to adjust your configuration for flat config.
|
|
|
46
50
|
* fix(codeStyle): allow single line arrays [#997](https://github.com/nextcloud-libraries/eslint-config/pull/997) ([ShGKme](https://github.com/ShGKme))
|
|
47
51
|
* fix(typescript): remove `@typescript-eslint/no-shadow` [#993](https://github.com/nextcloud-libraries/eslint-config/pull/993) ([ShGKme](https://github.com/ShGKme))
|
|
48
52
|
* fix(codeStyle): allow devs write small objects in one line [#996](https://github.com/nextcloud-libraries/eslint-config/pull/996) ([ShGKme](https://github.com/ShGKme))
|
|
53
|
+
* fix: ignore test report directories from linting [\#1096](https://github.com/nextcloud-libraries/eslint-config/pull/1096) \([Antreesy](https://github.com/Antreesy)\)
|
|
54
|
+
* fix(vue): also ignore the global router link component [\#1097](https://github.com/nextcloud-libraries/eslint-config/pull/1097) \([susnux](https://github.com/susnux)\)
|
|
55
|
+
* fix(no-deprecated-props): respect nextcloud/vue library version for the rule [\#1084](https://github.com/nextcloud-libraries/eslint-config/pull/1084) \([Antreesy](https://github.com/Antreesy)\)
|
|
56
|
+
* fix(codestyle): replace deprecated config in `@stylistic/quotes` rule [\#1109](https://github.com/nextcloud-libraries/eslint-config/pull/1109) \([susnux](https://github.com/susnux)\)
|
|
57
|
+
* fix(l10n-plugin): also check translation strings in `n` method by @susnux in https://github.com/nextcloud-libraries/eslint-config/pull/1112
|
|
58
|
+
* fix(filesystem): relax ignored files by @susnux in https://github.com/nextcloud-libraries/eslint-config/pull/1114
|
|
49
59
|
|
|
50
60
|
### Changed
|
|
51
61
|
* Add SPDX header [#802](https://github.com/nextcloud-libraries/eslint-config/pull/802)
|
|
@@ -55,7 +55,7 @@ export function codeStyle(options) {
|
|
|
55
55
|
'error',
|
|
56
56
|
'single',
|
|
57
57
|
{
|
|
58
|
-
allowTemplateLiterals:
|
|
58
|
+
allowTemplateLiterals: 'never',
|
|
59
59
|
avoidEscape: true,
|
|
60
60
|
},
|
|
61
61
|
],
|
|
@@ -75,13 +75,10 @@ export function codeStyle(options) {
|
|
|
75
75
|
'properties',
|
|
76
76
|
{ avoidQuotes: true },
|
|
77
77
|
],
|
|
78
|
-
// Enforce new lines after [ and before ]
|
|
78
|
+
// Enforce consistent new lines after [ and before ]
|
|
79
79
|
'@stylistic/array-bracket-newline': [
|
|
80
80
|
'error',
|
|
81
|
-
|
|
82
|
-
multiline: true,
|
|
83
|
-
minItems: null, // disable
|
|
84
|
-
},
|
|
81
|
+
'consistent',
|
|
85
82
|
],
|
|
86
83
|
// Enforce new lines between array elements (better git diff) but allow to have single line arrays
|
|
87
84
|
'@stylistic/array-element-newline': ['error', 'consistent'],
|
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
3
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
4
|
*/
|
|
5
|
+
import gitignore from 'eslint-config-flat-gitignore';
|
|
5
6
|
/**
|
|
6
7
|
* General config to exclude known non-source directories from linting
|
|
7
8
|
*/
|
|
8
9
|
export const filesystem = [
|
|
10
|
+
{
|
|
11
|
+
...gitignore(),
|
|
12
|
+
name: 'nextcloud/filesystem/gitignore',
|
|
13
|
+
},
|
|
9
14
|
{
|
|
10
15
|
name: 'nextcloud/filesystem/ignores',
|
|
11
16
|
ignores: [
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
+
'dist/',
|
|
18
|
+
'js/',
|
|
19
|
+
'l10n/',
|
|
20
|
+
'vendor/',
|
|
21
|
+
'vendor-bin/',
|
|
17
22
|
'**/package-lock.json',
|
|
18
23
|
],
|
|
19
24
|
},
|
package/dist/configs/imports.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import perfectionist from 'eslint-plugin-perfectionist';
|
|
2
2
|
import { GLOB_FILES_JAVASCRIPT, GLOB_FILES_TYPESCRIPT, GLOB_FILES_VUE, } from "../globs.js";
|
|
3
|
+
import importExtensions from "../plugins/import-extensions/index.js";
|
|
3
4
|
/**
|
|
4
5
|
* Generate imports and exports related ESLint rules.
|
|
5
6
|
*
|
|
@@ -12,6 +13,7 @@ export function imports(options) {
|
|
|
12
13
|
name: 'nextcloud/imports/setup',
|
|
13
14
|
plugins: {
|
|
14
15
|
perfectionist,
|
|
16
|
+
'import-extensions': importExtensions,
|
|
15
17
|
},
|
|
16
18
|
},
|
|
17
19
|
{
|
|
@@ -23,17 +25,7 @@ export function imports(options) {
|
|
|
23
25
|
],
|
|
24
26
|
rules: {
|
|
25
27
|
// Require file extensions
|
|
26
|
-
'
|
|
27
|
-
'error',
|
|
28
|
-
{
|
|
29
|
-
patterns: [
|
|
30
|
-
{
|
|
31
|
-
regex: '^(\\.*)/(.+/)*[^/.]+$',
|
|
32
|
-
message: 'Import is missing the file extension.',
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
],
|
|
28
|
+
'import-extensions/extensions': 'error',
|
|
37
29
|
// Sorting of imports
|
|
38
30
|
'sort-imports': 'off',
|
|
39
31
|
'perfectionist/sort-imports': [
|
package/dist/configs/vue.js
CHANGED
|
@@ -35,6 +35,7 @@ export function vue(options) {
|
|
|
35
35
|
: []),
|
|
36
36
|
{
|
|
37
37
|
files: GLOB_FILES_VUE,
|
|
38
|
+
name: 'nextcloud/vue/rules',
|
|
38
39
|
rules: {
|
|
39
40
|
// PascalCase components names for vuejs
|
|
40
41
|
'vue/component-name-in-template-casing': [
|
|
@@ -90,8 +91,8 @@ export function vue(options) {
|
|
|
90
91
|
'vue/no-undef-components': [
|
|
91
92
|
'warn',
|
|
92
93
|
{
|
|
93
|
-
// Ignore the router view as this is most often globally registered
|
|
94
|
-
ignorePatterns: ['
|
|
94
|
+
// Ignore the router link and view as this is (most often) globally registered
|
|
95
|
+
ignorePatterns: ['RouterLink', 'RouterView'],
|
|
95
96
|
},
|
|
96
97
|
],
|
|
97
98
|
// Warn on unused refs
|
|
@@ -99,13 +100,16 @@ export function vue(options) {
|
|
|
99
100
|
// Warn on unused props
|
|
100
101
|
'vue/no-unused-properties': 'warn',
|
|
101
102
|
},
|
|
102
|
-
name: 'nextcloud/vue/rules',
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
files: GLOB_FILES_VUE,
|
|
106
|
+
name: 'nextcloud/vue/stylistic-rules',
|
|
106
107
|
rules: {
|
|
107
108
|
// same as the stylistic rules but for the <template> in Vue files
|
|
108
109
|
...Object.fromEntries(vueStylisticRules),
|
|
110
|
+
// same as in the codeStyle config but for the <template> in Vue files
|
|
111
|
+
'@nextcloud-l10n/non-breaking-space-vue': 'error',
|
|
112
|
+
'@nextcloud-l10n/enforce-ellipsis-vue': 'error',
|
|
109
113
|
// Also enforce tabs for template
|
|
110
114
|
'vue/html-indent': [
|
|
111
115
|
'error',
|
|
@@ -131,7 +135,6 @@ export function vue(options) {
|
|
|
131
135
|
},
|
|
132
136
|
],
|
|
133
137
|
},
|
|
134
|
-
name: 'nextcloud/vue/stylistic-rules',
|
|
135
138
|
},
|
|
136
139
|
{
|
|
137
140
|
files: [
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
declare const _default: {
|
|
6
|
+
rules: {
|
|
7
|
+
extensions: import("eslint").Rule.RuleModule;
|
|
8
|
+
};
|
|
9
|
+
meta: {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import { packageVersion } from "../../version.js";
|
|
6
|
+
import { rules } from "./rules/index.js";
|
|
7
|
+
export default {
|
|
8
|
+
rules,
|
|
9
|
+
meta: {
|
|
10
|
+
name: '@nextcloud/eslint-plugin',
|
|
11
|
+
version: packageVersion,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import type { Rule } from 'eslint';
|
|
6
|
+
/**
|
|
7
|
+
* helper for unit tests
|
|
8
|
+
*/
|
|
9
|
+
export declare function clearCache(): void;
|
|
10
|
+
export declare const rule: Rule.RuleModule;
|
|
11
|
+
export default rule;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, opendirSync, statSync } from 'fs';
|
|
6
|
+
import { basename, dirname, extname, join, resolve } from 'path';
|
|
7
|
+
// we use this module scope map to cache results for resolving the file extensions
|
|
8
|
+
// in larger projects its likely the same files are imported so we cache "absolute resolved import without extension" -> array of possible extensions
|
|
9
|
+
const fsCache = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* helper for unit tests
|
|
12
|
+
*/
|
|
13
|
+
export function clearCache() {
|
|
14
|
+
fsCache.clear();
|
|
15
|
+
}
|
|
16
|
+
export const rule = {
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
hasSuggestions: true,
|
|
20
|
+
docs: {
|
|
21
|
+
description: 'Ensure all relative imports and exports have a file extension',
|
|
22
|
+
},
|
|
23
|
+
messages: {
|
|
24
|
+
missingExtension: 'This relative {{ type }} should have a file extension.',
|
|
25
|
+
recommendedMissingExtension: 'The relative {{ type }} should probably have the file extension "{{ extension }}".',
|
|
26
|
+
applySuggestedExtension: 'Add the "{{ extension }}" to the {{ type }}.',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
create(context) {
|
|
30
|
+
/**
|
|
31
|
+
* @param node - The ESTree node representing the source
|
|
32
|
+
* @param isImport - is this an import or export
|
|
33
|
+
*/
|
|
34
|
+
function handleImportExport(node, isImport = true) {
|
|
35
|
+
const value = String(node.value);
|
|
36
|
+
if (!value.includes('/')) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// get rid of query like ?raw
|
|
40
|
+
const [text] = value.split('?', 2);
|
|
41
|
+
if (text.match(/\.[a-z0-9]+$/i)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const type = isImport ? 'import' : 'export';
|
|
45
|
+
// check custom paths - we cannot fix though
|
|
46
|
+
if (text.startsWith('~/')) {
|
|
47
|
+
context.report({
|
|
48
|
+
node,
|
|
49
|
+
messageId: 'missingExtension',
|
|
50
|
+
data: { type },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!text.match(/^\.\.?\//)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const contextPath = dirname(context.physicalFilename);
|
|
57
|
+
const relativePath = resolve(contextPath, text);
|
|
58
|
+
const absolutePath = resolve(context.cwd, relativePath);
|
|
59
|
+
if (!fsCache.has(absolutePath)) {
|
|
60
|
+
let resolvedPath = relativePath;
|
|
61
|
+
if (existsSync(resolvedPath)) {
|
|
62
|
+
if (statSync(resolvedPath).isDirectory) {
|
|
63
|
+
resolvedPath = join(resolvedPath, 'index');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// weird extensionless file
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const resolvedDirectoryPath = dirname(resolvedPath);
|
|
71
|
+
if (existsSync(resolvedDirectoryPath)) {
|
|
72
|
+
const filename = basename(resolvedPath);
|
|
73
|
+
const resolvedDir = opendirSync(resolvedDirectoryPath);
|
|
74
|
+
const extensions = [];
|
|
75
|
+
try {
|
|
76
|
+
let entry;
|
|
77
|
+
while ((entry = resolvedDir.readSync()) !== null) {
|
|
78
|
+
const extension = extname(entry.name);
|
|
79
|
+
if (extension && `${filename}${extension}` === entry.name) {
|
|
80
|
+
extensions.push(extension);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
resolvedDir.close();
|
|
86
|
+
}
|
|
87
|
+
fsCache.set(absolutePath, extensions);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (fsCache.get(absolutePath)?.length) {
|
|
91
|
+
return context.report({
|
|
92
|
+
node,
|
|
93
|
+
messageId: fsCache.get(absolutePath).length === 1
|
|
94
|
+
? 'recommendedMissingExtension'
|
|
95
|
+
: 'missingExtension',
|
|
96
|
+
data: {
|
|
97
|
+
extension: fsCache.get(absolutePath)[0],
|
|
98
|
+
type,
|
|
99
|
+
},
|
|
100
|
+
suggest: fsCache.get(absolutePath).map((extension) => ({
|
|
101
|
+
messageId: 'applySuggestedExtension',
|
|
102
|
+
data: {
|
|
103
|
+
extension,
|
|
104
|
+
type,
|
|
105
|
+
},
|
|
106
|
+
fix(fixer) {
|
|
107
|
+
const range = [node.range[0], node.range[0] + 1 + text.length];
|
|
108
|
+
return fixer.insertTextAfterRange(range, extension);
|
|
109
|
+
},
|
|
110
|
+
})),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// no way to fix it
|
|
114
|
+
context.report({
|
|
115
|
+
node,
|
|
116
|
+
messageId: 'missingExtension',
|
|
117
|
+
data: {
|
|
118
|
+
type,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ExportAllDeclaration(node) {
|
|
124
|
+
handleImportExport(node.source, false);
|
|
125
|
+
},
|
|
126
|
+
ExportNamedDeclaration(node) {
|
|
127
|
+
if (node.source) {
|
|
128
|
+
handleImportExport(node.source, false);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
ImportDeclaration(node) {
|
|
132
|
+
handleImportExport(node.source);
|
|
133
|
+
},
|
|
134
|
+
ImportExpression(node) {
|
|
135
|
+
if (node.source.type === 'Literal') {
|
|
136
|
+
handleImportExport(node.source);
|
|
137
|
+
}
|
|
138
|
+
// we cannot handle dynamic imports here
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
export default rule;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
1
5
|
import { packageVersion } from "../../version.js";
|
|
2
|
-
import
|
|
3
|
-
import RuleNonBreakingSpace from "./rules/non-breaking-space.js";
|
|
6
|
+
import { rules } from "./rules/index.js";
|
|
4
7
|
/**
|
|
5
8
|
* ESLint plugin to enforce consistent translations
|
|
6
9
|
*/
|
|
@@ -9,9 +12,6 @@ const Plugin = {
|
|
|
9
12
|
name: '@nextcloud/l10n-plugin',
|
|
10
13
|
version: packageVersion,
|
|
11
14
|
},
|
|
12
|
-
rules
|
|
13
|
-
'non-breaking-space': RuleNonBreakingSpace,
|
|
14
|
-
'enforce-ellipsis': RuleEllipsis,
|
|
15
|
-
},
|
|
15
|
+
rules,
|
|
16
16
|
};
|
|
17
17
|
export default Plugin;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import * as vueUtils from 'eslint-plugin-vue/lib/utils/index.js';
|
|
6
|
+
import enforceEllipsis from "./enforce-ellipsis.js";
|
|
7
|
+
const defineRule = (r) => r;
|
|
8
|
+
export default defineRule({
|
|
9
|
+
...enforceEllipsis,
|
|
10
|
+
create(context) {
|
|
11
|
+
return vueUtils.defineTemplateBodyVisitor(context, enforceEllipsis.create(context));
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -7,24 +7,30 @@ export default defineRule({
|
|
|
7
7
|
docs: {
|
|
8
8
|
description: 'Enforce consistent usageof ellipsis instead of tripple dots',
|
|
9
9
|
},
|
|
10
|
+
messages: {
|
|
11
|
+
shoudUseEllipsis: 'Translated strings should use ellipsis character instead of triple dots',
|
|
12
|
+
},
|
|
10
13
|
},
|
|
11
14
|
create(context) {
|
|
12
15
|
return {
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
|| node.
|
|
16
|
-
|| node.parent.callee.type !== 'Identifier'
|
|
17
|
-
|| node.parent.callee.name !== 't') {
|
|
16
|
+
CallExpression(node) {
|
|
17
|
+
if (node.callee.type !== 'Identifier'
|
|
18
|
+
|| (node.callee.name !== 't' && node.callee.name !== 'n')) {
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
for (const argument of node.arguments) {
|
|
22
|
+
if (argument.type !== 'Literal') {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (argument.raw?.match(/(?<=[^.])\.\.\.(?!\.)/)) {
|
|
26
|
+
context.report({
|
|
27
|
+
node,
|
|
28
|
+
messageId: 'shoudUseEllipsis',
|
|
29
|
+
fix(fixer) {
|
|
30
|
+
return fixer.replaceText(argument, argument.raw.replaceAll(/(?<=[^.])\.\.\.(?!\.)/g, '…'));
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
28
34
|
}
|
|
29
35
|
},
|
|
30
36
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
export declare const rules: {
|
|
6
|
+
'enforce-ellipsis': import("eslint").Rule.RuleModule;
|
|
7
|
+
'enforce-ellipsis-vue': import("eslint").Rule.RuleModule;
|
|
8
|
+
'non-breaking-space': import("eslint").Rule.RuleModule;
|
|
9
|
+
'non-breaking-space-vue': import("eslint").Rule.RuleModule;
|
|
10
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import enforceEllipsisVue from "./enforce-ellipsis-vue.js";
|
|
6
|
+
import enforceEllipsis from "./enforce-ellipsis.js";
|
|
7
|
+
import nonBreakingSpaceVue from "./non-breaking-space-vue.js";
|
|
8
|
+
import nonBreakingSpace from "./non-breaking-space.js";
|
|
9
|
+
export const rules = {
|
|
10
|
+
'enforce-ellipsis': enforceEllipsis,
|
|
11
|
+
'enforce-ellipsis-vue': enforceEllipsisVue,
|
|
12
|
+
'non-breaking-space': nonBreakingSpace,
|
|
13
|
+
'non-breaking-space-vue': nonBreakingSpaceVue,
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import * as vueUtils from 'eslint-plugin-vue/lib/utils/index.js';
|
|
6
|
+
import nonBreakingSpace from "./non-breaking-space.js";
|
|
7
|
+
const defineRule = (r) => r;
|
|
8
|
+
export default defineRule({
|
|
9
|
+
...nonBreakingSpace,
|
|
10
|
+
create(context) {
|
|
11
|
+
return vueUtils.defineTemplateBodyVisitor(context, nonBreakingSpace.create(context));
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -7,6 +7,9 @@ export default defineRule({
|
|
|
7
7
|
docs: {
|
|
8
8
|
description: 'Enforce non-breaking spaces before ellipsis',
|
|
9
9
|
},
|
|
10
|
+
messages: {
|
|
11
|
+
precedeWithNonbreakingSpace: 'Ellipsis must be preceded by non-breaking spaces',
|
|
12
|
+
},
|
|
10
13
|
},
|
|
11
14
|
create(context) {
|
|
12
15
|
return {
|
|
@@ -18,7 +21,7 @@ export default defineRule({
|
|
|
18
21
|
if (matches && matches[1] !== ' ') {
|
|
19
22
|
context.report({
|
|
20
23
|
node,
|
|
21
|
-
|
|
24
|
+
messageId: 'precedeWithNonbreakingSpace',
|
|
22
25
|
fix(fixer) {
|
|
23
26
|
return fixer.replaceText(node, node.raw.replaceAll(/\s+…/g, ' …'));
|
|
24
27
|
},
|
|
@@ -13,8 +13,21 @@ declare const _default: {
|
|
|
13
13
|
type: "problem";
|
|
14
14
|
fixable: "code";
|
|
15
15
|
messages: {
|
|
16
|
+
outdatedVueLibrary: string;
|
|
16
17
|
useTypeInstead: string;
|
|
17
18
|
useVariantInstead: string;
|
|
19
|
+
useDisableSwipeForNavInstead: string;
|
|
20
|
+
useHideStatusInstead: string;
|
|
21
|
+
useVerboseStatusInstead: string;
|
|
22
|
+
useNoPlaceholderInstead: string;
|
|
23
|
+
useFormatInstead: string;
|
|
24
|
+
useTypeDateRangeInstead: string;
|
|
25
|
+
useNoCloseInstead: string;
|
|
26
|
+
useDisableSwipeForModalInstead: string;
|
|
27
|
+
useNoFocusTrapInstead: string;
|
|
28
|
+
useKeepOpenInstead: string;
|
|
29
|
+
useNcSelectUsersInstead: string;
|
|
30
|
+
removeAriaHidden: string;
|
|
18
31
|
};
|
|
19
32
|
};
|
|
20
33
|
create(context: import("eslint").Rule.RuleContext): any;
|
|
@@ -8,8 +8,21 @@ export declare const rules: {
|
|
|
8
8
|
type: "problem";
|
|
9
9
|
fixable: "code";
|
|
10
10
|
messages: {
|
|
11
|
+
outdatedVueLibrary: string;
|
|
11
12
|
useTypeInstead: string;
|
|
12
13
|
useVariantInstead: string;
|
|
14
|
+
useDisableSwipeForNavInstead: string;
|
|
15
|
+
useHideStatusInstead: string;
|
|
16
|
+
useVerboseStatusInstead: string;
|
|
17
|
+
useNoPlaceholderInstead: string;
|
|
18
|
+
useFormatInstead: string;
|
|
19
|
+
useTypeDateRangeInstead: string;
|
|
20
|
+
useNoCloseInstead: string;
|
|
21
|
+
useDisableSwipeForModalInstead: string;
|
|
22
|
+
useNoFocusTrapInstead: string;
|
|
23
|
+
useKeepOpenInstead: string;
|
|
24
|
+
useNcSelectUsersInstead: string;
|
|
25
|
+
removeAriaHidden: string;
|
|
13
26
|
};
|
|
14
27
|
};
|
|
15
28
|
create(context: import("eslint").Rule.RuleContext): any;
|
|
@@ -13,7 +13,10 @@ const rule = {
|
|
|
13
13
|
recommended: true,
|
|
14
14
|
},
|
|
15
15
|
fixable: 'code',
|
|
16
|
-
|
|
16
|
+
messages: {
|
|
17
|
+
outdatedVueLibrary: 'Installed @nextcloud/vue library is outdated and does not support all reported errors. Install latest compatible version',
|
|
18
|
+
deprecatedDist: 'Import from "@nextcloud/vue/dist" is deprecated',
|
|
19
|
+
},
|
|
17
20
|
},
|
|
18
21
|
create(context) {
|
|
19
22
|
const versionSatisfies = createLibVersionValidator(context);
|
|
@@ -21,17 +24,17 @@ const rule = {
|
|
|
21
24
|
const oldPattern = '@nextcloud/vue/dist/([^/]+)/([^/.]+)';
|
|
22
25
|
return {
|
|
23
26
|
ImportDeclaration: function (node) {
|
|
24
|
-
if (!isVersionValid) {
|
|
25
|
-
// Can't fix, ignore the rule
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
27
|
const importPath = node.source.value;
|
|
29
28
|
const match = importPath.match(new RegExp(oldPattern));
|
|
30
29
|
if (match) {
|
|
30
|
+
if (!isVersionValid) {
|
|
31
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
31
34
|
const newImportPath = `'@nextcloud/vue/${match[1].toLowerCase()}/${match[2]}'`;
|
|
32
35
|
context.report({
|
|
33
36
|
node,
|
|
34
|
-
|
|
37
|
+
messageId: 'deprecatedDist',
|
|
35
38
|
fix(fixer) {
|
|
36
39
|
return fixer.replaceText(node.source, newImportPath);
|
|
37
40
|
},
|
|
@@ -11,8 +11,21 @@ declare const _default: {
|
|
|
11
11
|
type: "problem";
|
|
12
12
|
fixable: "code";
|
|
13
13
|
messages: {
|
|
14
|
+
outdatedVueLibrary: string;
|
|
14
15
|
useTypeInstead: string;
|
|
15
16
|
useVariantInstead: string;
|
|
17
|
+
useDisableSwipeForNavInstead: string;
|
|
18
|
+
useHideStatusInstead: string;
|
|
19
|
+
useVerboseStatusInstead: string;
|
|
20
|
+
useNoPlaceholderInstead: string;
|
|
21
|
+
useFormatInstead: string;
|
|
22
|
+
useTypeDateRangeInstead: string;
|
|
23
|
+
useNoCloseInstead: string;
|
|
24
|
+
useDisableSwipeForModalInstead: string;
|
|
25
|
+
useNoFocusTrapInstead: string;
|
|
26
|
+
useKeepOpenInstead: string;
|
|
27
|
+
useNcSelectUsersInstead: string;
|
|
28
|
+
removeAriaHidden: string;
|
|
16
29
|
};
|
|
17
30
|
};
|
|
18
31
|
create(context: Rule.RuleContext): any;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as vueUtils from 'eslint-plugin-vue/lib/utils/index.js';
|
|
2
|
+
import { createLibVersionValidator } from "../utils/lib-version-parser.js";
|
|
2
3
|
export default {
|
|
3
4
|
meta: {
|
|
4
5
|
docs: {
|
|
@@ -7,14 +8,49 @@ export default {
|
|
|
7
8
|
type: 'problem',
|
|
8
9
|
fixable: 'code',
|
|
9
10
|
messages: {
|
|
11
|
+
outdatedVueLibrary: 'Installed @nextcloud/vue library is outdated and does not support all reported errors. Install latest compatible version',
|
|
10
12
|
useTypeInstead: 'Using `native-type` for button variant is deprecated - use `type` instead.',
|
|
11
13
|
useVariantInstead: 'Using `type` for button variant is deprecated - use `variant` instead.',
|
|
14
|
+
useDisableSwipeForNavInstead: 'Using `allow-swipe-navigation` is deprecated - use `disable-swipe` instead',
|
|
15
|
+
useHideStatusInstead: 'Using `show-user-status` is deprecated - use `hide-status` instead',
|
|
16
|
+
useVerboseStatusInstead: 'Using `show-user-status-compact` is deprecated - use `verbose-status` instead',
|
|
17
|
+
useNoPlaceholderInstead: 'Using `allow-placeholder` is deprecated - use `no-placeholder` instead',
|
|
18
|
+
useFormatInstead: 'Using `formatter` is deprecated - use `format` instead',
|
|
19
|
+
useTypeDateRangeInstead: 'Using `range` is deprecated - use `type` with `date-range` or `datetime-range` instead',
|
|
20
|
+
useNoCloseInstead: 'Using `can-close` is deprecated - use `no-close` instead',
|
|
21
|
+
useDisableSwipeForModalInstead: 'Using `enable-swipe` is deprecated - use `disable-swipe` instead',
|
|
22
|
+
useNoFocusTrapInstead: 'Using `focus-trap` is deprecated - use `no-focus-trap` instead',
|
|
23
|
+
useKeepOpenInstead: 'Using `close-on-select` is deprecated - use `keep-open` instead',
|
|
24
|
+
useNcSelectUsersInstead: 'Using `user-select` is deprecated - use `NcSelectUsers` component instead',
|
|
25
|
+
removeAriaHidden: 'Using `aria-hidden` is deprecated - remove prop from components, otherwise root element will inherit incorrect attribute.',
|
|
12
26
|
},
|
|
13
27
|
},
|
|
14
28
|
create(context) {
|
|
29
|
+
const versionSatisfies = createLibVersionValidator(context);
|
|
30
|
+
const isAriaHiddenValid = versionSatisfies('8.2.0'); // #4835
|
|
31
|
+
const isDisableSwipeValid = versionSatisfies('8.23.0'); // #6452
|
|
32
|
+
const isVariantTypeValid = versionSatisfies('8.24.0'); // #6472
|
|
33
|
+
const isDefaultBooleanFalseValid = versionSatisfies('8.24.0'); // #6656
|
|
34
|
+
const isDateTimePickerFormatValid = versionSatisfies('8.25.0'); // #6738
|
|
35
|
+
const isNcSelectKeepOpenValid = versionSatisfies('8.25.0'); // #6791
|
|
36
|
+
const isNcPopoverNoFocusTrapValid = versionSatisfies('8.26.0'); // #6808
|
|
37
|
+
const isNcSelectUsersValid = versionSatisfies('8.27.1'); // #7032
|
|
15
38
|
const legacyTypes = ['primary', 'error', 'warning', 'success', 'secondary', 'tertiary', 'tertiary-no-background'];
|
|
16
39
|
return vueUtils.defineTemplateBodyVisitor(context, {
|
|
17
|
-
'VElement
|
|
40
|
+
'VElement VAttribute:has(VIdentifier[name="type"])': function (node) {
|
|
41
|
+
if (![
|
|
42
|
+
'ncactions',
|
|
43
|
+
'ncappnavigationnew',
|
|
44
|
+
'ncbutton',
|
|
45
|
+
'ncchip',
|
|
46
|
+
'ncdialogbutton',
|
|
47
|
+
].includes(node.parent.parent.name)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!isVariantTypeValid) {
|
|
51
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
18
54
|
const hasNativeType = node.parent.attributes.find((attr) => (attr.key.name === 'native-type'
|
|
19
55
|
|| (attr.key.type === 'VDirectiveKey' && attr.key.argument && attr.key.argument.name === 'native-type')));
|
|
20
56
|
const isLiteral = node.value.type === 'VLiteral' && legacyTypes.includes(node.value.value);
|
|
@@ -42,7 +78,17 @@ export default {
|
|
|
42
78
|
});
|
|
43
79
|
}
|
|
44
80
|
},
|
|
45
|
-
'VElement
|
|
81
|
+
'VElement VAttribute:has(VIdentifier[name="native-type"])': function (node) {
|
|
82
|
+
if (![
|
|
83
|
+
'ncbutton',
|
|
84
|
+
'ncdialogbutton',
|
|
85
|
+
].includes(node.parent.parent.name)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!isVariantTypeValid) {
|
|
89
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
46
92
|
context.report({
|
|
47
93
|
node,
|
|
48
94
|
messageId: 'useTypeInstead',
|
|
@@ -56,6 +102,135 @@ export default {
|
|
|
56
102
|
},
|
|
57
103
|
});
|
|
58
104
|
},
|
|
105
|
+
'VElement VAttribute:has(VIdentifier[name="aria-hidden"])': function (node) {
|
|
106
|
+
if (node.parent.parent.name.startsWith('ncaction')
|
|
107
|
+
|| node.parent.parent.name === 'ncbutton') {
|
|
108
|
+
if (!isAriaHiddenValid) {
|
|
109
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
context.report({
|
|
113
|
+
node,
|
|
114
|
+
messageId: 'removeAriaHidden',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
'VElement[name="ncappcontent"] VAttribute:has(VIdentifier[name="allow-swipe-navigation"])': function (node) {
|
|
119
|
+
if (!isDisableSwipeValid) {
|
|
120
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
context.report({
|
|
124
|
+
node,
|
|
125
|
+
messageId: 'useDisableSwipeForNavInstead',
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="show-user-status"])': function (node) {
|
|
129
|
+
if (!isDefaultBooleanFalseValid) {
|
|
130
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
context.report({
|
|
134
|
+
node,
|
|
135
|
+
messageId: 'useHideStatusInstead',
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="show-user-status-compact"])': function (node) {
|
|
139
|
+
if (!isDefaultBooleanFalseValid) {
|
|
140
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
context.report({
|
|
144
|
+
node,
|
|
145
|
+
messageId: 'useVerboseStatusInstead',
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
'VElement[name="ncavatar"] VAttribute:has(VIdentifier[name="allow-placeholder"])': function (node) {
|
|
149
|
+
if (!isDefaultBooleanFalseValid) {
|
|
150
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
context.report({
|
|
154
|
+
node,
|
|
155
|
+
messageId: 'useNoPlaceholderInstead',
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
'VElement[name="ncdatetimepicker"] VAttribute:has(VIdentifier[name="formatter"])': function (node) {
|
|
159
|
+
if (!isDateTimePickerFormatValid) {
|
|
160
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
context.report({
|
|
164
|
+
node,
|
|
165
|
+
messageId: 'useFormatInstead',
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
'VElement[name="ncdatetimepicker"] VAttribute:has(VIdentifier[name="range"])': function (node) {
|
|
169
|
+
if (!isDateTimePickerFormatValid) {
|
|
170
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
context.report({
|
|
174
|
+
node,
|
|
175
|
+
messageId: 'useTypeDateRangeInstead',
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
'VElement VAttribute:has(VIdentifier[name="can-close"])': function (node) {
|
|
179
|
+
if (![
|
|
180
|
+
'ncdialog',
|
|
181
|
+
'ncmodal',
|
|
182
|
+
].includes(node.parent.parent.name)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!isDefaultBooleanFalseValid) {
|
|
186
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
context.report({
|
|
190
|
+
node,
|
|
191
|
+
messageId: 'useNoCloseInstead',
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
'VElement[name="ncmodal"] VAttribute:has(VIdentifier[name="enable-swipe"])': function (node) {
|
|
195
|
+
if (!isDisableSwipeValid) {
|
|
196
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
context.report({
|
|
200
|
+
node,
|
|
201
|
+
messageId: 'useDisableSwipeForModalInstead',
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
'VElement[name="ncpopover"] VAttribute:has(VIdentifier[name="focus-trap"])': function (node) {
|
|
205
|
+
if (!isNcPopoverNoFocusTrapValid) {
|
|
206
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
context.report({
|
|
210
|
+
node,
|
|
211
|
+
messageId: 'useNoFocusTrapInstead',
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
'VElement[name="ncselect"] VAttribute:has(VIdentifier[name="close-on-select"])': function (node) {
|
|
215
|
+
if (!isNcSelectKeepOpenValid) {
|
|
216
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
context.report({
|
|
220
|
+
node,
|
|
221
|
+
messageId: 'useKeepOpenInstead',
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
'VElement[name="ncselect"] VAttribute:has(VIdentifier[name="user-select"])': function (node) {
|
|
225
|
+
if (!isNcSelectUsersValid) {
|
|
226
|
+
context.report({ node, messageId: 'outdatedVueLibrary' });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
context.report({
|
|
230
|
+
node,
|
|
231
|
+
messageId: 'useNcSelectUsersInstead',
|
|
232
|
+
});
|
|
233
|
+
},
|
|
59
234
|
});
|
|
60
235
|
},
|
|
61
236
|
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { Linter } from 'eslint';
|
|
|
12
12
|
export declare function restrictConfigFiles(configs: Linter.Config[], files: string[]): {
|
|
13
13
|
files: (string | string[])[];
|
|
14
14
|
name?: string;
|
|
15
|
+
basePath?: string;
|
|
15
16
|
ignores?: string[];
|
|
16
17
|
language?: string;
|
|
17
18
|
languageOptions?: Linter.LanguageOptions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextcloud/eslint-config",
|
|
3
|
-
"version": "9.0.0-rc.
|
|
3
|
+
"version": "9.0.0-rc.4",
|
|
4
4
|
"description": "Eslint shared config for nextcloud apps and libraries",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -43,24 +43,25 @@
|
|
|
43
43
|
"test": "vitest run"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@eslint/json": "^0.
|
|
47
|
-
"@stylistic/eslint-plugin": "^
|
|
46
|
+
"@eslint/json": "^0.13.1",
|
|
47
|
+
"@stylistic/eslint-plugin": "^5.2.2",
|
|
48
|
+
"eslint-config-flat-gitignore": "^2.1.0",
|
|
48
49
|
"eslint-plugin-antfu": "^3.1.1",
|
|
49
|
-
"eslint-plugin-jsdoc": "^
|
|
50
|
-
"eslint-plugin-perfectionist": "^4.
|
|
51
|
-
"eslint-plugin-vue": "^10.
|
|
52
|
-
"fast-xml-parser": "^5.2.
|
|
53
|
-
"globals": "^16.
|
|
50
|
+
"eslint-plugin-jsdoc": "^51.2.3",
|
|
51
|
+
"eslint-plugin-perfectionist": "^4.15.0",
|
|
52
|
+
"eslint-plugin-vue": "^10.3.0",
|
|
53
|
+
"fast-xml-parser": "^5.2.5",
|
|
54
|
+
"globals": "^16.3.0",
|
|
54
55
|
"semver": "^7.7.2",
|
|
55
|
-
"sort-package-json": "^3.
|
|
56
|
-
"typescript-eslint": "^8.
|
|
56
|
+
"sort-package-json": "^3.4.0",
|
|
57
|
+
"typescript-eslint": "^8.38.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
|
-
"@types/node": "^
|
|
60
|
+
"@types/node": "^24.1.0",
|
|
60
61
|
"@types/semver": "^7.7.0",
|
|
61
|
-
"eslint": "^9.
|
|
62
|
-
"memfs": "^4.
|
|
63
|
-
"vitest": "^3.2.
|
|
62
|
+
"eslint": "^9.32.0",
|
|
63
|
+
"memfs": "^4.24.0",
|
|
64
|
+
"vitest": "^3.2.4"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
67
|
"eslint": ">=9"
|
|
@@ -69,10 +70,12 @@
|
|
|
69
70
|
"node": "^20.19 || ^22 || ^24"
|
|
70
71
|
},
|
|
71
72
|
"devEngines": {
|
|
72
|
-
"packageManager":
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
"packageManager": [
|
|
74
|
+
{
|
|
75
|
+
"name": "npm",
|
|
76
|
+
"version": "^10"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
76
79
|
"runtime": {
|
|
77
80
|
"name": "node",
|
|
78
81
|
"version": "^22.10"
|