UNPKG

@nextcloud/eslint-config

Version:

Eslint shared config for nextcloud apps and libraries

210 lines (209 loc) 7.7 kB
/*! * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ import { createVersionValidator } from "../utils/app-version-parser.js"; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ const global = { $: '19.0.0', Backbone: '18.0.0', Clipboard: '18.0.0', ClipboardJs: '18.0.0', DOMPurify: '18.0.0', formatDate: '16.0.0', getURLParameter: '16.0.0', Handlebars: '18.0.0', humanFileSize: '16.0.0', initCore: '17.0.0', jQuery: '19.0.0', jstimezonedetect: '18.0.0', jstz: '18.0.0', md5: '18.0.0', moment: '18.0.0', oc_appconfig: '17.0.0', oc_appswebroots: '17.0.0', oc_capabilities: '17.0.0', oc_config: '17.0.0', oc_current_user: '17.0.0', oc_debug: '17.0.0', oc_isadmin: '17.0.0', oc_requesttoken: '17.0.0', oc_webroot: '17.0.0', OCDialogs: '17.0.0', relative_modified_date: '16.0.0', }; const oc = { AppConfig: '16.0.0', L10n: '26.0.0', SystemTags: '32.0.0', _capabilities: '17.0.0', addTranslations: '17.0.0', basename: '18.0.0', coreApps: '17.0.0', currentUser: '19.0.0', dialogs: '30.0.0', dirname: '18.0.0', encodePath: '18.0.0', fileIsBlacklisted: '18.0.0', filePath: '19.0.0', generateUrl: '19.0.0', get: '19.0.0', getCanonicalLocale: '20.0.0', getCurrentUser: '19.0.0', getHost: '17.0.0', getHostName: '17.0.0', getPort: '17.0.0', getProtocol: '17.0.0', getRootPath: '19.0.0', imagePath: '19.0.0', isSamePath: '18.0.0', joinPaths: '18.0.0', linkTo: '19.0.0', linkToOCS: '19.0.0', linkToRemote: '19.0.0', set: '19.0.0', webroot: '19.0.0', }; const oca = { Search: '20.0.0', }; const ocp = { Toast: '19.0.0', }; const ocNested = { Util: { formatDate: '20.0.0', humanFileSize: '20.0.0', relativeModifiedDate: '20.0.0', }, dialogs: { fileexists: '29.0.0', }, config: { blacklist_files_regex: '30.0.0', forbidden_filename_characters: '30.0.0', }, }; const rule = { meta: { docs: { description: 'Deprecated Nextcloud APIs', recommended: true, }, // fixable: null or "code" or "whitespace" schema: [ { // We accept one option which is an object type: 'object', properties: { // if we should try to find an appinfo and only handle APIs removed before the max-version parseAppInfo: { type: 'boolean', }, // Set a Nextcloud target version, only APIs removed before that versions are checked targetVersion: { type: 'string', }, }, additionalProperties: false, }, ], messages: { deprecatedGlobal: 'The global property or function {{name}} was deprecated in Nextcloud {{version}}', }, }, create: function (context) { const checkTargetVersion = createVersionValidator(context); return { MemberExpression: function (node) { // OC.x if ('name' in node.object && 'name' in node.property && node.object.name === 'OC' && Object.hasOwn(oc, node.property.name) && checkTargetVersion(oc[node.property.name])) { context.report({ node, message: `The property or function OC.${node.property.name} was deprecated in Nextcloud ${oc[node.property.name]}`, }); } // OCA.x if ('name' in node.object && 'name' in node.property && node.object.name === 'OCA' && Object.hasOwn(oca, node.property.name) && checkTargetVersion(oca[node.property.name])) { context.report({ node, message: `The property or function OCA.${node.property.name} was deprecated in Nextcloud ${oca[node.property.name]}`, }); } // OCP.x if ('name' in node.object && 'name' in node.property && node.object.name === 'OCP' && Object.hasOwn(ocp, node.property.name) && checkTargetVersion(ocp[node.property.name])) { context.report({ node, message: `The property or function OCP.${node.property.name} was deprecated in Nextcloud ${ocp[node.property.name]}`, }); } // OC.x.y if (node.object.type === 'MemberExpression' && 'name' in node.object.object && node.object.object.name === 'OC' && 'name' in node.property && 'name' in node.object.property && Object.hasOwn(ocNested, node.object.property.name) && Object.hasOwn(ocNested[node.object.property.name], node.property.name)) { const version = ocNested[node.object.property.name][node.property.name]; if (checkTargetVersion(version)) { const prop = [ 'OC', node.object.property.name, node.property.name, ].join('.'); const deprecatedSince = ocNested[node.object.property.name][node.property.name]; context.report({ node, message: `The property or function ${prop} was deprecated in Nextcloud ${deprecatedSince}`, }); } } }, Program(node) { // Logic adapted from https://github.com/eslint/eslint/blob/master/lib/rules/no-restricted-globals.js const scope = context.sourceCode.getScope(node); const report = ({ identifier }) => { if (checkTargetVersion(global[identifier.name])) { context.report({ node, messageId: 'deprecatedGlobal', data: { name: identifier.name, version: global[identifier.name], }, }); } }; // Report variables declared elsewhere (ex: variables defined as "global" by eslint) scope.variables.forEach((variable) => { if (!variable.defs.length && Object.hasOwn(global, variable.name)) { variable.references.forEach(report); } }); // Report variables not declared at all scope.through.forEach((reference) => { if (Object.hasOwn(global, reference.identifier.name)) { report(reference); } }); }, }; }, }; export default rule;