chore: update libs

This commit is contained in:
Michael Naumov 2026-07-16 03:42:14 -06:00
parent a678d36a10
commit 62bab89d52
5 changed files with 76 additions and 107 deletions

View file

@ -30,7 +30,7 @@ Insert Multiple Attachments is an Obsidian plugin that allows inserting multiple
- **Root config files** are thin re-exports — actual logic lives in `scripts/` (`eslint.config.mts` → `scripts/eslint-config.ts`, etc.).
- **`src/`** — plugin source:
- `main.ts` — Obsidian entry point; imports the SCSS and default-exports the `Plugin` class.
- `plugin.ts``Plugin extends PluginBase`; `onloadImpl` wires up the settings component, settings tab, menu-event registrar, and the `CommandHandlerComponent` holding the `InvokeCommandHandler`.
- `plugin.ts``Plugin extends PluginBase`; `onloadImpl` wires up the settings component and settings tab, then registers the `InvokeCommandHandler` via the base-provided `this.commandHandlerComponent` (the active-file provider, command registrar, and menu-event registrar are owned by `PluginBase` since obsidian-dev-utils 86.0.0).
- `plugin-settings.ts``PluginSettings` data class: `attachmentLinksPrefix`, `attachmentLinksDelimiter` (default `\n\n`), `attachmentLinksSuffix`.
- `plugin-settings-component.ts``PluginSettingsComponent extends PluginSettingsComponentBase`; registers a legacy-settings converter mapping the old `shouldInsertDoubleLinesBetweenAttachmentLinks` flag to a delimiter.
- `plugin-settings-tab.ts``PluginSettingsTab extends PluginSettingsTabBase`; renders prefix/delimiter/suffix text settings with whitespace visualized as `␣`/`↵` via value converters.

48
package-lock.json generated
View file

@ -12,7 +12,7 @@
"@commitlint/cli": "^21.2.1",
"@commitlint/config-conventional": "^21.2.0",
"@commitlint/types": "^21.2.0",
"@obsidian-typings/obsidian-public-latest": "^6.22.0",
"@obsidian-typings/obsidian-public-latest": "^6.23.0",
"@total-typescript/ts-reset": "^0.6.1",
"@tsconfig/strictest": "^2.0.8",
"@types/node": "^26.1.1",
@ -25,8 +25,8 @@
"jsdom": "^29.1.1",
"nano-staged": "^1.0.2",
"obsidian": "^1.13.1",
"obsidian-dev-utils": "^85.1.0",
"obsidian-integration-testing": "^6.2.0",
"obsidian-dev-utils": "^86.0.0",
"obsidian-integration-testing": "^8.1.0",
"obsidian-test-mocks": "^3.6.0",
"vitest": "^4.1.10"
}
@ -2560,9 +2560,9 @@
}
},
"node_modules/@obsidian-typings/obsidian-public-1.12.7": {
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-1.12.7/-/obsidian-public-1.12.7-6.24.0.tgz",
"integrity": "sha512-Ktlvsen9jVDhDgupZp2UYjs14eJw0ESZCu28+iZZ1lVN7KBnsGM7xnq/3bPNrVE3FO1UniFRkDwMFcyrH44M2g==",
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-1.12.7/-/obsidian-public-1.12.7-6.26.0.tgz",
"integrity": "sha512-/EQqzZlpmub7nY3St+gaLISgoqTBo2cBFgLZZOdPZEz/VRaSLCK03g8X4TzDRIZ85uEGdu8VHoCPUm97tRaFuA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@ -2570,13 +2570,13 @@
}
},
"node_modules/@obsidian-typings/obsidian-public-latest": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-latest/-/obsidian-public-latest-6.22.0.tgz",
"integrity": "sha512-n9Bfwu3e+j8iMsTrF3EXs3W5KwnnIkmLZldohd2DCKg6NbZP9UmQeCi56aVX+7UgJRDPa3erd1XopWLANhoHyw==",
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-latest/-/obsidian-public-latest-6.24.0.tgz",
"integrity": "sha512-tRKsKw/SYiRB/3ucQIE3cKk8pQJIignsRe65fmb+/XbHIVetiHnO8Ke8PEN9WsLiY3tlNzNU+WGksA578JGaMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@obsidian-typings/obsidian-public-1.12.7": "^6.24.0"
"@obsidian-typings/obsidian-public-1.12.7": "^6.26.0"
}
},
"node_modules/@oxc-project/types": {
@ -5152,6 +5152,16 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/adm-zip": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
"integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
@ -12943,9 +12953,9 @@
}
},
"node_modules/obsidian-dev-utils": {
"version": "85.1.0",
"resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-85.1.0.tgz",
"integrity": "sha512-n+4F6/PtS9w80Ifei3cDhbrY6t3DUOSdM18HyxHn0w7B+reV0BIMX/tt+VX5GMQqMKgf0IUNA8U+TxBPZIGyjA==",
"version": "86.1.3",
"resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-86.1.3.tgz",
"integrity": "sha512-XH08KgaQmMULwL7/dEWEkEDI0Y3FXdZmTXuZBNfBmgZLmjmUkVn98y3Hz3bkJQCuE6ic6NmGjb5odZgBze1zdg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -12956,7 +12966,7 @@
"@eslint/config-helpers": "^0.6.0",
"@eslint/js": "^10.0.1",
"@lezer/common": "1.2.3",
"@obsidian-typings/obsidian-public-latest": "^6.22.0",
"@obsidian-typings/obsidian-public-latest": "^6.23.0",
"@stylistic/eslint-plugin": "^5.10.0",
"@tsconfig/strictest": "^2.0.8",
"@types/babel__core": "^7.20.5",
@ -12973,6 +12983,7 @@
"@types/shell-quote": "^1.7.5",
"@typescript-eslint/eslint-plugin": "^8.63.0",
"@typescript-eslint/parser": "^8.63.0",
"adm-zip": "^0.5.18",
"commander": "^15.0.0",
"compare-versions": "^6.1.1",
"cspell": "^10.0.1",
@ -13016,8 +13027,7 @@
"svelte-preprocess": "^6.0.5",
"type-fest": "^5.8.0",
"typescript": "6.0.3",
"typescript-eslint": "^8.63.0",
"unist-util-visit": "^5.1.0"
"typescript-eslint": "^8.63.0"
},
"engines": {
"node": ">=22.0.0"
@ -13096,9 +13106,9 @@
}
},
"node_modules/obsidian-integration-testing": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/obsidian-integration-testing/-/obsidian-integration-testing-6.2.0.tgz",
"integrity": "sha512-NSocP9kPgMhlIyTk/nbVDkP6o2AcvnNgAhrrKpky6EMHstXITWSnJTYPYV7mAHayD3DBT7/28vdxmD1U/cVKxw==",
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/obsidian-integration-testing/-/obsidian-integration-testing-8.2.0.tgz",
"integrity": "sha512-32RxOHXoEAFgkH9vtR80AhtPaxm3efIwMcASYWt+2DfLesDLgtUCmCpnQG38JA+HJoM0Y4+T0xZg+kLptQsiwQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View file

