@nextcloud/eslint-config 8.4.2 → 9.0.0-rc.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +151 -25
  3. package/dist/configs/codeStyle.d.ts +13 -0
  4. package/dist/configs/codeStyle.js +178 -0
  5. package/dist/configs/documentation.d.ts +12 -0
  6. package/dist/configs/documentation.js +84 -0
  7. package/dist/configs/filesystem.d.ts +9 -0
  8. package/dist/configs/filesystem.js +18 -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 +53 -0
  19. package/dist/configs/vue.d.ts +12 -0
  20. package/dist/configs/vue.js +134 -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 +24 -0
  25. package/dist/globs.d.ts +18 -0
  26. package/dist/globs.js +37 -0
  27. package/dist/index.d.ts +27 -0
  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 +8 -0
  46. package/dist/plugins/nextcloud-vue/index.js +9 -0
  47. package/dist/plugins/nextcloud-vue/rules/index.d.ts +6 -0
  48. package/dist/plugins/nextcloud-vue/rules/index.js +4 -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/utils/lib-version-parser.d.ts +33 -0
  52. package/dist/plugins/nextcloud-vue/utils/lib-version-parser.js +94 -0
  53. package/dist/plugins/packageJson.d.ts +10 -0
  54. package/dist/plugins/packageJson.js +66 -0
  55. package/dist/utils.d.ts +23 -0
  56. package/dist/utils.js +19 -0
  57. package/dist/version.d.ts +1 -0
  58. package/dist/version.js +6 -0
  59. package/package.json +53 -53
  60. package/.git-blame-ignore-revs +0 -7
  61. package/LICENSES/AGPL-3.0-or-later.txt +0 -235
  62. package/LICENSES/CC0-1.0.txt +0 -121
  63. package/LICENSES/MIT.txt +0 -9
  64. package/REUSE.toml +0 -12
  65. package/index.js +0 -25
  66. package/parts/base.js +0 -117
  67. package/parts/typescript.js +0 -49
  68. package/parts/vue.js +0 -45
  69. package/parts/vue3.js +0 -43
  70. package/typescript.js +0 -45
  71. package/vue3.js +0 -45
