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

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 (63) hide show
  1. package/CHANGELOG.md +80 -40
  2. package/README.md +142 -29
  3. package/dist/configs/codeStyle.d.ts +13 -0
  4. package/dist/configs/codeStyle.js +184 -0
  5. package/dist/configs/documentation.d.ts +12 -0
  6. package/dist/configs/documentation.js +123 -0
  7. package/dist/configs/filesystem.d.ts +9 -0
  8. package/dist/configs/filesystem.js +20 -0
  9. package/dist/configs/imports.d.ts +12 -0
  10. package/dist/configs/imports.js +114 -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 +152 -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 +26 -0
  25. package/dist/globs.d.ts +20 -0
  26. package/dist/globs.js +39 -0
  27. package/dist/index.d.ts +27 -31
  28. package/dist/index.js +78 -0
  29. package/dist/plugins/l10n/index.d.ts +10 -0
  30. package/dist/plugins/l10n/index.js +17 -0
  31. package/dist/plugins/l10n/rules/enforce-ellipsis.d.ts +7 -0
  32. package/dist/plugins/l10n/rules/enforce-ellipsis.js +32 -0
  33. package/dist/plugins/l10n/rules/non-breaking-space.d.ts +7 -0
  34. package/dist/plugins/l10n/rules/non-breaking-space.js +30 -0
  35. package/dist/plugins/nextcloud/index.d.ts +7 -0
  36. package/dist/plugins/nextcloud/index.js +9 -0
  37. package/dist/plugins/nextcloud/rules/index.d.ts +6 -0
  38. package/dist/plugins/nextcloud/rules/index.js +6 -0
  39. package/dist/plugins/nextcloud/rules/no-deprecations.d.ts +7 -0
  40. package/dist/plugins/nextcloud/rules/no-deprecations.js +196 -0
  41. package/dist/plugins/nextcloud/rules/no-removed-apis.d.ts +7 -0
  42. package/dist/plugins/nextcloud/rules/no-removed-apis.js +152 -0
  43. package/dist/plugins/nextcloud/utils/version-parser.d.ts +41 -0
  44. package/dist/plugins/nextcloud/utils/version-parser.js +106 -0
  45. package/dist/plugins/nextcloud-vue/index.d.ts +28 -0
  46. package/dist/plugins/nextcloud-vue/index.js +13 -0
  47. package/dist/plugins/nextcloud-vue/rules/index.d.ts +17 -0
  48. package/dist/plugins/nextcloud-vue/rules/index.js +10 -0
  49. package/dist/plugins/nextcloud-vue/rules/no-deprecated-exports.d.ts +7 -0
  50. package/dist/plugins/nextcloud-vue/rules/no-deprecated-exports.js +44 -0
  51. package/dist/plugins/nextcloud-vue/rules/no-deprecated-props.d.ts +20 -0
  52. package/dist/plugins/nextcloud-vue/rules/no-deprecated-props.js +61 -0
  53. package/dist/plugins/nextcloud-vue/utils/lib-version-parser.d.ts +33 -0
  54. package/dist/plugins/nextcloud-vue/utils/lib-version-parser.js +94 -0
  55. package/dist/plugins/packageJson.d.ts +10 -0
  56. package/dist/plugins/packageJson.js +66 -0
  57. package/dist/utils.d.ts +23 -0
  58. package/dist/utils.js +19 -0
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +6 -0
  61. package/package.json +26 -22
  62. package/dist/index.mjs +0 -1464
  63. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,61 @@
