@nextcloud/eslint-config 9.0.0-rc.0 → 9.0.0-rc.10

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +133 -44
  2. package/README.md +139 -33
  3. package/dist/configs/codeStyle.d.ts +13 -0
  4. package/dist/configs/codeStyle.js +176 -0
  5. package/dist/configs/documentation.d.ts +12 -0
  6. package/dist/configs/documentation.js +117 -0
  7. package/dist/configs/filesystem.d.ts +9 -0
  8. package/dist/configs/filesystem.js +25 -0
  9. package/dist/configs/imports.d.ts +12 -0
  10. package/dist/configs/imports.js +111 -0
  11. package/dist/configs/javascript.d.ts +14 -0
  12. package/dist/configs/javascript.js +118 -0
  13. package/dist/configs/json.d.ts +9 -0
  14. package/dist/configs/json.js +45 -0
  15. package/dist/configs/node.d.ts +10 -0
  16. package/dist/configs/node.js +24 -0
  17. package/dist/configs/typescript.d.ts +12 -0
  18. package/dist/configs/typescript.js +60 -0
  19. package/dist/configs/vue.d.ts +12 -0
  20. package/dist/configs/vue.js +158 -0
  21. package/dist/configs/vue2.d.ts +12 -0
  22. package/dist/configs/vue2.js +30 -0
  23. package/dist/configs/vue3.d.ts +12 -0
  24. package/dist/configs/vue3.js +48 -0
  25. package/dist/globs.d.ts +20 -0
  26. package/dist/globs.js +41 -0
  27. package/dist/index.d.ts +31 -31
  28. package/dist/index.js +81 -0
  29. package/dist/plugins/import-extensions/index.d.ts +15 -0
  30. package/dist/plugins/import-extensions/index.js +13 -0
  31. package/dist/plugins/import-extensions/rules/ban-inline-type-imports.d.ts +7 -0
  32. package/dist/plugins/import-extensions/rules/ban-inline-type-imports.js +176 -0
  33. package/dist/plugins/import-extensions/rules/extensions.d.ts +11 -0
  34. package/dist/plugins/import-extensions/rules/extensions.js +143 -0
  35. package/dist/plugins/import-extensions/rules/index.d.ts +8 -0
  36. package/dist/plugins/import-extensions/rules/index.js +10 -0
  37. package/dist/plugins/nextcloud/index.d.ts +7 -0
  38. package/dist/plugins/nextcloud/index.js +9 -0
  39. package/dist/plugins/nextcloud/rules/index.d.ts +6 -0
  40. package/dist/plugins/nextcloud/rules/index.js +18 -0
  41. package/dist/plugins/nextcloud/rules/l10n-enforce-ellipsis.d.ts +7 -0
  42. package/dist/plugins/nextcloud/rules/l10n-enforce-ellipsis.js +44 -0
  43. package/dist/plugins/nextcloud/rules/l10n-non-breaking-space.d.ts +7 -0
  44. package/dist/plugins/nextcloud/rules/l10n-non-breaking-space.js +39 -0
  45. package/dist/plugins/nextcloud/rules/no-deprecated-globals.d.ts +7 -0
  46. package/dist/plugins/nextcloud/rules/no-deprecated-globals.js +209 -0
  47. package/dist/plugins/nextcloud/rules/no-deprecated-library-exports.d.ts +7 -0
  48. package/dist/plugins/nextcloud/rules/no-deprecated-library-exports.js +89 -0
  49. package/dist/plugins/nextcloud/rules/no-deprecated-library-props.d.ts +41 -0
  50. package/dist/plugins/nextcloud/rules/no-deprecated-library-props.js +429 -0
  51. package/dist/plugins/nextcloud/rules/no-removed-globals.d.ts +7 -0
  52. package/dist/plugins/nextcloud/rules/no-removed-globals.js +203 -0
  53. package/dist/plugins/nextcloud/utils/app-version-parser.d.ts +41 -0
  54. package/dist/plugins/nextcloud/utils/app-version-parser.js +106 -0
  55. package/dist/plugins/nextcloud/utils/lib-version-parser.d.ts +18 -0
  56. package/dist/plugins/nextcloud/utils/lib-version-parser.js +49 -0
  57. package/dist/plugins/nextcloud/utils/vue-template-visitor.d.ts +26 -0
  58. package/dist/plugins/nextcloud/utils/vue-template-visitor.js +38 -0
  59. package/dist/plugins/packageJson.d.ts +10 -0
  60. package/dist/plugins/packageJson.js +66 -0
  61. package/dist/utils.d.ts +12 -0
  62. package/dist/utils.js +19 -0
  63. package/dist/version.d.ts +1 -0
  64. package/dist/version.js +6 -0
  65. package/package.json +35 -27
  66. package/dist/index.mjs +0 -1464
  67. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,106 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import { XMLParser } from 'fast-xml-parser';
6
+ import { lstatSync, readFileSync } from 'node:fs';
7
+ import { dirname, isAbsolute, resolve, sep } from 'node:path';
8
+ import { lte, valid } from 'semver';
9
+ /**
10
+ * Check if a given path exists and is a directory
11
+ *
12
+ * @param filePath The path
13
+ */
14
+ export function isDirectory(filePath) {
15
+ const stats = lstatSync(filePath, { throwIfNoEntry: false });
16
+ return stats !== undefined && stats.isDirectory();
17
+ }
18
+ /**
19
+ * Check if a given path exists and is a directory
20
+ *
21
+ * @param filePath The path
22
+ */
23
+ export function isFile(filePath) {
24
+ const stats = lstatSync(filePath, { throwIfNoEntry: false });
25
+ return stats !== undefined && stats.isFile();
26
+ }
27
+ /**
28
+ * Find the path of nearest `appinfo/info.xml` relative to given path
29
+ *
30
+ * @param currentPath Path to lookup
31
+ * @return Either the full path including the `info.xml` part or `undefined` if no found
32
+ */
33
+ export function findAppinfo(currentPath) {
34
+ while (currentPath && currentPath !== sep) {
35
+ const appinfoPath = `${currentPath}${sep}appinfo`;
36
+ if (isDirectory(appinfoPath)
37
+ && isFile(`${appinfoPath}${sep}info.xml`)) {
38
+ return `${appinfoPath}${sep}info.xml`;
39
+ }
40
+ currentPath = resolve(currentPath, '..');
41
+ }
42
+ return undefined;
43
+ }
44
+ /**
45
+ * Make sure that versions like '25' can be handled by semver
46
+ *
47
+ * @param version The pure version string
48
+ * @return Sanitized version string
49
+ */
50
+ export function sanitizeTargetVersion(version) {
51
+ let sanitizedVersion = version;
52
+ const sections = sanitizedVersion.split('.').length;
53
+ if (sections < 3) {
54
+ sanitizedVersion = sanitizedVersion + '.0'.repeat(3 - sections);
55
+ }
56
+ // now version should look like '25.0.0'
57
+ if (!valid(sanitizedVersion)) {
58
+ throw Error(`[@nextcloud/eslint-plugin] Invalid target version ${version} found`);
59
+ }
60
+ return sanitizedVersion;
61
+ }
62
+ /**
63
+ * Create a callback that takes a version number an checks if the version
64
+ * is valid compared to configured version / detected version.
65
+ *
66
+ * @param options Options
67
+ * @param options.cwd The current working directory
68
+ * @param options.physicalFilename The real filename where ESLint is linting currently
69
+ * @param options.options The plugin options
70
+ */
71
+ export function createVersionValidator({ cwd, physicalFilename, options }) {
72
+ const settings = options[0];
73
+ if (settings?.targetVersion) {
74
+ // check if the rule version is lower than the current target version
75
+ const maxVersion = sanitizeTargetVersion(settings.targetVersion);
76
+ return (version) => lte(version, maxVersion);
77
+ }
78
+ // Try to find appinfo and parse the supported version
79
+ if (settings?.parseAppInfo !== false) {
80
+ // Current working directory, either the filename (can be empty) or the cwd property
81
+ const currentDirectory = isAbsolute(physicalFilename)
82
+ ? resolve(dirname(physicalFilename))
83
+ : dirname(resolve(cwd, physicalFilename));
84
+ // The nearest appinfo
85
+ const appinfoPath = findAppinfo(currentDirectory);
86
+ if (appinfoPath) {
87
+ const parser = new XMLParser({
88
+ attributeNamePrefix: '@',
89
+ ignoreAttributes: false,
90
+ });
91
+ const xml = parser.parse(readFileSync(appinfoPath));
92
+ let maxVersion = xml?.info?.dependencies?.nextcloud?.['@max-version'];
93
+ if (typeof maxVersion !== 'string') {
94
+ throw new Error(`[@nextcloud/eslint-plugin] AppInfo does not contain a max-version (location: ${appinfoPath})`);
95
+ }
96
+ maxVersion = sanitizeTargetVersion(maxVersion);
97
+ return (version) => lte(version, maxVersion);
98
+ }
99
+ if (settings?.parseAppInfo === true) {
100
+ // User enforced app info parsing, so we throw an error - otherwise just fallback to default
101
+ throw new Error('[@nextcloud/eslint-plugin] AppInfo parsing was enabled, but no `appinfo/info.xml` was found.');
102
+ }
103
+ }
104
+ // If not configured or parsing is disabled, every rule should be handled
105
+ return () => true;
106
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Create a callback that takes a version number and checks if the version
3
+ * is valid compared to configured version / detected version.
4
+ *
5
+ * @param options Options
6
+ * @param options.cwd The current working directory
7
+ * @param options.physicalFilename The real filename where ESLint is linting currently
8
+ * @param importResolve Optional custom import resolver function (for testing purposes)
9
+ * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
10
+ */
11
+ 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);
15
+ /**
16
+ * Clear the module cache
17
+ */
18
+ export declare function clearCache(): void;
@@ -0,0 +1,49 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import { readFileSync } from 'node:fs';
6
+ import { isAbsolute, join, resolve } from 'node:path';
7
+ import { fileURLToPath, pathToFileURL } from 'node:url';
8
+ import { gte } from 'semver';
9
+ /**
10
+ * Cached map of paths: Reco <path_to_package.json, validator>
11
+ */
12
+ const cachedMap = new Map();
13
+ /**
14
+ * Create a callback that takes a version number and checks if the version
15
+ * is valid compared to configured version / detected version.
16
+ *
17
+ * @param options Options
18
+ * @param options.cwd The current working directory
19
+ * @param options.physicalFilename The real filename where ESLint is linting currently
20
+ * @param importResolve Optional custom import resolver function (for testing purposes)
21
+ * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
22
+ */
23
+ export function createLibVersionValidator({ cwd, physicalFilename }, importResolve = import.meta.resolve) {
24
+ // Try to find package.json of the nextcloud-vue package
25
+ const sourceFile = isAbsolute(physicalFilename)
26
+ ? resolve(physicalFilename)
27
+ : resolve(cwd, physicalFilename);
28
+ let packageJsonDir;
29
+ try {
30
+ const modulePath = fileURLToPath(importResolve('@nextcloud/vue', pathToFileURL(sourceFile)));
31
+ const idx = modulePath.lastIndexOf('/dist/');
32
+ packageJsonDir = modulePath.substring(0, idx);
33
+ }
34
+ catch {
35
+ return () => false;
36
+ }
37
+ if (!cachedMap[packageJsonDir]) {
38
+ const json = JSON.parse(readFileSync(join(packageJsonDir, 'package.json'), 'utf-8'));
39
+ const libVersion = json.version;
40
+ cachedMap[packageJsonDir] = (version) => gte(libVersion, version);
41
+ }
42
+ return cachedMap[packageJsonDir];
43
+ }
44
+ /**
45
+ * Clear the module cache
46
+ */
47
+ export function clearCache() {
48
+ cachedMap.clear();
49
+ }
@@ -0,0 +1,26 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import type { Rule } from 'eslint';
6
+ type TemplateVisitor = {
7
+ [key: string]: (...args: unknown[]) => void;
8
+ };
9
+ type TemplateVisitorOptions = {
10
+ templateBodyTriggerSelector: 'Program' | 'Program:exit';
11
+ };
12
+ /**
13
+ * Register the given visitor to parser services.
14
+ * If the parser service of `vue-eslint-parser` was not found,
15
+ * this generates a warning.
16
+ *
17
+ * @param context - The rule context to use parser services.
18
+ * @param templateBodyVisitor - The visitor to traverse the template body.
19
+ * @param scriptVisitor - The visitor to traverse the script.
20
+ * @param options - The options.
21
+ *
22
+ * @return The merged visitor.
23
+ * @see https://github.com/vuejs/eslint-plugin-vue/blob/745fd4e1f3719c3a2f93bd3531da5e886c16f008/lib/utils/index.js#L2281-L2315
24
+ */
25
+ export declare function defineTemplateBodyVisitor(context: Rule.RuleContext, templateBodyVisitor: TemplateVisitor, scriptVisitor?: Rule.RuleListener, options?: TemplateVisitorOptions): Rule.RuleListener;
26
+ export {};
@@ -0,0 +1,38 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import { extname } from 'node:path';
6
+ /**
7
+ * Register the given visitor to parser services.
8
+ * If the parser service of `vue-eslint-parser` was not found,
9
+ * this generates a warning.
10
+ *
11
+ * @param context - The rule context to use parser services.
12
+ * @param templateBodyVisitor - The visitor to traverse the template body.
13
+ * @param scriptVisitor - The visitor to traverse the script.
14
+ * @param options - The options.
15
+ *
16
+ * @return The merged visitor.
17
+ * @see https://github.com/vuejs/eslint-plugin-vue/blob/745fd4e1f3719c3a2f93bd3531da5e886c16f008/lib/utils/index.js#L2281-L2315
18
+ */
19
+ export function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor, options) {
20
+ const sourceCode = context.sourceCode;
21
+ if (!sourceCode.parserServices?.defineTemplateBodyVisitor) {
22
+ const filename = context.filename;
23
+ if (extname(filename) === '.vue') {
24
+ context.report({
25
+ loc: {
26
+ line: 1,
27
+ column: 0,
28
+ },
29
+ message: 'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.',
30
+ });
31
+ }
32
+ else {
33
+ return scriptVisitor ?? {};
34
+ }
35
+ }
36
+ return sourceCode.parserServices
37
+ .defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor, options);
38
+ }
@@ -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
+ import type { ESLint } from 'eslint';
6
+ /**
7
+ * ESLint plugin to sort package.json files in a consistent way.
8
+ */
9
+ declare const Plugin: ESLint.Plugin;
10
+ export default Plugin;
@@ -0,0 +1,66 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import path from 'node:path';
6
+ import sortPackageJson from 'sort-package-json';
7
+ import { packageVersion } from "../version.js";
8
+ const SortPackageJsonRule = {
9
+ meta: {
10
+ fixable: 'code',
11
+ docs: {
12
+ recommended: true,
13
+ description: 'Sort package json in consistent order',
14
+ },
15
+ schema: [
16
+ {
17
+ type: 'object',
18
+ properties: {
19
+ sortOrder: {
20
+ type: 'array',
21
+ minItems: 0,
22
+ items: {
23
+ type: 'string',
24
+ },
25
+ },
26
+ },
27
+ additionalProperties: false,
28
+ },
29
+ ],
30
+ },
31
+ create(context) {
32
+ if (path.basename(context.filename) !== 'package.json') {
33
+ return {};
34
+ }
35
+ const options = context.options ? context.options[0] : undefined;
36
+ return {
37
+ Document({ body }) {
38
+ const sourceCode = context.sourceCode.text;
39
+ const packageJsonText = sourceCode.slice(...body.range);
40
+ const sortedPackageJsonText = sortPackageJson(packageJsonText, options);
41
+ if (packageJsonText !== sortedPackageJsonText) {
42
+ context.report({
43
+ node: body,
44
+ message: 'package.json is not sorted correctly',
45
+ fix(fixer) {
46
+ return fixer.replaceText(body, sortedPackageJsonText);
47
+ },
48
+ });
49
+ }
50
+ },
51
+ };
52
+ },
53
+ };
54
+ /**
55
+ * ESLint plugin to sort package.json files in a consistent way.
56
+ */
57
+ const Plugin = {
58
+ meta: {
59
+ name: '@nextcloud/package-json',
60
+ version: packageVersion,
61
+ },
62
+ rules: {
63
+ 'sort-package-json': SortPackageJsonRule,
64
+ },
65
+ };
66
+ export default Plugin;
@@ -0,0 +1,12 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import type { Linter } from 'eslint';
6
+ /**
7
+ * Restrict a set of configurations to a given glob pattern
8
+ *
9
+ * @param configs The configs to restrict
10
+ * @param files The glob pattern to assign
11
+ */
12
+ export declare function restrictConfigFiles(configs: Linter.Config[], files: string[]): Linter.Config[];
package/dist/utils.js ADDED
@@ -0,0 +1,19 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ /**
6
+ * Restrict a set of configurations to a given glob pattern
7
+ *
8
+ * @param configs The configs to restrict
9
+ * @param files The glob pattern to assign
10
+ */
11
+ export function restrictConfigFiles(configs, files) {
12
+ return configs.map((config) => ({
13
+ ...config,
14
+ files: [
15
+ ...(config.files ?? []),
16
+ ...files,
17
+ ],
18
+ }));
19
+ }
@@ -0,0 +1 @@
1
+ export declare const packageVersion: string;
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import packageJson from '../package.json' with { type: 'json' };
6
+ export const packageVersion = packageJson.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextcloud/eslint-config",
3
- "version": "9.0.0-rc.0",
3
+ "version": "9.0.0-rc.10",
4
4
  "description": "Eslint shared config for nextcloud apps and libraries",
5
5
  "keywords": [
6
6
  "eslint",
@@ -22,7 +22,7 @@
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./dist/index.d.ts",
25
- "default": "./dist/index.mjs"
25
+ "default": "./dist/index.js"
26
26
  }
27
27
  },
28
28
  "main": "dist/index.mjs",
@@ -34,44 +34,52 @@
34
34
  "dist"
35
35
  ],
36
36
  "scripts": {
37
- "build": "vite --mode production build",
38
- "lint": "npm run build && eslint",
39
- "lint:fix": "eslint --fix",
37
+ "build": "npm run build:cleanup && npm run build:source",
38
+ "build:cleanup": "tsc --build --clean",
39
+ "build:source": "tsc",
40
+ "lint": "eslint --flag unstable_native_nodejs_ts_config",
41
+ "lint:fix": "eslint --flag unstable_native_nodejs_ts_config --fix",
42
+ "prerelease:format-changelog": "node build/format-changelog.mjs",
40
43
  "test": "vitest run"
41
44
  },
42
45
  "dependencies": {
43
- "@eslint/json": "^0.11.0",
44
- "@stylistic/eslint-plugin": "^4.2.0",
45
- "eslint-plugin-jsdoc": "^50.6.9",
46
- "eslint-plugin-vue": "^10.0.0",
47
- "fast-xml-parser": "^5.0.9",
48
- "globals": "^16.0.0",
49
- "semver": "^7.7.1",
50
- "sort-package-json": "^3.0.0",
51
- "typescript-eslint": "^8.28.0"
46
+ "@eslint/js": "^10.0.1",
47
+ "@eslint/json": "^2.0.0",
48
+ "@stylistic/eslint-plugin": "^5.10.0",
49
+ "eslint-config-flat-gitignore": "^2.3.0",
50
+ "eslint-plugin-antfu": "^3.2.3",
51
+ "eslint-plugin-jsdoc": "^63.0.4",
52
+ "eslint-plugin-perfectionist": "^5.9.1",
53
+ "eslint-plugin-vue": "^10.9.2",
54
+ "fast-xml-parser": "^5.9.0",
55
+ "globals": "^17.6.0",
56
+ "semver": "^7.8.4",
57
+ "sort-package-json": "^4.0.0",
58
+ "typescript-eslint": "^8.61.1"
52
59
  },
53
60
  "devDependencies": {
54
- "@nextcloud/vite-config": "^2.3.2",
55
- "@types/node": "^22.13.13",
56
- "eslint": "^9.23.0",
57
- "memfs": "^4.17.0",
58
- "vite": "^6.2.3",
59
- "vitest": "^3.0.9"
61
+ "@types/node": "^26.0.0",
62
+ "@types/semver": "^7.7.1",
63
+ "eslint": "^10.5.0",
64
+ "memfs": "^4.57.7",
65
+ "vitest": "^4.1.9"
60
66
  },
61
67
  "peerDependencies": {
62
- "eslint": ">=9"
68
+ "eslint": ">=10"
63
69
  },
64
70
  "engines": {
65
- "node": "^20.9 || ^22"
71
+ "node": "^22.13 || ^24 || >=26"
66
72
  },
67
73
  "devEngines": {
68
- "packageManager": {
69
- "name": "npm",
70
- "version": "^10"
71
- },
74
+ "packageManager": [
75
+ {
76
+ "name": "npm",
77
+ "version": "^11.3.0"
78
+ }
79
+ ],
72
80
  "runtime": {
73
81
  "name": "node",
74
- "version": "^22"
82
+ "version": "^24.3.0"
75
83
  }
76
84
  }
77
85
  }