package/dist/globs.js ADDED
@@ -0,0 +1,37 @@
1
+ /*!
2
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ /** Glob pattern for test files (specs) */
6
+ export const GLOB_FILES_TESTING = [
7
+ '**/*.test.*',
8
+ '**/*.spec.*',
9
+ '**/*.cy.*',
10
+ '**/test',
11
+ '**/tests',
12
+ ];
13
+ /** Glob pattern for Typescript files */
14
+ export const GLOB_FILES_TYPESCRIPT = [
15
+ '**/*.ts',
16
+ '**/*.mts',
17
+ '**/*.cts',
18
+ '**/*.tsx',
19
+ ];
20
+ /** Glob pattern for Javascript files */
21
+ export const GLOB_FILES_JAVASCRIPT = [
22
+ '**/*.js',
23
+ '**/*.cjs',
24
+ '**/*.mjs',
25
+ '**/*.jsx',
26
+ ];
27
+ /** Glob pattern for JSON files */
28
+ export const GLOB_FILES_JSON = ['**/*.json'];
29
+ /** Glob pattern for JSONC files */
30
+ export const GLOB_FILES_JSONC = ['**/*.jsonc'];
31
+ /** Glob pattern for Microsoft JSON files which use a slightly different JSONC implementation */
32
+ export const GLOB_FILES_MS_JSON = [
33
+ '**/tsconfig.json',
34
+ '.vscode/*.json',
35
+ ];
36
+ /** Glob pattern for Vue.JS files */
37
+ export const GLOB_FILES_VUE = ['**/*.vue'];
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Nextcloud shared configuration for projects using Vue 2 with Javascript <script> blocks
3
+ */
4
+ export declare const recommendedVue2Javascript: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
5
+ /**
6
+ * Nextcloud shared configuration for projects using Vue 2 with Typescript <script> blocks
7
+ */
8
+ export declare const recommendedVue2: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
9
+ /**
10
+ * Nextcloud shared configuration for projects using Vue 3 with Javascript <script> blocks
11
+ */
12
+ export declare const recommendedJavascript: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
13
+ /**
14
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
15
+ */
16
+ export declare const recommended: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
17
+ /**
18
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
19
+ */
20
+ export declare const recommendedLibrary: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
21
+ /**
22
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
23
+ */
24
+ export declare const recommendedVue2Library: (import("eslint").Linter.Config<import("eslint").Linter.RulesRecord> | import("eslint").Linter.BaseConfig<import("eslint").Linter.RulesRecord, import("eslint").Linter.RulesRecord>)[];
25
+ export { default as packageJsonPlugin } from './plugins/packageJson.ts';
26
+ export { default as nextcloudPlugin } from './plugins/nextcloud/index.ts';
27
+ export { default as l10nPlugin } from './plugins/l10n/index.ts';
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ import { codeStyle } from "./configs/codeStyle.js";
2
+ import { documentation } from "./configs/documentation.js";
3
+ import { filesystem } from "./configs/filesystem.js";
4
+ import { imports } from "./configs/imports.js";
5
+ import { javascript } from "./configs/javascript.js";
6
+ import { json } from "./configs/json.js";
7
+ import { node } from "./configs/node.js";
8
+ import { typescript } from "./configs/typescript.js";
9
+ import { vue2 } from "./configs/vue2.js";
10
+ import { vue3 } from "./configs/vue3.js";
11
+ /**
12
+ * Nextcloud shared configuration for projects using Vue 2 with Javascript <script> blocks
13
+ */
14
+ export const recommendedVue2Javascript = createConfig({
15
+ isLibrary: false,
16
+ vue2: true,
17
+ vueIsTypescript: false,
18
+ });
19
+ /**
20
+ * Nextcloud shared configuration for projects using Vue 2 with Typescript <script> blocks
21
+ */
22
+ export const recommendedVue2 = createConfig({
23
+ isLibrary: false,
24
+ vue2: true,
25
+ vueIsTypescript: true,
26
+ });
27
+ /**
28
+ * Nextcloud shared configuration for projects using Vue 3 with Javascript <script> blocks
29
+ */
30
+ export const recommendedJavascript = createConfig({
31
+ isLibrary: false,
32
+ vueIsTypescript: false,
33
+ });
34
+ /**
35
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
36
+ */
37
+ export const recommended = createConfig({
38
+ isLibrary: false,
39
+ vueIsTypescript: true,
40
+ });
41
+ /**
42
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
43
+ */
44
+ export const recommendedLibrary = createConfig({
45
+ isLibrary: true,
46
+ vueIsTypescript: true,
47
+ });
48
+ /**
49
+ * Nextcloud shared configuration for projects using Vue 3 with Typescript <script> blocks
50
+ */
51
+ export const recommendedVue2Library = createConfig({
52
+ isLibrary: true,
53
+ vue2: true,
54
+ vueIsTypescript: true,
55
+ });
56
+ export { default as packageJsonPlugin } from "./plugins/packageJson.js";
57
+ export { default as nextcloudPlugin } from "./plugins/nextcloud/index.js";
58
+ export { default as l10nPlugin } from "./plugins/l10n/index.js";
59
+ /**
60
+ * Generate a configuration based on given options
61
+ *
62
+ * @param options - Configuration options
63
+ */
64
+ function createConfig(options) {
65
+ return [
66
+ ...filesystem,
67
+ ...javascript(options),
68
+ ...json,
69
+ ...node,
70
+ ...typescript(options),
71
+ ...(options.vue2
72
+ ? vue2(options)
73
+ : vue3(options)),
74
+ ...documentation(options),
75
+ ...imports(options),
76
+ ...codeStyle(options),
77
+ ];
78
+ }
@@ -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 enforce consistent translations
8
+ */
9
+ declare const Plugin: ESLint.Plugin;
10
+ export default Plugin;
@@ -0,0 +1,17 @@
1
+ import { packageVersion } from "../../version.js";
2
+ import RuleEllipsis from "./rules/enforce-ellipsis.js";
3
+ import RuleNonBreakingSpace from "./rules/non-breaking-space.js";
4
+ /**
5
+ * ESLint plugin to enforce consistent translations
6
+ */
7
+ const Plugin = {
8
+ meta: {
9
+ name: '@nextcloud/l10n-plugin',
10
+ version: packageVersion,
11
+ },
12
+ rules: {
13
+ 'non-breaking-space': RuleNonBreakingSpace,
14
+ 'enforce-ellipsis': RuleEllipsis,
15
+ },
16
+ };
17
+ export default Plugin;
@@ -0,0 +1,7 @@
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
+ declare const _default: Rule.RuleModule;
7
+ export default _default;
@@ -0,0 +1,32 @@
1
+ const defineRule = (r) => r;
2
+ export default defineRule({
3
+ meta: {
4
+ fixable: 'code',
5
+ type: 'suggestion',
6
+ schema: [],
7
+ docs: {
8
+ description: 'Enforce consistent usageof ellipsis instead of tripple dots',
9
+ },
10
+ },
11
+ create(context) {
12
+ return {
13
+ Literal(node) {
14
+ if (typeof node.value !== 'string'
15
+ || node.parent.type !== 'CallExpression'
16
+ || node.parent.callee.type !== 'Identifier'
17
+ || node.parent.callee.name !== 't') {
18
+ return;
19
+ }
20
+ if (node.raw?.match(/(?<=[^.])\.\.\.(?!\.)/)) {
21
+ context.report({
22
+ node,
23
+ message: 'Strings should ellipsis instead of triple dots',
24
+ fix(fixer) {
25
+ return fixer.replaceText(node, node.raw.replaceAll(/(?<=[^.])\.\.\.(?!\.)/g, '…'));
26
+ },
27
+ });
28
+ }
29
+ },
30
+ };
31
+ },
32
+ });
@@ -0,0 +1,7 @@
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
+ declare const _default: Rule.RuleModule;
7
+ export default _default;
@@ -0,0 +1,30 @@
1
+ const defineRule = (r) => r;
2
+ export default defineRule({
3
+ meta: {
4
+ fixable: 'code',
5
+ type: 'suggestion',
6
+ schema: [],
7
+ docs: {
8
+ description: 'Enforce non-breaking spaces before ellipsis',
9
+ },
10
+ },
11
+ create(context) {
12
+ return {
13
+ Literal(node) {
14
+ if (typeof node.value !== 'string') {
15
+ return;
16
+ }
17
+ const matches = node.value.match(/(\s+)…/);
18
+ if (matches && matches[1] !== ' ') {
19
+ context.report({
20
+ node,
21
+ message: 'Ellipsis must be preceded by non-breaking spaces',
22
+ fix(fixer) {
23
+ return fixer.replaceText(node, node.raw.replaceAll(/\s+…/g, ' …'));
24
+ },
25
+ });
26
+ }
27
+ },
28
+ };
29
+ },
30
+ });
@@ -0,0 +1,7 @@
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
+ declare const _default: ESLint.Plugin;
7
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import { packageVersion } from "../../version.js";
2
+ import { rules } from "./rules/index.js";
3
+ export default {
4
+ rules,
5
+ meta: {
6
+ name: '@nextcloud/eslint-plugin',
7
+ version: packageVersion,
8
+ },
9
+ };
@@ -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 type { Rule } from 'eslint';
6
+ export declare const rules: Record<string, Rule.RuleModule>;
@@ -0,0 +1,6 @@
1
+ import noDeprecations from "./no-deprecations.js";
2
+ import noRemovedApis from "./no-removed-apis.js";
3
+ export const rules = {
4
+ 'no-deprecations': noDeprecations,
5
+ 'no-removed-apis': noRemovedApis,
6
+ };
@@ -0,0 +1,7 @@
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
+ declare const rule: Rule.RuleModule;
7
+ export default rule;
@@ -0,0 +1,196 @@
1
+ import { createVersionValidator } from "../utils/version-parser.js";
2
+ // ------------------------------------------------------------------------------
3
+ // Rule Definition
4
+ // ------------------------------------------------------------------------------
5
+ const global = {
6
+ $: '19.0.0',
7
+ Backbone: '18.0.0',
8
+ Clipboard: '18.0.0',
9
+ ClipboardJs: '18.0.0',
10
+ DOMPurify: '18.0.0',
11
+ formatDate: '16.0.0',
12
+ getURLParameter: '16.0.0',
13
+ Handlebars: '18.0.0',
14
+ humanFileSize: '16.0.0',
15
+ initCore: '17.0.0',
16
+ jQuery: '19.0.0',
17
+ jstimezonedetect: '18.0.0',
18
+ jstz: '18.0.0',
19
+ md5: '18.0.0',
20
+ moment: '18.0.0',
21
+ oc_appconfig: '17.0.0',
22
+ oc_appswebroots: '17.0.0',
23
+ oc_capabilities: '17.0.0',
24
+ oc_config: '17.0.0',
25
+ oc_current_user: '17.0.0',
26
+ oc_debug: '17.0.0',
27
+ oc_isadmin: '17.0.0',
28
+ oc_requesttoken: '17.0.0',
29
+ oc_webroot: '17.0.0',
30
+ OCDialogs: '17.0.0',
31
+ relative_modified_date: '16.0.0',
32
+ };
33
+ const oc = {
34
+ L10n: '26.0.0',
35
+ _capabilities: '17.0.0',
36
+ addTranslations: '17.0.0',
37
+ basename: '18.0.0',
38
+ coreApps: '17.0.0',
39
+ currentUser: '19.0.0',
40
+ dirname: '18.0.0',
41
+ encodePath: '18.0.0',
42
+ fileIsBlacklisted: '17.0.0',
43
+ filePath: '19.0.0',
44
+ generateUrl: '19.0.0',
45
+ get: '19.0.0',
46
+ getCanonicalLocale: '20.0.0',
47
+ getCurrentUser: '19.0.0',
48
+ getHost: '17.0.0',
49
+ getHostName: '17.0.0',
50
+ getPort: '17.0.0',
51
+ getProtocol: '17.0.0',
52
+ getRootPath: '19.0.0',
53
+ imagePath: '19.0.0',
54
+ isSamePath: '18.0.0',
55
+ joinPaths: '18.0.0',
56
+ linkTo: '19.0.0',
57
+ linkToOCS: '19.0.0',
58
+ linkToRemote: '19.0.0',
59
+ set: '19.0.0',
60
+ webroot: '19.0.0',
61
+ };
62
+ const oca = {
63
+ Search: '20.0.0',
64
+ };
65
+ const ocp = {
66
+ Toast: '19.0.0',
67
+ };
68
+ const ocNested = {
69
+ Util: {
70
+ formatDate: '20.0.0',
71
+ humanFileSize: '20.0.0',
72
+ relativeModifiedDate: '20.0.0',
73
+ },
74
+ };
75
+ const rule = {
76
+ meta: {
77
+ docs: {
78
+ description: 'Deprecated Nextcloud APIs',
79
+ category: 'Nextcloud',
80
+ recommended: true,
81
+ },
82
+ // fixable: null or "code" or "whitespace"
83
+ schema: [
84
+ {
85
+ // We accept one option which is an object
86
+ type: 'object',
87
+ properties: {
88
+ // if we should try to find an appinfo and only handle APIs removed before the max-version
89
+ parseAppInfo: {
90
+ type: 'boolean',
91
+ },
92
+ // Set a Nextcloud target version, only APIs removed before that versions are checked
93
+ targetVersion: {
94
+ type: 'string',
95
+ },
96
+ },
97
+ additionalProperties: false,
98
+ },
99
+ ],
100
+ messages: {
101
+ deprecatedGlobal: 'The global property or function {{name}} was deprecated in Nextcloud {{version}}',
102
+ },
103
+ },
104
+ create: function (context) {
105
+ const checkTargetVersion = createVersionValidator(context);
106
+ return {
107
+ MemberExpression: function (node) {
108
+ // OC.x
109
+ if ('name' in node.object
110
+ && 'name' in node.property
111
+ && node.object.name === 'OC'
112
+ && Object.hasOwn(oc, node.property.name)
113
+ && checkTargetVersion(oc[node.property.name])) {
114
+ context.report({
115
+ node,
116
+ message: `The property or function OC.${node.property.name} was deprecated in Nextcloud ${oc[node.property.name]}`,
117
+ });
118
+ }
119
+ // OCA.x
120
+ if ('name' in node.object
121
+ && 'name' in node.property
122
+ && node.object.name === 'OCA'
123
+ && Object.hasOwn(oca, node.property.name)
124
+ && checkTargetVersion(oca[node.property.name])) {
125
+ context.report({
126
+ node,
127
+ message: `The property or function OCA.${node.property.name} was deprecated in Nextcloud ${oca[node.property.name]}`,
128
+ });
129
+ }
130
+ // OCP.x
131
+ if ('name' in node.object
132
+ && 'name' in node.property
133
+ && node.object.name === 'OCP'
134
+ && Object.hasOwn(ocp, node.property.name)
135
+ && checkTargetVersion(ocp[node.property.name])) {
136
+ context.report({
137
+ node,
138
+ message: `The property or function OCP.${node.property.name} was deprecated in Nextcloud ${ocp[node.property.name]}`,
139
+ });
140
+ }
141
+ // OC.x.y
142
+ if (node.object.type === 'MemberExpression'
143
+ && 'name' in node.object.object
144
+ && node.object.object.name === 'OC'
145
+ && 'name' in node.property
146
+ && 'name' in node.object.property
147
+ && Object.hasOwn(ocNested, node.object.property.name)
148
+ && Object.hasOwn(ocNested[node.object.property.name], node.property.name)) {
149
+ const version = ocNested[node.object.property.name][node.property.name];
150
+ if (checkTargetVersion(version)) {
151
+ const prop = [
152
+ 'OC',
153
+ node.object.property.name,
154
+ node.property.name,
155
+ ].join('.');
156
+ const deprecatedSince = ocNested[node.object.property.name][node.property.name];
157
+ context.report({
158
+ node,
159
+ message: `The property or function ${prop} was deprecated in Nextcloud ${deprecatedSince}`,
160
+ });
161
+ }
162
+ }
163
+ },
164
+ Program(node) {
165
+ // Logic adapted from https://github.com/eslint/eslint/blob/master/lib/rules/no-restricted-globals.js
166
+ const scope = context.sourceCode.getScope(node);
167
+ const report = ({ identifier }) => {
168
+ if (checkTargetVersion(global[identifier.name])) {
169
+ context.report({
170
+ node,
171
+ messageId: 'deprecatedGlobal',
172
+ data: {
173
+ name: identifier.name,
174
+ version: global[identifier.name],
175
+ },
176
+ });
177
+ }
178
+ };
179
+ // Report variables declared elsewhere (ex: variables defined as "global" by eslint)
180
+ scope.variables.forEach((variable) => {
181
+ if (!variable.defs.length
182
+ && Object.hasOwn(global, variable.name)) {
183
+ variable.references.forEach(report);
184
+ }
185
+ });
186
+ // Report variables not declared at all
187
+ scope.through.forEach((reference) => {
188
+ if (Object.hasOwn(global, reference.identifier.name)) {
189
+ report(reference);
190
+ }
191
+ });
192
+ },
193
+ };
194
+ },
195
+ };
196
+ export default rule;
@@ -0,0 +1,7 @@
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
+ declare const rule: Rule.RuleModule;
7
+ export default rule;
@@ -0,0 +1,152 @@
1
+ import { createVersionValidator } from "../utils/version-parser.js";
2
+ // ------------------------------------------------------------------------------
3
+ // Rule Definition
4
+ // ------------------------------------------------------------------------------
5
+ const global = {
6
+ autosize: '29.0.0',
7
+ escapeHTML: '20.0.0',
8
+ fileDownloadPath: '15.0.0',
9
+ formatDate: '19.0.0',
10
+ getScrollBarWidth: '15.0.0',
11
+ getURLParameter: '19.0.0',
12
+ humanFileSize: '19.0.0',
13
+ marked: '19.0.0',
14
+ relative_modified_date: '19.0.0',
15
+ };
16
+ const oc = {
17
+ getScrollBarWidth: '15.0.0',
18
+ addTranslations: '26.0.0',
19
+ appSettings: '28.0.0',
20
+ loadScript: '28.0.0',
21
+ loadStyle: '28.0.0',
22
+ };
23
+ const ocNested = {
24
+ AppConfig: {
25
+ hasKey: '15.0.0',
26
+ deleteApp: '15.0.0',
27
+ },
28
+ Util: {
29
+ hasSVGSupport: '15.0.0',
30
+ replaceSVGIcon: '15.0.0',
31
+ replaceSVG: '15.0.0',
32
+ scaleFixForIE8: '15.0.0',
33
+ isIE8: '15.0.0',
34
+ },
35
+ };
36
+ const oca = {
37
+ // ref: https://github.com/nextcloud/server/commit/6eced42b7a40f5b0ea0489244583219d0ee2e7af
38
+ Search: '20.0.0',
39
+ };
40
+ // TODO: handle OC.x.y.z like OC.Share.ShareConfigModel.areAvatarsEnabled()
41
+ // ref https://github.com/nextcloud/server/issues/11045
42
+ const rule = {
43
+ meta: {
44
+ docs: {
45
+ description: 'Removed Nextcloud APIs',
46
+ category: 'Nextcloud',
47
+ recommended: true,
48
+ },
49
+ // fixable: "code" or "whitespace"
50
+ schema: [
51
+ {
52
+ // We accept one option which is an object
53
+ type: 'object',
54
+ properties: {
55
+ // if we should try to find an appinfo and only handle APIs removed before the max-version
56
+ parseAppInfo: {
57
+ type: 'boolean',
58
+ },
59
+ // Set a Nextcloud target version, only APIs removed before that versions are checked
60
+ targetVersion: {
61
+ type: 'string',
62
+ },
63
+ },
64
+ additionalProperties: false,
65
+ },
66
+ ],
67
+ messages: {
68
+ removedGlobal: 'The global property or function {{name}} was removed in Nextcloud {{version}}',
69
+ },
70
+ },
71
+ create(context) {
72
+ const checkTargetVersion = createVersionValidator(context);
73
+ return {
74
+ MemberExpression(node) {
75
+ // OCA.x
76
+ if ('name' in node.object
77
+ && 'name' in node.property
78
+ && node.object.name === 'OCA'
79
+ && oca[node.property.name]
80
+ && checkTargetVersion(oca[node.property.name])) {
81
+ context.report({
82
+ node,
83
+ message: `The property or function OCA.${node.property.name} was removed in Nextcloud ${oca[node.property.name]}`,
84
+ });
85
+ }
86
+ // OC.x
87
+ if ('name' in node.object
88
+ && 'name' in node.property
89
+ && node.object.name === 'OC'
90
+ && Object.hasOwn(oc, node.property.name)
91
+ && checkTargetVersion(oc[node.property.name])) {
92
+ context.report({
93
+ node,
94
+ message: `The property or function OC.${node.property.name} was removed in Nextcloud ${oc[node.property.name]}`,
95
+ });
96
+ }
97
+ // OC.x.y
98
+ if (node.object.type === 'MemberExpression'
99
+ && 'name' in node.object.object
100
+ && node.object.object.name === 'OC'
101
+ && 'name' in node.object.property
102
+ && ocNested[node.object.property.name]
103
+ && 'name' in node.property
104
+ && ocNested[node.object.property.name][node.property.name]) {
105
+ const version = ocNested[node.object.property.name][node.property.name];
106
+ if (checkTargetVersion(version)) {
107
+ const prop = [
108
+ 'OC',
109
+ node.object.property.name,
110
+ node.property.name,
111
+ ].join('.');
112
+ context.report({
113
+ node,
114
+ message: `The property or function ${prop} was removed in Nextcloud ${version}`,
115
+ });
116
+ }
117
+ }
118
+ },
119
+ Program(node) {
120
+ // Logic adapted from https://github.com/eslint/eslint/blob/master/lib/rules/no-restricted-globals.js
121
+ const scope = context.sourceCode.getScope(node);
122
+ const report = (ref) => {
123
+ const { identifier } = ref;
124
+ if (global[identifier.name] && checkTargetVersion(global[identifier.name])) {
125
+ context.report({
126
+ node,
127
+ messageId: 'removedGlobal',
128
+ data: {
129
+ name: identifier.name,
130
+ version: global[identifier.name],
131
+ },
132
+ });
133
+ }
134
+ };
135
+ // Report variables declared elsewhere (ex: variables defined as "global" by eslint)
136
+ scope.variables.forEach((variable) => {
137
+ if (!variable.defs.length
138
+ && Object.hasOwn(global, variable.name)) {
139
+ variable.references.forEach(report);
140
+ }
141
+ });
142
+ // Report variables not declared at all
143
+ scope.through.forEach((reference) => {
144
+ if (Object.hasOwn(global, reference.identifier.name)) {
145
+ report(reference);
146
+ }
147
+ });
148
+ },
149
+ };
150
+ },
151
+ };
152
+ export default rule;