eslint-plugin-obsidianmd
Version:
Validates guidelines for Obsidian plugins
77 lines (76 loc) • 3.33 kB
JavaScript
import { ESLintUtils } from "@typescript-eslint/utils";
import { manifest } from "../../readManifest.js";
const ruleCreator = ESLintUtils.RuleCreator((name) => `https://github.com/obsidianmd/eslint-plugin/blob/master/docs/rules/settings-tab/${name}.md`);
export default ruleCreator({
name: "no-problematic-settings-headings",
meta: {
docs: {
description: "Discourage anti-patterns in settings headings.",
url: "https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines#UI+text",
},
type: "problem",
messages: {
settings: 'Avoid using "settings" in settings headings.',
general: 'Avoid using a "General" heading in settings.',
pluginName: "Avoid including the plugin name in settings headings.",
},
schema: [],
fixable: "code",
},
defaultOptions: [],
create(context) {
let insidePluginSettingTab = false;
return {
ClassDeclaration(node) {
if (node.superClass?.type === "Identifier" &&
node.superClass.name === "PluginSettingTab") {
insidePluginSettingTab = true;
}
},
"ClassDeclaration:exit"() {
insidePluginSettingTab = false;
},
// Find `new Setting(...).setName("...").setHeading()`
"CallExpression[callee.property.name='setHeading']"(node) {
if (!insidePluginSettingTab)
return;
let setNameCall = null;
let settingConstructor = null;
let current = node.callee;
// Traverse the call chain to find setName and the constructor
while (current.object.type === "CallExpression") {
const innerCall = current.object;
if (innerCall.callee.type === "MemberExpression" &&
innerCall.callee.property.type === "Identifier" &&
innerCall.callee.property.name === "setName") {
setNameCall = innerCall;
}
current = innerCall.callee;
}
if (current.object.type === "NewExpression" &&
current.object.callee.name ===
"Setting") {
settingConstructor = current.object;
}
if (!setNameCall || !settingConstructor)
return;
const textArg = setNameCall.arguments[0];
if (textArg?.type !== "Literal" ||
typeof textArg.value !== "string") {
return;
}
const text = textArg.value.toLowerCase();
if (text.includes("settings") || text.includes("options")) {
context.report({ node, messageId: "settings" });
}
if (text.includes("general")) {
context.report({ node, messageId: "general" });
}
if (manifest.name &&
text.includes(manifest.name.toLowerCase())) {
context.report({ node, messageId: "pluginName" });
}
},
};
},
});