1
+ import * as vueUtils from 'eslint-plugin-vue/lib/utils/index.js';
2
+ export default {
3
+ meta: {
4
+ docs: {
5
+ description: 'Deprecated `@nextcloud/vue` properties',
6
+ },
7
+ type: 'problem',
8
+ fixable: 'code',
9
+ messages: {
10
+ useTypeInstead: 'Using `native-type` for button variant is deprecated - use `type` instead.',
11
+ useVariantInstead: 'Using `type` for button variant is deprecated - use `variant` instead.',
12
+ },
13
+ },
14
+ create(context) {
15
+ const legacyTypes = ['primary', 'error', 'warning', 'success', 'secondary', 'tertiary', 'tertiary-no-background'];
16
+ return vueUtils.defineTemplateBodyVisitor(context, {
17
+ 'VElement[name="ncbutton"] VAttribute:has(VIdentifier[name="type"])': function (node) {
18
+ const hasNativeType = node.parent.attributes.find((attr) => (attr.key.name === 'native-type'
19
+ || (attr.key.type === 'VDirectiveKey' && attr.key.argument && attr.key.argument.name === 'native-type')));
20
+ const isLiteral = node.value.type === 'VLiteral' && legacyTypes.includes(node.value.value);
21
+ const isExpression = node.value.type === 'VExpressionContainer'
22
+ && node.value.expression.type === 'ConditionalExpression'
23
+ && (legacyTypes.includes(node.value.expression.consequent.value)
24
+ || legacyTypes.includes(node.value.expression.alternate.value));
25
+ /**
26
+ * if it is a literal with a deprecated value -> we migrate
27
+ * if it is an expression with a defined deprecated value -> we migrate
28
+ * if it is unknown value (e.g. computed), but there is also the `native-type` we assume it is legacy and we migrate
29
+ */
30
+ if (isLiteral || isExpression || hasNativeType) {
31
+ context.report({
32
+ node,
33
+ messageId: 'useVariantInstead',
34
+ fix: (fixer) => {
35
+ if (node.key.type === 'VIdentifier') {
36
+ return fixer.replaceTextRange(node.key.range, 'variant');
37
+ }
38
+ else if (node.key.type === 'VDirectiveKey') {
39
+ return fixer.replaceTextRange(node.key.argument.range, 'variant');
40
+ }
41
+ },
42
+ });
43
+ }
44
+ },
45
+ 'VElement[name="ncbutton"] VAttribute:has(VIdentifier[name="native-type"])': function (node) {
46
+ context.report({
47
+ node,
48
+ messageId: 'useTypeInstead',
49
+ fix: (fixer) => {
50
+ if (node.key.type === 'VIdentifier') {
51
+ return fixer.replaceTextRange(node.key.range, 'type');
52
+ }
53
+ else if (node.key.type === 'VDirectiveKey') {
54
+ return fixer.replaceTextRange(node.key.argument.range, 'type');
55
+ }
56
+ },
57
+ });
58
+ },
59
+ });
60
+ },
61
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Check if a given path exists and is a file
3
+ *
4
+ * @param filePath The path
5
+ */
6
+ export declare function isFile(filePath: string): boolean;
7
+ /**
8
+ * Find the path of nearest `package.json` relative to given path
9
+ *
10
+ * @param currentPath Path to lookup
11
+ * @return Either the full path where `package.json` is located or `undefined` if no found
12
+ */
13
+ export declare function findPackageJsonDir(currentPath: string): string | undefined;
14
+ /**
15
+ * Make sure that versions like '25' can be handled by semver
16
+ *
17
+ * @param version The pure version string
18
+ * @return Sanitized version string
19
+ */
20
+ export declare function sanitizeTargetVersion(version: string): string;
21
+ /**
22
+ * Create a callback that takes a version number and checks if the version
23
+ * is valid compared to configured version / detected version.
24
+ *
25
+ * @param options Options
26
+ * @param options.cwd The current working directory
27
+ * @param options.physicalFilename The real filename where ESLint is linting currently
28
+ * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
29
+ */
30
+ export declare function createLibVersionValidator({ cwd, physicalFilename }: {
31
+ cwd: any;
32
+ physicalFilename: any;
33
+ }): ((version: string) => boolean);
@@ -0,0 +1,94 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import { lstatSync, readFileSync } from 'node:fs';
6
+ import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
7
+ import { gte, minVersion, valid } from 'semver';
8
+ /**
9
+ * Cached map of paths: Reco <path_to_package.json, validator>
10
+ */
11
+ const cachedMap = {};
12
+ /**
13
+ * Check if a given path exists and is a file
14
+ *
15
+ * @param filePath The path
16
+ */
17
+ export function isFile(filePath) {
18
+ const stats = lstatSync(filePath, { throwIfNoEntry: false });
19
+ return stats !== undefined && stats.isFile();
20
+ }
21
+ /**
22
+ * Find the path of nearest `package.json` relative to given path
23
+ *
24
+ * @param currentPath Path to lookup
25
+ * @return Either the full path where `package.json` is located or `undefined` if no found
26
+ */
27
+ export function findPackageJsonDir(currentPath) {
28
+ for (const cachedDirPath of Object.keys(cachedMap)) {
29
+ if (currentPath.startsWith(cachedDirPath)) {
30
+ return cachedDirPath;
31
+ }
32
+ }
33
+ while (currentPath && currentPath !== sep) {
34
+ if (isFile(join(currentPath, 'package.json'))) {
35
+ return currentPath;
36
+ }
37
+ currentPath = resolve(currentPath, '..');
38
+ }
39
+ return undefined;
40
+ }
41
+ /**
42
+ * Make sure that versions like '25' can be handled by semver
43
+ *
44
+ * @param version The pure version string
45
+ * @return Sanitized version string
46
+ */
47
+ export function sanitizeTargetVersion(version) {
48
+ const sanitizedVersion = minVersion(version)?.version;
49
+ // now version should look like '23.0.0'
50
+ if (!valid(sanitizedVersion)) {
51
+ throw Error(`[@nextcloud/eslint-plugin] Invalid target version ${version} found`);
52
+ }
53
+ return sanitizedVersion;
54
+ }
55
+ /**
56
+ * Create a callback that takes a version number and checks if the version
57
+ * is valid compared to configured version / detected version.
58
+ *
59
+ * @param options Options
60
+ * @param options.cwd The current working directory
61
+ * @param options.physicalFilename The real filename where ESLint is linting currently
62
+ * @return Function validator, return a boolean whether current version satisfies minimal required for the rule
63
+ */
64
+ export function createLibVersionValidator({ cwd, physicalFilename }) {
65
+ // Try to find package.json and parse the supported version
66
+ // Current working directory, either the filename (can be empty) or the cwd property
67
+ const currentDirectory = isAbsolute(physicalFilename)
68
+ ? resolve(dirname(physicalFilename))
69
+ : dirname(resolve(cwd, physicalFilename));
70
+ // The nearest package.json
71
+ const packageJsonDir = findPackageJsonDir(currentDirectory);
72
+ if (!packageJsonDir) {
73
+ // Skip the rule
74
+ return () => false;
75
+ }
76
+ else if (cachedMap[packageJsonDir]) {
77
+ return cachedMap[packageJsonDir];
78
+ }
79
+ const json = JSON.parse(readFileSync(join(packageJsonDir, 'package.json'), 'utf-8'));
80
+ const libVersions = [
81
+ json?.dependencies?.['@nextcloud/vue'],
82
+ json?.devDependencies?.['@nextcloud/vue'],
83
+ json?.peerDependencies?.['@nextcloud/vue'],
84
+ ]
85
+ .filter((version) => typeof version === 'string' && !!version)
86
+ .map(sanitizeTargetVersion)
87
+ .sort((a, b) => gte(a, b) ? 1 : -1);
88
+ if (!libVersions.length) {
89
+ // Skip the rule
90
+ return () => false;
91
+ }
92
+ // Return, whether given version satisfies minimal version from dependencies
93
+ return (version) => gte(libVersions[0], version);
94
+ }
@@ -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,23 @@
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[]): {
13
+ files: (string | string[])[];
14
+ name?: string;
15
+ ignores?: string[];
16
+ language?: string;
17
+ languageOptions?: Linter.LanguageOptions;
18
+ linterOptions?: Linter.LinterOptions;
19
+ processor?: string | Linter.Processor;
20
+ plugins?: Record<string, import("eslint").ESLint.Plugin>;
21
+ rules?: Partial<Linter.RulesRecord>;
22
+ settings?: Record<string, unknown>;
23
+ }[];
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.2",
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,35 +34,39 @@
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": "npx --node-options='--experimental-strip-types' eslint --flag unstable_native_nodejs_ts_config",
41
+ "lint:fix": "npx --node-options='--experimental-strip-types' 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/json": "^0.12.0",
47
+ "@stylistic/eslint-plugin": "^4.4.1",
48
+ "eslint-plugin-antfu": "^3.1.1",
49
+ "eslint-plugin-jsdoc": "^50.7.1",
50
+ "eslint-plugin-perfectionist": "^4.14.0",
51
+ "eslint-plugin-vue": "^10.1.0",
52
+ "fast-xml-parser": "^5.2.3",
53
+ "globals": "^16.2.0",
54
+ "semver": "^7.7.2",
55
+ "sort-package-json": "^3.2.1",
56
+ "typescript-eslint": "^8.33.1"
52
57
  },
53
58
  "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"
59
+ "@types/node": "^22.15.29",
60
+ "@types/semver": "^7.7.0",
61
+ "eslint": "^9.28.0",
62
+ "memfs": "^4.17.2",
63
+ "vitest": "^3.2.1"
60
64
  },
61
65
  "peerDependencies": {
62
66
  "eslint": ">=9"
63
67
  },
64
68
  "engines": {
65
- "node": "^20.9 || ^22"
69
+ "node": "^20.19 || ^22 || ^24"
66
70
  },
67
71
  "devEngines": {
68
72
  "packageManager": {
@@ -71,7 +75,7 @@
71
75
  },
72
76
  "runtime": {
73
77
  "name": "node",
74
- "version": "^22"
78
+ "version": "^22.10"
75
79
  }
76
80
  }
77
81
  }