Skip to content

Commit

Permalink
Update API publish method, event definition method and simplify build…
Browse files Browse the repository at this point in the history
… configs (blacksmithgu#678)

* build: simplify rollup config and tsconfig

* feat(api): update api

add utils in exported package to get api; api interface is defined
separately; add ver utils to check dataview version; update events
definition method

* docs(develop-against-dataview): update api usage; remove api-ready evt section

* refactor: adjust interface name

* refactor(api): improve event definition method

now events are defined in index.ts and can includes comments

* refactor: rename types folder to typings

* style: code formatting

* refactor(api): rename `ver` to `version`

* build: remove redundant ver injection in build process

remove redundant version injection in build process, keep only the build
date

* refactor(api): rename `version.verNum` to `version.current`
  • Loading branch information
aidenlx authored Nov 26, 2021
1 parent d56d1bc commit 743c89e
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 191 deletions.
70 changes: 6 additions & 64 deletions docs/docs/plugin/develop-against-dataview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,17 @@ Dataview includes a high-level plugin-facing API as well as TypeScript definitio
for your plugin, follow these steps:

1. Install the utility library and types via `npm install -D obsidian-dataview` in your plugin directory.
2. Create a types file `types.d.ts`, with the contents below, which adds some dataview-related event typings and
provides access to `app.plugins`:

~~~ts
import "obsidian";
import { DataviewApi } from "obsidian-dataview";

declare module "obsidian" {
interface App {
plugins: {
enabledPlugins: Set<string>;
plugins: {
[id: string]: any;
dataview?: {
api?: DataviewApi;
};
};
};
}
interface MetadataCache {
on(
name: "dataview:api-ready",
callback: (api: DataviewPlugin["api"]) => any,
ctx?: any
): EventRef;
on(
name: "dataview:metadata-change",
callback: (
...args:
| [op: "rename", file: TAbstractFile, oldPath: string]
| [op: "delete", file: TFile]
| [op: "update", file: TFile]
) => any,
ctx?: any
): EventRef;
}
}
~~~
2. import utils to use Dataview API: `import { getAPI, isPluginEnabled } from "obsidian-dataview";`

Following these steps will allow you to access Dataview in a typed way, including doing things such as:

- **Checking if Dataview is enabled**: `plugin.app.enabledPlugins.has("dataview")`.
- **Accessing the Dataview API**: `plugin.app.plugins.dataview?.api`.
- **Checking if Dataview is enabled**: `isPluginEnabled(plugin.app)`.
- **Accessing the Dataview API**: `getAPI(plugin.app)` or just `getAPI()` (require version 0.4.22+), will return undefined if Dataview API is not available.
- **Check and compare Dataview API version**: use utils provided in [`api.ver`](../../../src/types/api.ts) (require version 0.4.22+)
- **Bind to Dataview events**: `plugin.registerEvent(plugin.app.metadataCache.on("dataview:...", (...) => ...))`.

## Using the Dataview API Programatically

The Dataview API takes a short amount of time to initialize before being available (during which it may be `undefined`
or in an unknown state). To ensure the API is available, you can wait on the metadata cache `dataview:api-ready` event,
such as in the idiom below:

~~~ts
async onload() {
const doSomethingWith = (api: DataviewPlugin["api"]) => {
// do something
};

if (this.app.enabledPlugins.has("dataview")) {
const api = this.app.plugins.dataview?.api;
if (api) doSomethingWith(api);
else
this.registerEvent(
this.app.metadataCache.on("dataview:api-ready", (api) =>
doSomethingWith(api)
)
);
}
}
~~~
> - For full API definitions available, check [api.ts](../../../src/types/api.ts)
> - For all events hooked on MetadataCache, check [index.ts](../../../src/index.ts)
## Value Utilities

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
"@types/papaparse": "^5.2.6",
"@types/parsimmon": "^1.10.6",
"@zerollup/ts-transform-paths": "^1.7.18",
"compare-versions": "^4.1.1",
"jest": "^27.1.0",
"obsidian": "^0.12.11",
"prettier": "2.3.2",
"rollup": "^2.56.3",
"rollup-jest": "^1.1.3",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-typescript2": "^0.30.0",
"rollup-plugin-typescript2": "^0.31.0",
"rollup-plugin-version-injector": "^1.3.3",
"rollup-plugin-web-worker-loader": "^1.6.1",
"ts-jest": "^27.0.5",
Expand Down
89 changes: 37 additions & 52 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,67 @@ import ttypescript from "ttypescript";
import typescript2 from "rollup-plugin-typescript2";
import versionInjector from "rollup-plugin-version-injector";

const LIBRARY_CONFIG = {
input: "src/index.ts",
output: {
dir: "lib",
sourcemap: true,
format: "cjs",
},
const BASE_CONFIG = {
input: "src/main.ts",
external: ["obsidian"],
plugins: [
typescript2({
tsconfig: "tsconfig-lib.json",
typescript: ttypescript,
}),
nodeResolve({ browser: true }),
commonjs(),
webWorker({ inline: true, forceInline: true, targetPlatform: "browser" }),
versionInjector({ logLevel: "warn" }),
],
onwarn: (warning, warn) => {
// Sorry rollup, but we're using eval...
if (/Use of eval is strongly discouraged/.test(warning.message)) return;
warn(warning);
},
};

const PROD_PLUGIN_CONFIG = {
input: "src/main.ts",
output: {
dir: "build",
sourcemap: "inline",
sourcemapExcludeSources: true,
format: "cjs",
exports: "default",
},
external: ["obsidian"],
plugins: [
const getRollupPlugins = (tsconfig, ...plugins) =>
[
typescript2(tsconfig),
nodeResolve({ browser: true }),
commonjs(),
webWorker({ inline: true, forceInline: true, targetPlatform: "browser" }),
typescript2({ tsconfig: "tsconfig.json" }),
versionInjector({ logLevel: "warn" }),
],
onwarn: (warning, warn) => {
// Sorry rollup, but we're using eval...
if (/Use of eval is strongly discouraged/.test(warning.message)) return;
warn(warning);
},
};
].concat(plugins);

const DEV_PLUGIN_CONFIG = {
input: "src/main.ts",
...BASE_CONFIG,
output: {
dir: "test-vault/.obsidian/plugins/dataview",
format: "cjs",
sourcemap: "inline",
format: "cjs",
exports: "default",
},
external: ["obsidian"],
plugins: [
nodeResolve({ browser: true }),
commonjs(),
webWorker({ inline: true, forceInline: true, targetPlatform: "browser" }),
typescript2({ tsconfig: "tsconfig.json" }),
versionInjector({ logLevel: "warn" }),
plugins: getRollupPlugins(
undefined,
copy({
targets: [
{ src: "manifest.json", dest: "test-vault/.obsidian/plugins/dataview/" },
{ src: "styles.css", dest: "test-vault/.obsidian/plugins/dataview/" },
],
}),
],
onwarn: (warning, warn) => {
// Sorry rollup, but we're using eval...
if (/Use of eval is strongly discouraged/.test(warning.message)) return;
warn(warning);
})
),
};

const PROD_PLUGIN_CONFIG = {
...BASE_CONFIG,
output: {
dir: "build",
sourcemap: "inline",
sourcemapExcludeSources: true,
format: "cjs",
exports: "default",
},
plugins: getRollupPlugins(),
};

const LIBRARY_CONFIG = {
...BASE_CONFIG,
input: "src/index.ts",
output: {
dir: "lib",
sourcemap: true,
format: "cjs",
},
plugins: getRollupPlugins(
{ tsconfig: "tsconfig-lib.json", typescript: ttypescript },
copy({ targets: [{ src: "src/types/*.d.ts", dest: "lib/types" }] })
),
};

let configs = [];
Expand Down
6 changes: 4 additions & 2 deletions src/api/inline-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class DataviewInlineApi {
container: HTMLElement,
app: App,
settings: DataviewSettings,
verNum: string,
currentFilePath: string
) {
this.index = index;
Expand All @@ -91,7 +92,7 @@ export class DataviewInlineApi {
this.currentFilePath = currentFilePath;
this.settings = settings;

this.api = new DataviewApi(this.app, this.index, this.settings);
this.api = new DataviewApi(this.app, this.index, this.settings, verNum);
this.io = new DataviewInlineIOApi(this.api.io, this.currentFilePath);

// Set up the evaluation context with variables from the current file.
Expand Down Expand Up @@ -324,8 +325,9 @@ export function makeApiContext(
component: Component,
app: App,
settings: DataviewSettings,
verNum: string,
container: HTMLElement,
originFile: string
): DataviewInlineApi {
return new DataviewInlineApi(index, component, container, app, settings, originFile);
return new DataviewInlineApi(index, component, container, app, settings, verNum, originFile);
}
25 changes: 22 additions & 3 deletions src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import { Context } from "expression/context";
import { defaultLinkHandler } from "query/engine";
import { DateTime, Duration } from "luxon";
import * as Luxon from "luxon";
import { compare, satisfies } from "compare-versions";
import { DvAPIInterface, DvIOAPIInterface } from "../typings/api";

/** Asynchronous API calls related to file / system IO. */
export class DataviewIOApi {
export class DataviewIOApi implements DvIOAPIInterface {
public constructor(public api: DataviewApi) {}

/** Load the contents of a CSV asynchronously, returning a data array of rows (or undefined if it does not exist). */
Expand Down Expand Up @@ -50,7 +52,7 @@ export class DataviewIOApi {
}
}

export class DataviewApi {
export class DataviewApi implements DvAPIInterface {
/** Evaluation context which expressions can be evaluated in. */
public evaluationContext: Context;
public io: DataviewIOApi;
Expand All @@ -61,12 +63,29 @@ export class DataviewApi {
/** Re-exporting of luxon for people who can't easily require it. Sorry! */
public luxon = Luxon;

public constructor(public app: App, public index: FullIndex, public settings: DataviewSettings) {
public constructor(
public app: App,
public index: FullIndex,
public settings: DataviewSettings,
private verNum: string
) {
this.evaluationContext = new Context(defaultLinkHandler(index, ""), settings);
this.func = Functions.bindAll(DEFAULT_FUNCTIONS, this.evaluationContext);
this.io = new DataviewIOApi(this);
}

/** utils to check api version */
public version: DvAPIInterface["version"] = (() => {
const { verNum: version } = this;
return {
get current() {
return version;
},
compare: (op, ver) => compare(version, ver, op),
satisfies: range => satisfies(version, range),
};
})();

/////////////////////////////
// Index + Data Collection //
/////////////////////////////
Expand Down
27 changes: 12 additions & 15 deletions src/data/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/** Stores various indices on all files in the vault to make dataview generation fast. */
import { Result } from "api/result";
import { DataObject } from "data/value";
import { MetadataCache, TAbstractFile, TFile, Vault } from "obsidian";
import { MetadataCache, TFile, Vault } from "obsidian";
import { getParentFolder } from "util/normalize";
import { PageMetadata } from "data/metadata";
import { ParsedMarkdown, parsePage } from "data/parse/markdown";
import { DateTime } from "luxon";
import { parseCsv } from "data/parse/csv";
import { FileImporter } from "data/import/import-manager";
import { IndexEvtFullName, IndexEvtTriggerArgs } from "../typings/events";

/** A generic index which indexes variables of the form key -> value[], allowing both forward and reverse lookups. */
export class IndexMap {
Expand Down Expand Up @@ -84,19 +85,11 @@ export class IndexMap {
}
}

/** Lists all possible index events. */
export interface IndexEvents {
/** Called when dataview metadata for a file changes. */
trigger(evt: "dataview:metadata-change", type: "rename", file: TAbstractFile, oldPath: string): void;
/** Called when a file is deleted from the dataview index. */
trigger(evt: "dataview:metadata-change", type: "update" | "delete", file: TAbstractFile): void;
}

/** Aggregate index which has several sub-indices and will initialize all of them. */
export class FullIndex {
/** Generate a full index from the given vault. */
public static create(vault: Vault, metadata: MetadataCache, events: IndexEvents): FullIndex {
return new FullIndex(vault, metadata, events);
public static create(vault: Vault, metadata: MetadataCache): FullIndex {
return new FullIndex(vault, metadata);
}

/* Maps path -> markdown metadata for all markdown pages. */
Expand Down Expand Up @@ -125,7 +118,7 @@ export class FullIndex {
public importer: FileImporter;

/** Construct a new index over the given vault and metadata cache. */
private constructor(public vault: Vault, public metadataCache: MetadataCache, public events: IndexEvents) {
private constructor(public vault: Vault, public metadataCache: MetadataCache) {
this.pages = new Map();
this.tags = new IndexMap();
this.etags = new IndexMap();
Expand All @@ -141,6 +134,10 @@ export class FullIndex {
this.csv = new CsvCache(this.vault);
}

trigger(...args: IndexEvtTriggerArgs): void {
this.metadataCache.trigger("dataview:metadata-change" as IndexEvtFullName, ...args);
}

/** Runs through the whole vault to set up initial file */
public initialize() {
// Traverse all markdown files & fill in initial data.
Expand All @@ -165,7 +162,7 @@ export class FullIndex {
}

this.revision += 1;
this.events.trigger("dataview:metadata-change", "rename", file, oldPath);
this.trigger("rename", file, oldPath);
});

// File creation does cause a metadata change, but deletes do not. Clear the caches for this.
Expand All @@ -180,7 +177,7 @@ export class FullIndex {
this.folders.delete(file.path);

this.revision += 1;
this.events.trigger("dataview:metadata-change", "delete", file);
this.trigger("delete", file);
});

// Initialize sub-indices.
Expand All @@ -206,7 +203,7 @@ export class FullIndex {
this.folders.set(file.path, new Set<string>([getParentFolder(file.path)]));

this.revision += 1;
this.events.trigger("dataview:metadata-change", "update", file);
this.trigger("update", file);
}
}

Expand Down
Loading

0 comments on commit 743c89e

Please sign in to comment.