@nextcloud/eslint-config
Version:
Eslint shared config for nextcloud apps and libraries
204 lines (203 loc) • 7.73 kB
JavaScript
/*!
* 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 = {
autosize: '29.0.0',
escapeHTML: '20.0.0',
fileDownloadPath: '15.0.0',
formatDate: '19.0.0',
getScrollBarWidth: '15.0.0',
getURLParameter: '19.0.0',
humanFileSize: '19.0.0',
marked: '19.0.0',
md5: '32.0.0',
relative_modified_date: '19.0.0',
};
const oc = {
AppConfig: '33.0.0',
SystemTags: '33.0.0',
getScrollBarWidth: '15.0.0',
addTranslations: '26.0.0',
appSettings: '28.0.0',
fileIsBlacklisted: '33.0.0',
get: '33.0.0',
getHost: '33.0.0',
getHostName: '33.0.0',
getPort: '33.0.0',
getProtocol: '33.0.0',
loadScript: '28.0.0',
loadStyle: '28.0.0',
redirect: '33.0.0',
reload: '33.0.0',
set: '33.0.0',
};
const ocNested = {
AppConfig: {
hasKey: '15.0.0',
deleteApp: '15.0.0',
},
Util: {
hasSVGSupport: '15.0.0',
replaceSVGIcon: '15.0.0',
replaceSVG: '15.0.0',
scaleFixForIE8: '15.0.0',
isIE8: '15.0.0',
},
Settings: {
UserSettings: '33.0.0',
},
};
const oca = {
// ref: https://github.com/nextcloud/server/commit/6eced42b7a40f5b0ea0489244583219d0ee2e7af
Search: '20.0.0',
FilesSharingDrop: '31.0.0',
};
const ocaNested = {
Core: {
ProfileSections: '33.0.0',
},
Files: {
Sidebar: '33.0.0',
},
Sharing: {
ExternalLinkActions: '33.0.0',
},
};
// TODO: handle OC.x.y.z like OC.Share.ShareConfigModel.areAvatarsEnabled()
// ref https://github.com/nextcloud/server/issues/11045
const rule = {
meta: {
docs: {
description: 'Removed Nextcloud APIs',
recommended: true,
},
// fixable: "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: {
removedGlobal: 'The global property or function {{name}} was removed in Nextcloud {{version}}',
},
},
create(context) {
const checkTargetVersion = createVersionValidator(context);
return {
MemberExpression(node) {
// OCA.x
if ('name' in node.object
&& 'name' in node.property
&& node.object.name === 'OCA'
&& oca[node.property.name]
&& checkTargetVersion(oca[node.property.name])) {
context.report({
node,
message: `The property or function OCA.${node.property.name} was removed in Nextcloud ${oca[node.property.name]}`,
});
}
// OCA.x.y
if (node.object.type === 'MemberExpression'
&& 'name' in node.object.object
&& node.object.object.name === 'OCA'
&& 'name' in node.object.property
&& ocaNested[node.object.property.name]
&& 'name' in node.property
&& ocaNested[node.object.property.name][node.property.name]) {
const version = ocaNested[node.object.property.name][node.property.name];
if (checkTargetVersion(version)) {
const prop = [
'OC',
node.object.property.name,
node.property.name,
].join('.');
context.report({
node,
message: `The property or function ${prop} was removed in Nextcloud ${version}`,
});
}
}
// 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 removed in Nextcloud ${oc[node.property.name]}`,
});
}
// OC.x.y
if (node.object.type === 'MemberExpression'
&& 'name' in node.object.object
&& node.object.object.name === 'OC'
&& 'name' in node.object.property
&& ocNested[node.object.property.name]
&& 'name' in node.property
&& 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('.');
context.report({
node,
message: `The property or function ${prop} was removed in Nextcloud ${version}`,
});
}
}
},
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 = (ref) => {
const { identifier } = ref;
if (global[identifier.name] && checkTargetVersion(global[identifier.name])) {
context.report({
node,
messageId: 'removedGlobal',
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;