@ -48,7 +48,7 @@
"@commitlint/cli": "^21.2.1",
"@commitlint/config-conventional": "^21.2.0",
"@commitlint/types": "^21.2.0",
"@obsidian-typings/obsidian-public-latest": "^6.22.0",
"@obsidian-typings/obsidian-public-latest": "^6.23.0",
"@total-typescript/ts-reset": "^0.6.1",
"@tsconfig/strictest": "^2.0.8",
"@types/node": "^26.1.1",
@ -61,8 +61,8 @@
"jsdom": "^29.1.1",
"nano-staged": "^1.0.2",
"obsidian": "^1.13.1",
"obsidian-dev-utils": "^85.1.0",
"obsidian-integration-testing": "^6.2.0",
"obsidian-dev-utils": "^86.0.0",
"obsidian-integration-testing": "^8.1.0",
"obsidian-test-mocks": "^3.6.0",
"vitest": "^4.1.10"
}

View file

@ -2,14 +2,13 @@ import type {
App as AppOriginal,
PluginManifest
} from 'obsidian';
import type { CommandHandlerComponent } from 'obsidian-dev-utils/obsidian/command-handlers/command-handler-component';
import { AppActiveFileProvider } from 'obsidian-dev-utils/obsidian/active-file-provider';
import { CommandHandlerComponent } from 'obsidian-dev-utils/obsidian/command-handlers/command-handler-component';
import { PluginCommandRegistrar } from 'obsidian-dev-utils/obsidian/command-registrar';
import { MenuEventRegistrarComponent } from 'obsidian-dev-utils/obsidian/components/menu-event-registrar-component';
import { castTo } from 'obsidian-dev-utils/object-utils';
import { PluginSettingsTabComponent } from 'obsidian-dev-utils/obsidian/components/plugin-settings-tab-component';
import { PluginDataHandler } from 'obsidian-dev-utils/obsidian/data-handler';
import { PluginEventSourceImpl } from 'obsidian-dev-utils/obsidian/plugin/plugin-event-source';
import { strictProxy } from 'obsidian-dev-utils/strict-proxy';
import { App } from 'obsidian-test-mocks/obsidian';
import {
beforeEach,
@ -24,14 +23,6 @@ import { PluginSettingsComponent } from './plugin-settings-component.ts';
import { PluginSettingsTab } from './plugin-settings-tab.ts';
import { Plugin } from './plugin.ts';
// The real `PluginBase.onload()` loads dev-utils' own notice/context/debug components, which read a
// Shared-state bag off the app via `getObsidianDevUtilsState`. The strict App mock has no such bag, so
// Stub this one utility (return a fresh value wrapper per call) — mirroring dev-utils' own PluginBase test.
vi.mock('obsidian-dev-utils/obsidian/app', async (importOriginal) => ({
...await importOriginal<typeof import('obsidian-dev-utils/obsidian/app')>(),
getObsidianDevUtilsState: vi.fn((_app: unknown, _key: string, defaultValue: unknown) => ({ value: defaultValue }))
}));
// A dev-utils component added via `addChild` must be loadable, so its stub returns a real `Component`. The
// Flowing instance is the stub's return value (`mock.results[0].value`), not the discarded `this`.
interface ObsidianComponentModule {
@ -49,26 +40,10 @@ async function loadableComponentStub(): Promise<ReturnType<typeof vi.fn>> {
});
}
vi.mock('obsidian-dev-utils/obsidian/components/menu-event-registrar-component', async () => ({
MenuEventRegistrarComponent: await loadableComponentStub()
}));
vi.mock('obsidian-dev-utils/obsidian/components/plugin-settings-tab-component', async () => ({
PluginSettingsTabComponent: await loadableComponentStub()
}));
vi.mock('obsidian-dev-utils/obsidian/command-handlers/command-handler-component', async () => ({
CommandHandlerComponent: await loadableComponentStub()
}));
vi.mock('obsidian-dev-utils/obsidian/active-file-provider', () => ({
AppActiveFileProvider: vi.fn()
}));
vi.mock('obsidian-dev-utils/obsidian/command-registrar', () => ({
PluginCommandRegistrar: vi.fn()
}));
vi.mock('obsidian-dev-utils/obsidian/data-handler', () => ({
PluginDataHandler: vi.fn()
}));
@ -96,10 +71,6 @@ vi.mock('./plugin-settings-component.ts', () => ({
})
}));
const MockAppActiveFileProvider = vi.mocked(AppActiveFileProvider);
const MockCommandHandlerComponent = vi.mocked(CommandHandlerComponent);
const MockPluginCommandRegistrar = vi.mocked(PluginCommandRegistrar);
const MockMenuEventRegistrarComponent = vi.mocked(MenuEventRegistrarComponent);
const MockPluginSettingsTabComponent = vi.mocked(PluginSettingsTabComponent);
const MockPluginDataHandler = vi.mocked(PluginDataHandler);
const MockPluginEventSourceImpl = vi.mocked(PluginEventSourceImpl);
@ -116,30 +87,42 @@ const manifest: PluginManifest = {
version: '1.0.0'
};
// ODU 86.0.0 moved the command-handler component into `PluginBase` (`this.commandHandlerComponent`). Driving
// `onloadImpl()` directly with a seeded `_commandHandlerComponent` keeps this a focused wiring test — the base
// `onload()` (notice/context/debug components) is dev-utils' own concern, covered by its tests.
interface PluginInternals {
_commandHandlerComponent: CommandHandlerComponent;
onloadImpl(): void;
}
let app: AppOriginal;
function instanceOf(mock: ReturnType<typeof vi.fn>): unknown {
return mock.mock.results[0]?.value;
}
function seedAndRun(plugin: Plugin): ReturnType<typeof vi.fn> {
const internals = castTo<PluginInternals>(plugin);
const registerCommandHandlers = vi.fn();
internals._commandHandlerComponent = strictProxy<CommandHandlerComponent>({ registerCommandHandlers });
internals.onloadImpl();
return registerCommandHandlers;
}
describe('Plugin', () => {
beforeEach(() => {
vi.clearAllMocks();
const appMock = App.createConfigured__();
appMock.workspace.onLayoutReady = vi.fn((cb: () => void) => {
cb();
});
app = appMock.asOriginalType__();
app = App.createConfigured__().asOriginalType__();
});
it('should create a plugin instance', () => {
expect(new Plugin(app, manifest)).toBeInstanceOf(Plugin);
});
describe('onload', () => {
it('should create PluginSettingsComponent with the data handler and plugin event source', async () => {
describe('onloadImpl', () => {
it('should create PluginSettingsComponent with the data handler and plugin event source', () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
seedAndRun(plugin);
expect(MockPluginSettingsComponent).toHaveBeenCalledWith({
dataHandler: MockPluginDataHandler.mock.instances[0],
@ -147,9 +130,9 @@ describe('Plugin', () => {
});
});
it('should create PluginSettingsTab with the plugin and settings component', async () => {
it('should create PluginSettingsTab with the plugin and settings component', () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
seedAndRun(plugin);
expect(MockPluginSettingsTab).toHaveBeenCalledWith({
plugin,
@ -157,9 +140,9 @@ describe('Plugin', () => {
});
});
it('should create PluginSettingsTabComponent with the plugin and settings tab', async () => {
it('should create PluginSettingsTabComponent with the plugin and settings tab', () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
seedAndRun(plugin);
expect(MockPluginSettingsTabComponent).toHaveBeenCalledWith({
plugin,
@ -167,44 +150,33 @@ describe('Plugin', () => {
});
});
it('should create MenuEventRegistrarComponent with the app', async () => {
it('should create InvokeCommandHandler with the app and settings component', () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
expect(MockMenuEventRegistrarComponent).toHaveBeenCalledWith(app);
});
it('should create InvokeCommandHandler with the app and settings component', async () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
seedAndRun(plugin);
const params = MockInvokeCommandHandler.mock.calls[0]?.[0];
expect(params?.app).toBe(app);
expect(params?.pluginSettingsComponent).toBe(instanceOf(MockPluginSettingsComponent));
});
it('should create CommandHandlerComponent wiring the invoke command and menu registrar', async () => {
it('should register the invoke command handler on the base command-handler component', () => {
const plugin = new Plugin(app, manifest);
await plugin.onload();
const registerCommandHandlers = seedAndRun(plugin);
expect(MockCommandHandlerComponent).toHaveBeenCalledWith({
activeFileProvider: MockAppActiveFileProvider.mock.instances[0],
commandHandlers: [MockInvokeCommandHandler.mock.instances[0]],
commandRegistrar: MockPluginCommandRegistrar.mock.instances[0],
menuEventRegistrar: instanceOf(MockMenuEventRegistrarComponent),
pluginName: manifest.name
});
expect(registerCommandHandlers).toHaveBeenCalledOnce();
expect(registerCommandHandlers).toHaveBeenCalledWith([instanceOf(MockInvokeCommandHandler)]);
});
it('should add the four plugin components as children', async () => {
it('should add the two plugin components as children', () => {
const plugin = new Plugin(app, manifest);
const addChildSpy = vi.spyOn(plugin, 'addChild');
await plugin.onload();
seedAndRun(plugin);
expect(addChildSpy).toHaveBeenCalledWith(instanceOf(MockPluginSettingsComponent));
expect(addChildSpy).toHaveBeenCalledWith(instanceOf(MockPluginSettingsTabComponent));
expect(addChildSpy).toHaveBeenCalledWith(instanceOf(MockMenuEventRegistrarComponent));
expect(addChildSpy).toHaveBeenCalledWith(instanceOf(MockCommandHandlerComponent));
const EXPECTED_ADD_CHILD_CALLS = 2;
expect(addChildSpy).toHaveBeenCalledTimes(EXPECTED_ADD_CHILD_CALLS);
});
});
});

View file

@ -1,7 +1,3 @@
import { AppActiveFileProvider } from 'obsidian-dev-utils/obsidian/active-file-provider';
import { CommandHandlerComponent } from 'obsidian-dev-utils/obsidian/command-handlers/command-handler-component';
import { PluginCommandRegistrar } from 'obsidian-dev-utils/obsidian/command-registrar';
import { MenuEventRegistrarComponent } from 'obsidian-dev-utils/obsidian/components/menu-event-registrar-component';
import { PluginSettingsTabComponent } from 'obsidian-dev-utils/obsidian/components/plugin-settings-tab-component';
import { PluginDataHandler } from 'obsidian-dev-utils/obsidian/data-handler';
import { PluginBase } from 'obsidian-dev-utils/obsidian/plugin/plugin';
@ -28,20 +24,11 @@ export class Plugin extends PluginBase {
})
})
);
const menuEventRegistrar = this.addChild(new MenuEventRegistrarComponent(this.app));
this.addChild(
new CommandHandlerComponent({
activeFileProvider: new AppActiveFileProvider(this.app),
commandHandlers: [
new InvokeCommandHandler({
app: this.app,
pluginSettingsComponent
})
],
commandRegistrar: new PluginCommandRegistrar(this),
menuEventRegistrar,
pluginName: this.manifest.name
this.commandHandlerComponent.registerCommandHandlers([
new InvokeCommandHandler({
app: this.app,
pluginSettingsComponent
})
);
]);
}
}