mirror of
https://github.com/gtritchie/bulk-properties.git
synced 2026-07-22 14:10:30 +00:00
Address Obsidian review lint findings
Replace `any` casts with typed App extensions for the undocumented `app.setting` and `app.metadataTypeManager` APIs, remove sentence-case disables by rephrasing the empty-state link and fallback Notice, and drop `async` from `toggleSelection` (which never awaited). Tighten local ESLint config to catch similar issues going forward: enable `reportUnusedDisableDirectives` and re-enable `@typescript-eslint/require-await` (the obsidianmd preset disables it).
This commit is contained in:
parent
fdd32c79a6
commit
5db582c69a
3 changed files with 45 additions and 17 deletions
|
|
@ -5,6 +5,12 @@ import { globalIgnores } from "eslint/config";
|
|||
|
||||
export default tseslint.config(
|
||||
{
|
||||
// Report directive comments that silence rules which never fired.
|
||||
// Obsidian's plugin reviewer enables this; without it, stale
|
||||
// eslint-disable comments slip through local lint.
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: "error",
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
|
|
@ -22,6 +28,17 @@ export default tseslint.config(
|
|||
},
|
||||
},
|
||||
...obsidianmd.configs.recommended,
|
||||
{
|
||||
// The obsidianmd preset disables require-await, but Obsidian's
|
||||
// reviewer enforces it. Re-enable so local lint matches.
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
plugins: {
|
||||
'@typescript-eslint': tseslint.plugin,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/require-await": "error",
|
||||
},
|
||||
},
|
||||
globalIgnores([
|
||||
"node_modules",
|
||||
"dist",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,17 @@ import {
|
|||
import {withProgress} from "./progress";
|
||||
import {makeToggleAccessible, updateToggleAriaChecked} from "./accessible-toggle";
|
||||
|
||||
// `app.setting` is an undocumented internal API used by core plugins to
|
||||
// open the settings pane. Declared here as an optional property so it
|
||||
// can be accessed type-safely without reaching for `any`; all usages
|
||||
// are runtime-guarded.
|
||||
type AppWithSetting = App & {
|
||||
setting?: {
|
||||
open?: () => void;
|
||||
openTabById?: (id: string) => void;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a tag name against Obsidian's naming rules.
|
||||
* Returns null if valid, or a reason string if invalid.
|
||||
|
|
@ -167,7 +178,7 @@ export class BulkEditModal extends Modal {
|
|||
this.fileCheckboxes.set(file, checkbox);
|
||||
row.createEl("span", {text: file.path, cls: "bulk-properties-file-path"});
|
||||
checkbox.addEventListener("change", () => {
|
||||
void this.toggleSelection(file, checkbox);
|
||||
this.toggleSelection(file, checkbox);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -190,22 +201,18 @@ export class BulkEditModal extends Modal {
|
|||
|
||||
if (!hasEditableProperties) {
|
||||
const p = contentEl.createEl("p");
|
||||
p.appendText("No properties configured. Add properties in the ");
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case -- mid-sentence text
|
||||
const link = p.createEl("a", {text: "plugin settings", href: "#"});
|
||||
p.appendText("No properties configured. ");
|
||||
const link = p.createEl("a", {text: "Open settings to configure properties", href: "#"});
|
||||
link.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
this.close();
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call -- undocumented Obsidian API */
|
||||
const setting = (this.app as any).setting;
|
||||
const {setting} = this.app as AppWithSetting;
|
||||
if (typeof setting?.open === "function" && typeof setting?.openTabById === "function") {
|
||||
setting.open();
|
||||
setting.openTabById(this.plugin.manifest.id);
|
||||
} else {
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case -- navigation path
|
||||
new Notice("Open Settings → Community plugins → Bulk Properties to configure properties.");
|
||||
new Notice("Open the settings pane to configure properties.");
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
||||
});
|
||||
p.appendText(".");
|
||||
}
|
||||
|
|
@ -301,7 +308,7 @@ export class BulkEditModal extends Modal {
|
|||
this.fileSelection.set(file, selected);
|
||||
}
|
||||
|
||||
private async toggleSelection(file: TFile, checkbox: HTMLInputElement) {
|
||||
private toggleSelection(file: TFile, checkbox: HTMLInputElement): void {
|
||||
const desired = checkbox.checked;
|
||||
checkbox.disabled = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,16 @@ const PROPERTY_TYPE_LABELS: Record<PropertyType, string> = {
|
|||
text: "Text",
|
||||
};
|
||||
|
||||
// metadataTypeManager is an undocumented internal API — not in
|
||||
// obsidian.d.ts. Declared here as an optional property of App so all
|
||||
// access is type-safe without reaching for `any`. All access is also
|
||||
// runtime-guarded below.
|
||||
type AppWithMetadataTypeManager = App & {
|
||||
metadataTypeManager?: {
|
||||
getPropertyInfo?: (name: string) => { widget?: string } | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
// Uses Obsidian's undocumented metadataTypeManager to look up the type
|
||||
// assigned to a property in Settings → Properties. Returns null if the
|
||||
// API is unavailable, the property is unknown, or the widget value
|
||||
|
|
@ -79,13 +89,7 @@ function detectPropertyType(app: App, name: string): PropertyType | null {
|
|||
const known = new Set(getAllPropertyNames(app));
|
||||
if (!known.has(name)) return null;
|
||||
|
||||
// metadataTypeManager is an undocumented internal API — not in
|
||||
// obsidian.d.ts. All access is runtime-guarded; the any cast and
|
||||
// unsafe member accesses are intentional.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
||||
const mtm = (app as any).metadataTypeManager as
|
||||
| { getPropertyInfo?: (name: string) => { widget?: string } | undefined }
|
||||
| undefined;
|
||||
const mtm = (app as AppWithMetadataTypeManager).metadataTypeManager;
|
||||
if (!mtm || typeof mtm.getPropertyInfo !== "function") return null;
|
||||
const info = mtm.getPropertyInfo(name);
|
||||
if (!info || typeof info.widget !== "string") return null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue