Skip to content

Instantly share code, notes, and snippets.

@aidenlx
Last active December 17, 2021 05:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aidenlx/6067c943fbec8ead230f2b163bfd3bc8 to your computer and use it in GitHub Desktop.
Save aidenlx/6067c943fbec8ead230f2b163bfd3bc8 to your computer and use it in GitHub Desktop.
Example for inter-plugin API declaration in obisidian
// Import API
import "your-package-name"; // if no export member used
// --- OR ---
import { exportMember } from "your-package-name";
// NOTE: Calling `import` in any files will apply type declarations
// in `declare global` and `declare module obsidian` globally
// Access API with Method 1
import { API_NAME } from "your-package-name";
window[API_NAME];
// Access API with Method 2 (need access to App object)
app.plugins.enabledPlugins.has("your-plugin-id"); // check if enabled
app.plugins.plugins["your-plugin-id"]?.api; // check if api available directly
// Required if you need to extend obsidian's declarations
import "obsidian";
// NOTE: Don't export Obsidian undocumented APIs used internally,
// export them only when they are required for api consumer
// API INTERFACE
// ------------------------
export interface SomeApi { }
// EXPOSE API
// ------------------------
// Method 1: Export API to global scope
// Can use varible name to indicate major version
// Remember to use an unique name for your API variable
// (avoid using general name like: parse to prevent global namespace pollution)
declare global {
// Must use var, no const/let
var SomeApiv0: SomeApi | undefined;
}
export type API_NAME = "SomeApiv0"
// exported type API_NAME and global api name need to be synced manually for now
// Method 2: Access API via app.plugins
// Hard to access, but no risk of global namespace pollution
declare module "obsidian" {
interface App {
plugins: {
enabledPlugins: Set<string>;
plugins: {
["your-plugin-id"]?: {
api: SomeApi;
};
};
};
}
}
// CUSTOM EVENTS
// ------------------------
// Convert tuple to arguments of Event.on
type OnArgs<T> = T extends [infer A, ...infer B]
? A extends string
? [name: A, callback: (...args: B) => any]
: never
: never;
export type YourPluginEvents =
| [name: "your-plugin-id:ready", api: SomeApi]
| [name: "your-plugin-id:event-name"]
| [name: "your-plugin-id:event-with-data-name", data: string];
type EventsOnArgs = OnArgs<YourPluginEvents>;
declare module "obsidian" {
interface Vault {
on(...args: EventsOnArgs): EventRef;
}
}
// If your api declaration contains no export member,
// add this line to the end
export {}
import { Plugin } from "obsidian";
import { YourPluginEvents, SomeApi } from "./api";
// --- Method 1 ---
import { API_NAME } from "./api";
const API_NAME: API_NAME extends keyof typeof window ? API_NAME : never =
"SomeApiv0" as const; // this line will throw error if name out of sync
export default class extends Plugin {
trigger(...args: YourPluginEvents): void {
const [name, ...data] = args;
this.app.vault.trigger(name, ...data);
}
// If API need to be initialized
api?: SomeApi;
onload() {
const api = {};
this.api = api;
window[API_NAME] = api;
this.register(() => (window[API_NAME] = undefined));
this.trigger("your-plugin-id:ready", api);
}
// --- OR ---
// If API is available when plugin is constrcuted
api: SomeApi;
constructor(app: any, manifest: any) {
super(app, manifest);
const api = {};
this.api = api;
window[API_NAME] = api;
this.register(() => (window[API_NAME] = undefined));
this.trigger("your-plugin-id:ready", api);
}
}
// --- Method 2 ---
export default class extends Plugin {
trigger(...args: YourPluginEvents): void {
const [name, ...data] = args;
this.app.vault.trigger(name, ...data);
}
// If API need to be initialized
api?: SomeApi;
onload() {
this.api = {};
this.trigger("your-plugin-id:ready", api);
}
// --- OR ---
// If API is available when class is constrcuted
api: SomeApi = {}
onload() {
this.trigger("your-plugin-id:ready", api);
}
}
{
"name": "your-package-name",
"version": "0.0.0",
"types": "src/api.d.ts",
"files": ["src/api.d.ts"],
"dependencies": {
"obsidian": "^0.12.11"
}
}
@aidenlx
Copy link
Author

aidenlx commented Sep 3, 2021

Folder structure for plugin in this example:

  • your-plugin
    • package.json
    • src/
    • main.ts
    • api.d.ts
  • api-customer
    • .../api-customer.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment