mirror of
https://github.com/jsmorabito/obsidian-commander.git
synced 2026-07-22 06:40:31 +00:00
lint: replace remaining any-typed casts with proper types
Resolves the rest of the no-unsafe-* family by giving TypeScript enough information instead of suppressing it: - Extend the Obsidian module augmentation (types.ts) with App.commands.removeCommand and Vault.getConfig so util.tsx and main.ts no longer need @ts-ignore around them. - Cast event.target to the concrete HTMLElement subtype at each DOM event handler that reads .value/.checked/.offsetWidth (ChangeableText, MacroBuilder, commandComponent) instead of @ts-ignore/@ts-expect-error. - Cast JSON.parse() results to their known shape (MacroBuilder's macro clone, locales.test.ts's loadJson, main.ts's loadSettings) instead of letting `any` flow through. - Drop menuManager's unusual `this: (_item: MenuItem) => void` parameter and the .call(this, ...) it required - the returned closure never reads `this`, so this was both unnecessary and, under this project's non-strict bind/call/apply typing, exactly why the arguments passed through untyped. Calling the method directly is equivalent and fully typed. - Same root cause for MacroBuilderModal's this.close.bind(this) and executeMacro.test.ts's .bind() - replaced with equivalent non-bind forms. - confirmDeleteModal/mobileModifyModal: inline the h(...) call into render() instead of round-tripping through an intermediately VNode-typed field, which is where preact's h() lost prop typing. - vitest.config.ts-style import.meta.dirname swapped for fileURLToPath(import.meta.url) in eslint.config.mts, since the project's TS lib doesn't type import.meta.dirname. Type-level only; no behavior change beyond what's already covered by the two preceding commits.
This commit is contained in:
parent
fd04bcad28
commit
c476604d09
14 changed files with 54 additions and 45 deletions
|
|
@ -2,6 +2,10 @@ import tseslint from 'typescript-eslint';
|
|||
import obsidianmd from 'eslint-plugin-obsidianmd';
|
||||
import globals from 'globals';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default defineConfig(
|
||||
globalIgnores([
|
||||
|
|
@ -22,7 +26,7 @@ export default defineConfig(
|
|||
projectService: {
|
||||
allowDefaultProject: ['eslint.config.mts', 'manifest.json'],
|
||||
},
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
tsconfigRootDir: dirname,
|
||||
extraFileExtensions: ['.json'],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ function buildPlugin(macros: Macro[]): CommanderPlugin {
|
|||
commands: { executeCommandById },
|
||||
},
|
||||
} as unknown as CommanderPlugin;
|
||||
plugin.executeMacro = CommanderPluginClass.prototype.executeMacro.bind(plugin);
|
||||
plugin.executeMacro = CommanderPluginClass.prototype.executeMacro.bind(
|
||||
plugin
|
||||
) as CommanderPlugin["executeMacro"];
|
||||
return plugin;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ const __dirname = path.dirname(__filename);
|
|||
const LOCALE_DIR = path.resolve(__dirname, "../../locale");
|
||||
|
||||
function loadJson(file: string): Record<string, string> {
|
||||
return JSON.parse(readFileSync(path.join(LOCALE_DIR, file), "utf-8"));
|
||||
return JSON.parse(
|
||||
readFileSync(path.join(LOCALE_DIR, file), "utf-8")
|
||||
) as Record<string, string>;
|
||||
}
|
||||
|
||||
// Extract all {{placeholder}} tokens from a string
|
||||
|
|
|
|||
12
src/main.ts
12
src/main.ts
|
|
@ -142,7 +142,11 @@ export default class CommanderPlugin extends Plugin {
|
|||
}
|
||||
|
||||
private async loadSettings(): Promise<void> {
|
||||
const data = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
const data = Object.assign(
|
||||
{},
|
||||
DEFAULT_SETTINGS,
|
||||
await this.loadData()
|
||||
) as CommanderSettings;
|
||||
this.settings = data;
|
||||
}
|
||||
|
||||
|
|
@ -151,11 +155,7 @@ export default class CommanderPlugin extends Plugin {
|
|||
}
|
||||
|
||||
public listActiveToolbarCommands(): string[] {
|
||||
//@ts-ignore
|
||||
const activeCommands = this.app.vault.getConfig(
|
||||
"mobileToolbarCommands"
|
||||
);
|
||||
return activeCommands;
|
||||
return this.app.vault.getConfig("mobileToolbarCommands") as string[];
|
||||
}
|
||||
|
||||
public getCommands(): Command[] {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ abstract class Base extends CommandManagerBase {
|
|||
public reorder(): void {}
|
||||
|
||||
protected addRemovableCommand(
|
||||
this: (_item: MenuItem) => void,
|
||||
command: Command,
|
||||
cmdPair: CommandIconPair,
|
||||
plugin: CommanderPlugin,
|
||||
|
|
@ -197,8 +196,7 @@ export class EditorMenuCommandManager extends Base {
|
|||
continue;
|
||||
|
||||
menu.addItem(
|
||||
this.addRemovableCommand.call(
|
||||
this,
|
||||
this.addRemovableCommand(
|
||||
command,
|
||||
cmdPair,
|
||||
plugin,
|
||||
|
|
@ -253,8 +251,7 @@ export class FileMenuCommandManager extends Base {
|
|||
}
|
||||
|
||||
menu.addItem(
|
||||
this.addRemovableCommand.call(
|
||||
this,
|
||||
this.addRemovableCommand(
|
||||
command,
|
||||
cmdPair,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ declare module "obsidian" {
|
|||
[id: string]: Command;
|
||||
};
|
||||
executeCommandById: (id: string) => void;
|
||||
removeCommand: (id: string) => void;
|
||||
};
|
||||
plugins: {
|
||||
manifests: {
|
||||
|
|
@ -131,4 +132,8 @@ declare module "obsidian" {
|
|||
interface WorkspaceLeaf {
|
||||
containerEl: HTMLElement;
|
||||
}
|
||||
|
||||
interface Vault {
|
||||
getConfig(key: string): unknown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ export default function ChangeableText({
|
|||
) : (
|
||||
<span
|
||||
onDblClick={({ target }): void => {
|
||||
/* @ts-ignore */
|
||||
setWidth(target?.offsetWidth);
|
||||
setWidth((target as HTMLElement | null)?.offsetWidth ?? 0);
|
||||
setShowInput(true);
|
||||
}}
|
||||
aria-label={ariaLabel}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default function ({
|
|||
const [icon, setIcon] = useState(macro.icon || "star");
|
||||
const [startup, setStartup] = useState(macro.startup || false);
|
||||
const [macroCommands, setMacroCommands] = useState<MacroItem[]>(
|
||||
JSON.parse(JSON.stringify(macro.macro)) || []
|
||||
(JSON.parse(JSON.stringify(macro.macro)) as MacroItem[]) || []
|
||||
);
|
||||
|
||||
const handleAddCommand = async (): Promise<void> => {
|
||||
|
|
@ -237,8 +237,9 @@ export default function ({
|
|||
id="checkbox"
|
||||
checked={startup}
|
||||
onChange={({ target }): void => {
|
||||
//@ts-expect-error
|
||||
setStartup(target?.checked ?? false);
|
||||
setStartup(
|
||||
(target as HTMLInputElement | null)?.checked ?? false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="checkbox">Auto-Run on Startup</label>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default class MacroBuilderModal extends Modal {
|
|||
plugin: this.plugin,
|
||||
macro: this.macro,
|
||||
onSave: this.onSave,
|
||||
onCancel: this.close.bind(this),
|
||||
onCancel: (): void => this.close(),
|
||||
}),
|
||||
this.contentEl
|
||||
);
|
||||
|
|
|
|||
|
|
@ -100,8 +100,8 @@ export default function CommandComponent({
|
|||
const owningPlugin = plugin.app.plugins.manifests[owningPluginID!];
|
||||
const isInternal = !owningPlugin;
|
||||
const isChecked =
|
||||
Object.prototype.hasOwnProperty.call(cmd, "checkCallback") ||
|
||||
Object.prototype.hasOwnProperty.call(cmd, "editorCheckCallback");
|
||||
(Object.prototype.hasOwnProperty.call(cmd, "checkCallback") as boolean) ||
|
||||
(Object.prototype.hasOwnProperty.call(cmd, "editorCheckCallback") as boolean);
|
||||
|
||||
const modeIcon = getModeIcon(pair.mode);
|
||||
const modeName = pair.mode.match(/desktop|mobile|any/)
|
||||
|
|
@ -124,8 +124,9 @@ export default function CommandComponent({
|
|||
<ChangeableText
|
||||
ariaLabel={t("Double click to rename")}
|
||||
handleChange={({ target }): void => {
|
||||
/* @ts-ignore */
|
||||
handleRename(target?.value);
|
||||
handleRename(
|
||||
(target as HTMLInputElement | null)?.value ?? ""
|
||||
);
|
||||
}}
|
||||
value={pair.name}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -176,14 +176,14 @@ export default function settingTabComponent({
|
|||
}
|
||||
);
|
||||
|
||||
plugin.app.setting.activeTab.containerEl
|
||||
.querySelectorAll(
|
||||
".setting-item-heading"
|
||||
)[1]
|
||||
// @ts-ignore
|
||||
.nextSibling?.nextSibling?.nextSibling?.addClass?.(
|
||||
"cmdr-cta"
|
||||
);
|
||||
(
|
||||
plugin.app.setting.activeTab.containerEl
|
||||
.querySelectorAll(
|
||||
".setting-item-heading"
|
||||
)[1]
|
||||
.nextSibling?.nextSibling
|
||||
?.nextSibling as HTMLElement | null
|
||||
)?.addClass?.("cmdr-cta");
|
||||
}, 50);
|
||||
}}
|
||||
className="mod-cta"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Modal } from "obsidian";
|
||||
import { h, render, VNode } from "preact";
|
||||
import { h, render } from "preact";
|
||||
import t from "src/l10n";
|
||||
import CommanderPlugin from "src/main";
|
||||
import { confirmDeleteComponent } from "./components/confirmDeleteComponent";
|
||||
|
||||
export default class ConfirmDeleteModal extends Modal {
|
||||
private reactComponent: VNode;
|
||||
public remove: boolean;
|
||||
|
||||
public constructor(public plugin: CommanderPlugin) {
|
||||
|
|
@ -15,8 +14,7 @@ export default class ConfirmDeleteModal extends Modal {
|
|||
public async onOpen(): Promise<void> {
|
||||
this.titleEl.innerText = t("Remove Command");
|
||||
this.containerEl.style.zIndex = "99";
|
||||
this.reactComponent = h(confirmDeleteComponent, { modal: this });
|
||||
render(this.reactComponent, this.contentEl);
|
||||
render(h(confirmDeleteComponent, { modal: this }), this.contentEl);
|
||||
}
|
||||
|
||||
public async didChooseRemove(): Promise<boolean> {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { CommandIconPair } from "src/types";
|
||||
import { Modal } from "obsidian";
|
||||
import { h, render, VNode } from "preact";
|
||||
import { h, render } from "preact";
|
||||
import MobileModifyComponent from "./components/mobileModifyComponent";
|
||||
import CommanderPlugin from "src/main";
|
||||
|
||||
export default class MobileModifyModal extends Modal {
|
||||
private reactComponent: VNode;
|
||||
public remove: boolean;
|
||||
|
||||
public constructor(
|
||||
|
|
@ -21,11 +20,13 @@ export default class MobileModifyModal extends Modal {
|
|||
|
||||
public async onOpen(): Promise<void> {
|
||||
this.titleEl.innerText = this.pair.name;
|
||||
this.reactComponent = h(MobileModifyComponent, {
|
||||
plugin: this.plugin,
|
||||
modal: this,
|
||||
});
|
||||
render(this.reactComponent, this.contentEl);
|
||||
render(
|
||||
h(MobileModifyComponent, {
|
||||
plugin: this.plugin,
|
||||
modal: this,
|
||||
}),
|
||||
this.contentEl
|
||||
);
|
||||
}
|
||||
|
||||
public onClose(): void {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export async function chooseNewCommand(
|
|||
const command = await new AddCommandModal(plugin).awaitSelection();
|
||||
|
||||
let icon;
|
||||
if (!Object.prototype.hasOwnProperty.call(command, "icon")) {
|
||||
if (!(Object.prototype.hasOwnProperty.call(command, "icon") as boolean)) {
|
||||
icon = await new ChooseIconModal(plugin).awaitSelection();
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +141,6 @@ export function updateMacroCommands(plugin: CommanderPlugin): void {
|
|||
p.startsWith("cmdr:macro-")
|
||||
);
|
||||
for (const command of oldCommands) {
|
||||
//@ts-ignore
|
||||
plugin.app.commands.removeCommand(command);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue