Skip to content

Commit 9a932fb

Browse files
authored
Merge pull request #64 from philoserf/add-test-vault
feat: add hot-reload plugin to test vault
2 parents cd53076 + e8d7dc0 commit 9a932fb

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed

.gitignore

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
# Build artifacts
12
main.js
23
main.js.map
4+
5+
# Dependencies
36
node_modules/
4-
test-vault/.obsidian/plugins/obsidian-vault-changelog/
5-
!test-vault/.obsidian/plugins/journals/.hotreload
7+
8+
# Test vault - plugins
69
test-vault/.obsidian/plugins/*
7-
!test-vault/.obsidian/plugins/hotreload/main.js
8-
!test-vault/.obsidian/plugins/hotreload/manifest.json
10+
!test-vault/.obsidian/plugins/journals/.hotreload
11+
!test-vault/.obsidian/plugins/hot-reload/main.js
12+
!test-vault/.obsidian/plugins/hot-reload/manifest.json
13+
14+
# Test vault - config files
915
test-vault/.obsidian/app.json
1016
test-vault/.obsidian/appearance.json
1117
test-vault/.obsidian/core-plugins-migration.json
1218
test-vault/.obsidian/core-plugins.json
1319
test-vault/.obsidian/workspace.json
20+
21+
# Test vault - markdown files
1422
test-vault/**/*.md
1523
!test-vault/Change.md
1624
!test-vault/Changelog.md
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[
2-
"hot-reload",
3-
"obsidian-vault-changelog"
2+
"obsidian-vault-changelog",
3+
"hot-reload"
44
]
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const {Plugin, Notice, debounce} = require("obsidian");
2+
const fs = require("fs");
3+
4+
const watchNeeded = window.process.platform !== "darwin" && window.process.platform !== "win32";
5+
6+
module.exports = class HotReload extends Plugin {
7+
8+
statCache = new Map(); // path -> Stat
9+
queue = Promise.resolve();
10+
11+
run(val, err) {
12+
return this.queue = this.queue.then(val, err);
13+
}
14+
15+
reindexPlugins = debounce(() => this.run(() => this.getPluginNames()), 500, true);
16+
requestScan = debounce(() => this.run(() => this.checkVersions()), 250, true);
17+
18+
onload() {
19+
app.workspace.onLayoutReady(async ()=> {
20+
this.pluginReloaders = {};
21+
this.inProgress = null;
22+
await this.getPluginNames();
23+
this.registerEvent( this.app.vault.on("raw", this.requestScan));
24+
this.watch(".obsidian/plugins");
25+
this.requestScan();
26+
this.addCommand({
27+
id: "scan-for-changes",
28+
name: "Check plugins for changes and reload them",
29+
callback: () => this.requestScan()
30+
})
31+
});
32+
}
33+
34+
watch(path) {
35+
if (this.app.vault.adapter.watchers.hasOwnProperty(path)) return;
36+
const realPath = [this.app.vault.adapter.basePath, path].join("/");
37+
const lstat = fs.lstatSync(realPath);
38+
if (lstat && (watchNeeded || lstat.isSymbolicLink()) && fs.statSync(realPath).isDirectory()) {
39+
this.app.vault.adapter.startWatchPath(path, false);
40+
}
41+
}
42+
43+
async checkVersions() {
44+
const base = this.app.plugins.getPluginFolder();
45+
for (const dir of Object.keys(this.pluginNames)) {
46+
for (const file of ["manifest.json", "main.js", "styles.css", ".hotreload"]) {
47+
const path = `${base}/${dir}/${file}`;
48+
const stat = await app.vault.adapter.stat(path);
49+
if (stat) {
50+
if (this.statCache.has(path) && stat.mtime !== this.statCache.get(path).mtime) {
51+
this.onFileChange(path);
52+
}
53+
this.statCache.set(path, stat);
54+
}
55+
}
56+
}
57+
}
58+
59+
async getPluginNames() {
60+
const plugins = {}, enabled = new Set();
61+
for (const {id, dir} of Object.values(app.plugins.manifests)) {
62+
this.watch(dir);
63+
plugins[dir.split("/").pop()] = id;
64+
if (
65+
await this.app.vault.exists(dir+"/.git") ||
66+
await this.app.vault.exists(dir+"/.hotreload")
67+
) enabled.add(id);
68+
}
69+
this.pluginNames = plugins;
70+
this.enabledPlugins = enabled;
71+
}
72+
73+
onFileChange(filename) {
74+
if (!filename.startsWith(this.app.plugins.getPluginFolder()+"/")) return;
75+
const path = filename.split("/");
76+
const base = path.pop(), dir = path.pop();
77+
if (path.length === 1 && dir === "plugins") return this.watch(filename);
78+
if (path.length != 2) return;
79+
const plugin = dir && this.pluginNames[dir];
80+
if (base === "manifest.json" || base === ".hotreload" || base === ".git" || !plugin) return this.reindexPlugins();
81+
if (base !== "main.js" && base !== "styles.css") return;
82+
if (!this.enabledPlugins.has(plugin)) return;
83+
const reloader = this.pluginReloaders[plugin] || (
84+
this.pluginReloaders[plugin] = debounce(() => this.run(() => this.reload(plugin), console.error), 750, true)
85+
);
86+
reloader();
87+
}
88+
89+
async reload(plugin) {
90+
const plugins = app.plugins;
91+
92+
// Don't reload disabled plugins
93+
if (!plugins.enabledPlugins.has(plugin)) return;
94+
95+
await plugins.disablePlugin(plugin);
96+
console.debug("disabled", plugin);
97+
98+
// Ensure sourcemaps are loaded (Obsidian 14+)
99+
const oldDebug = localStorage.getItem("debug-plugin");
100+
localStorage.setItem("debug-plugin", "1");
101+
try {
102+
await plugins.enablePlugin(plugin);
103+
} finally {
104+
// Restore previous setting
105+
if (oldDebug === null) localStorage.removeItem("debug-plugin"); else localStorage.setItem("debug-plugin", oldDebug);
106+
}
107+
console.debug("enabled", plugin);
108+
new Notice(`Plugin "${plugin}" has been reloaded`);
109+
}
110+
}

0 commit comments

Comments
 (0)