refactor bridge plugin into virtual module
This commit is contained in:
parent
69f8320d05
commit
956a11d0cd
17 changed files with 225 additions and 148 deletions
|
|
@ -1,46 +1,42 @@
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const {
|
|
||||||
installObsidianPlugin,
|
|
||||||
isObsidianPluginInstalled,
|
|
||||||
} = require("./plugin-system/obsidian-plugin");
|
|
||||||
|
|
||||||
const BRIDGE_PLUGIN_ID = "ignis-bridge";
|
const BRIDGE_PLUGIN_ID = "ignis-bridge";
|
||||||
const BRIDGE_PLUGIN_DIR = path.join(__dirname, "..", "..", "..", "packages", "bridge");
|
|
||||||
|
|
||||||
// .ignis metadata helpers
|
// Old vaults still have bridge in .obsidian/plugins from before it became virtual.
|
||||||
|
async function migratePluginFromVault(vaultPath, vaultName, pluginId) {
|
||||||
|
let didWork = false;
|
||||||
|
|
||||||
async function getIgnisMeta(vaultPath) {
|
const pluginDir = path.join(vaultPath, ".obsidian", "plugins", pluginId);
|
||||||
const metaFile = path.join(vaultPath, ".ignis", "meta.json");
|
|
||||||
|
if (await fs.promises.stat(pluginDir).catch(() => null)) {
|
||||||
|
await fs.promises.rm(pluginDir, { recursive: true, force: true });
|
||||||
|
didWork = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpFile = path.join(vaultPath, ".obsidian", "community-plugins.json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await fs.promises.readFile(metaFile, "utf-8");
|
const list = JSON.parse(await fs.promises.readFile(cpFile, "utf-8"));
|
||||||
return JSON.parse(content);
|
|
||||||
} catch {
|
if (Array.isArray(list)) {
|
||||||
return {};
|
const filtered = list.filter((id) => id !== pluginId);
|
||||||
|
|
||||||
|
if (filtered.length !== list.length) {
|
||||||
|
await fs.promises.writeFile(cpFile, JSON.stringify(filtered));
|
||||||
|
didWork = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
if (didWork) {
|
||||||
|
console.log(`[ignis] Migrated ${pluginId} out of vault: ${vaultName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return didWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setIgnisMeta(vaultPath, data) {
|
async function migratePluginsFromAllVaults(vaultRoot, pluginIds) {
|
||||||
const ignisDir = path.join(vaultPath, ".ignis");
|
|
||||||
const metaFile = path.join(ignisDir, "meta.json");
|
|
||||||
|
|
||||||
await fs.promises.mkdir(ignisDir, { recursive: true });
|
|
||||||
await fs.promises.writeFile(metaFile, JSON.stringify(data, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bridge plugin install/check
|
|
||||||
|
|
||||||
async function isBridgePluginInstalled(vaultPath) {
|
|
||||||
return isObsidianPluginInstalled(BRIDGE_PLUGIN_ID, vaultPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function installBridgePlugin(vaultPath) {
|
|
||||||
const result = await installObsidianPlugin(BRIDGE_PLUGIN_DIR, vaultPath);
|
|
||||||
return result.installed;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateBridgePluginInAllVaults(vaultRoot) {
|
|
||||||
if (!(await fs.promises.stat(vaultRoot).catch(() => null))) {
|
if (!(await fs.promises.stat(vaultRoot).catch(() => null))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -53,18 +49,14 @@ async function updateBridgePluginInAllVaults(vaultRoot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const vaultPath = path.join(vaultRoot, entry.name);
|
const vaultPath = path.join(vaultRoot, entry.name);
|
||||||
const installed = await installBridgePlugin(vaultPath);
|
|
||||||
|
|
||||||
if (installed) {
|
for (const pluginId of pluginIds) {
|
||||||
console.log(`[ignis] Installed bridge plugin in vault: ${entry.name}`);
|
await migratePluginFromVault(vaultPath, entry.name, pluginId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
installBridgePlugin,
|
BRIDGE_PLUGIN_ID,
|
||||||
updateBridgePluginInAllVaults,
|
migratePluginsFromAllVaults,
|
||||||
isBridgePluginInstalled,
|
|
||||||
getIgnisMeta,
|
|
||||||
setIgnisMeta,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Vault provisioning for demo sessions.
|
// Vault provisioning for demo sessions.
|
||||||
//
|
//
|
||||||
// Copies the template into a session-prefixed dir, installs the bridge plugin, and registers the vault on the session.
|
// Copies the template into a session-prefixed dir and registers the vault on the session.
|
||||||
// Re-provisions if disk was wiped under an existing session.
|
// Re-provisions if disk was wiped under an existing session.
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
@ -8,7 +8,6 @@ const fsp = fs.promises;
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const config = require("../config");
|
const config = require("../config");
|
||||||
const { installBridgePlugin } = require("../bridge-plugin");
|
|
||||||
const bootstrapRoutes = require("../routes/bootstrap");
|
const bootstrapRoutes = require("../routes/bootstrap");
|
||||||
|
|
||||||
const { sessions, makeStorageName } = require("./demo-sessions");
|
const { sessions, makeStorageName } = require("./demo-sessions");
|
||||||
|
|
@ -96,9 +95,6 @@ async function provisionVault(sessionId, userVaultName) {
|
||||||
// Copy template (default: Welcome.md, Getting Started.md, .obsidian/*).
|
// Copy template (default: Welcome.md, Getting Started.md, .obsidian/*).
|
||||||
await fsp.cp(config.demoTemplateDir, vaultPath, { recursive: true });
|
await fsp.cp(config.demoTemplateDir, vaultPath, { recursive: true });
|
||||||
|
|
||||||
// Install bridge plugin
|
|
||||||
await installBridgePlugin(vaultPath);
|
|
||||||
|
|
||||||
config.refreshVaults();
|
config.refreshVaults();
|
||||||
bootstrapRoutes.invalidateVault(storageName);
|
bootstrapRoutes.invalidateVault(storageName);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ const {
|
||||||
watcher,
|
watcher,
|
||||||
writeCoalescer,
|
writeCoalescer,
|
||||||
} = require("@ignis/server-core");
|
} = require("@ignis/server-core");
|
||||||
const { updateBridgePluginInAllVaults } = require("./bridge-plugin");
|
const {
|
||||||
|
BRIDGE_PLUGIN_ID,
|
||||||
|
migratePluginsFromAllVaults,
|
||||||
|
} = require("./bridge-plugin");
|
||||||
const { initPlugins, shutdownPlugins } = require("./plugin-system/manager");
|
const { initPlugins, shutdownPlugins } = require("./plugin-system/manager");
|
||||||
const pluginRoutes = require("./routes/plugins");
|
const pluginRoutes = require("./routes/plugins");
|
||||||
writeCoalescer.configure({ writeCoalesceMs: config.writeCoalesceMs });
|
writeCoalescer.configure({ writeCoalesceMs: config.writeCoalesceMs });
|
||||||
|
|
@ -170,7 +173,7 @@ const server = app.listen(config.port, async () => {
|
||||||
console.log(`[ignis] Vault root: ${config.vaultRoot}`);
|
console.log(`[ignis] Vault root: ${config.vaultRoot}`);
|
||||||
console.log(`[ignis] Vaults: ${Object.keys(config.vaults).join(", ")}`);
|
console.log(`[ignis] Vaults: ${Object.keys(config.vaults).join(", ")}`);
|
||||||
|
|
||||||
await updateBridgePluginInAllVaults(config.vaultRoot);
|
await migratePluginsFromAllVaults(config.vaultRoot, [BRIDGE_PLUGIN_ID]);
|
||||||
await initPlugins({ app, config, wss, watcher });
|
await initPlugins({ app, config, wss, watcher });
|
||||||
bootstrapRoutes
|
bootstrapRoutes
|
||||||
.warmUp()
|
.warmUp()
|
||||||
|
|
|
||||||
16
apps/ignis-server/server/routes/bootstrap.js
vendored
16
apps/ignis-server/server/routes/bootstrap.js
vendored
|
|
@ -9,7 +9,6 @@ const fsp = fs.promises;
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const zlib = require("zlib");
|
const zlib = require("zlib");
|
||||||
const config = require("../config");
|
const config = require("../config");
|
||||||
const { isBridgePluginInstalled, getIgnisMeta } = require("../bridge-plugin");
|
|
||||||
const { getDiscoveredPlugins } = require("../plugin-system/manager");
|
const { getDiscoveredPlugins } = require("../plugin-system/manager");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
@ -76,20 +75,13 @@ async function walkTree(rootPath) {
|
||||||
return { tree, dirMtimes };
|
return { tree, dirMtimes };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildVaultInfo(vaultId, vaultPath) {
|
function buildVaultInfo(vaultId, vaultPath) {
|
||||||
const pluginInstalled = await isBridgePluginInstalled(vaultPath);
|
|
||||||
const ignisMeta = await getIgnisMeta(vaultPath);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: vaultId,
|
id: vaultId,
|
||||||
name: vaultId,
|
name: vaultId,
|
||||||
path: vaultPath,
|
path: vaultPath,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
version: config.obsidianVersion,
|
version: config.obsidianVersion,
|
||||||
ignisPlugin: {
|
|
||||||
installed: pluginInstalled,
|
|
||||||
prompted: ignisMeta.pluginPrompted || false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,10 +126,8 @@ async function buildEntry(vaultId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
const [vault, { tree, dirMtimes }] = await Promise.all([
|
const vault = buildVaultInfo(vaultId, vaultPath);
|
||||||
buildVaultInfo(vaultId, vaultPath),
|
const { tree, dirMtimes } = await walkTree(vaultPath);
|
||||||
walkTree(vaultPath),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
vault,
|
vault,
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,6 @@ const express = require("express");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const config = require("../config");
|
const config = require("../config");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const {
|
|
||||||
isBridgePluginInstalled,
|
|
||||||
getIgnisMeta,
|
|
||||||
setIgnisMeta,
|
|
||||||
installBridgePlugin,
|
|
||||||
} = require("../bridge-plugin");
|
|
||||||
const bootstrapRoutes = require("./bootstrap");
|
const bootstrapRoutes = require("./bootstrap");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
@ -34,19 +28,12 @@ router.get("/info", async (req, res) => {
|
||||||
return res.status(404).json({ error: "Vault not found", id: vaultId });
|
return res.status(404).json({ error: "Vault not found", id: vaultId });
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginInstalled = await isBridgePluginInstalled(vaultPath);
|
|
||||||
const ignisMeta = await getIgnisMeta(vaultPath);
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
id: vaultId,
|
id: vaultId,
|
||||||
name: vaultId,
|
name: vaultId,
|
||||||
path: vaultPath,
|
path: vaultPath,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
version: config.obsidianVersion,
|
version: config.obsidianVersion,
|
||||||
ignisPlugin: {
|
|
||||||
installed: pluginInstalled,
|
|
||||||
prompted: ignisMeta.pluginPrompted || false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -66,8 +53,6 @@ router.post("/create", async (req, res) => {
|
||||||
recursive: false,
|
recursive: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await installBridgePlugin(vaultPath);
|
|
||||||
|
|
||||||
config.refreshVaults();
|
config.refreshVaults();
|
||||||
bootstrapRoutes.invalidateVault(name);
|
bootstrapRoutes.invalidateVault(name);
|
||||||
|
|
||||||
|
|
@ -138,42 +123,4 @@ router.delete("/remove", async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST /api/vault/install-plugin { vault, dismiss } - install plugin or mark as prompted
|
|
||||||
router.post("/install-plugin", async (req, res) => {
|
|
||||||
const vaultId = req.body?.vault;
|
|
||||||
const dismiss = req.body?.dismiss || false;
|
|
||||||
|
|
||||||
if (!vaultId) {
|
|
||||||
return res.status(400).json({ error: "Missing vault ID" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const vaultPath = config.getVaultPath(vaultId);
|
|
||||||
|
|
||||||
if (!vaultPath) {
|
|
||||||
return res.status(404).json({ error: "Vault not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const meta = await getIgnisMeta(vaultPath);
|
|
||||||
|
|
||||||
if (dismiss) {
|
|
||||||
// User clicked "Don't Ask Again" or "Not Now"
|
|
||||||
meta.pluginPrompted = true;
|
|
||||||
await setIgnisMeta(vaultPath, meta);
|
|
||||||
|
|
||||||
return res.json({ ok: true, prompted: true });
|
|
||||||
} else {
|
|
||||||
// User wants to install the plugin
|
|
||||||
const installed = await installBridgePlugin(vaultPath);
|
|
||||||
|
|
||||||
meta.pluginPrompted = true;
|
|
||||||
await setIgnisMeta(vaultPath, meta);
|
|
||||||
|
|
||||||
return res.json({ ok: true, installed, prompted: true });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).json({ error: e.message, code: e.code });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
|
||||||
3
build.js
3
build.js
|
|
@ -8,9 +8,6 @@ Promise.all([
|
||||||
// Build ignis-ui.js (delegated to packages/ui)
|
// Build ignis-ui.js (delegated to packages/ui)
|
||||||
require("./packages/ui/build.js"),
|
require("./packages/ui/build.js"),
|
||||||
|
|
||||||
// Build ignis-bridge plugin (delegated to packages/bridge)
|
|
||||||
require("./packages/bridge/build.js"),
|
|
||||||
|
|
||||||
// Build headless-sync bundled plugin
|
// Build headless-sync bundled plugin
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
entryPoints: [
|
entryPoints: [
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
const esbuild = require("esbuild");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
module.exports = esbuild.build({
|
|
||||||
entryPoints: [path.join(__dirname, "src", "main.js")],
|
|
||||||
bundle: true,
|
|
||||||
outfile: path.join(__dirname, "main.js"),
|
|
||||||
format: "cjs",
|
|
||||||
platform: "browser",
|
|
||||||
target: ["chrome90"],
|
|
||||||
external: ["obsidian", "fs"],
|
|
||||||
logLevel: "info",
|
|
||||||
});
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"id": "ignis-bridge",
|
|
||||||
"name": "Ignis Bridge",
|
|
||||||
"version": "0.8.1",
|
|
||||||
"minAppVersion": "1.12.4",
|
|
||||||
"description": "Additional Ignis specific functionality and ignis plugin management.",
|
|
||||||
"author": "Nystik",
|
|
||||||
"authorUrl": "https://github.com/Nystik-gh/ignis",
|
|
||||||
"isDesktopOnly": false
|
|
||||||
}
|
|
||||||
|
|
@ -2,10 +2,5 @@
|
||||||
"name": "@ignis/bridge",
|
"name": "@ignis/bridge",
|
||||||
"version": "0.0.0-internal",
|
"version": "0.0.0-internal",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"main": "src/main.js"
|
||||||
"build": "node build.js"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"esbuild": "^0.20.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ const { initStatusBar } = require("./status-bar");
|
||||||
const { WorkspacePickerModal } = require("./workspace-picker");
|
const { WorkspacePickerModal } = require("./workspace-picker");
|
||||||
const { startDemoGuards, stopDemoGuards } = require("./demo-guards");
|
const { startDemoGuards, stopDemoGuards } = require("./demo-guards");
|
||||||
|
|
||||||
window.__obsidianAPI = require("obsidian");
|
|
||||||
|
|
||||||
class IgnisBridgePlugin extends Plugin {
|
class IgnisBridgePlugin extends Plugin {
|
||||||
async onload() {
|
async onload() {
|
||||||
if (!window.__ignis) {
|
if (!window.__ignis) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ module.exports = esbuild.build({
|
||||||
alias: {
|
alias: {
|
||||||
path: "path-browserify",
|
path: "path-browserify",
|
||||||
},
|
},
|
||||||
|
loader: {
|
||||||
|
".css": "text",
|
||||||
|
},
|
||||||
|
external: ["obsidian", "fs"],
|
||||||
define: {
|
define: {
|
||||||
__IGNIS_VERSION__: JSON.stringify(ignisVersion),
|
__IGNIS_VERSION__: JSON.stringify(ignisVersion),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Injects a link to the CSS overrides stylesheet served from /assets/overrides.css.
|
import bridgeCss from "@ignis/bridge/styles.css";
|
||||||
|
|
||||||
export function installCssOverrides() {
|
export function installCssOverrides() {
|
||||||
const link = document.createElement("link");
|
const link = document.createElement("link");
|
||||||
|
|
@ -6,4 +6,9 @@ export function installCssOverrides() {
|
||||||
link.href = "/assets/overrides.css";
|
link.href = "/assets/overrides.css";
|
||||||
link.setAttribute("data-ignis", "css-overrides");
|
link.setAttribute("data-ignis", "css-overrides");
|
||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
|
|
||||||
|
const bridgeStyle = document.createElement("style");
|
||||||
|
bridgeStyle.textContent = bridgeCss;
|
||||||
|
bridgeStyle.setAttribute("data-ignis", "bridge-css");
|
||||||
|
document.head.appendChild(bridgeStyle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { markLocalOp } from "./echo-guard.js";
|
import { markLocalOp } from "./echo-guard.js";
|
||||||
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
||||||
import { applyReadTransform, applyWriteTransform, resolvePath } from "./transforms.js";
|
import { applyReadTransform, applyWriteTransform, resolvePath } from "./transforms.js";
|
||||||
|
import { hasVirtualFile, getVirtualFile } from "./virtual-files.js";
|
||||||
|
|
||||||
export function createFsPromises(metadataCache, contentCache, transport) {
|
export function createFsPromises(metadataCache, contentCache, transport) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -49,6 +50,21 @@ export function createFsPromises(metadataCache, contentCache, transport) {
|
||||||
const wantText = encoding === "utf8" || encoding === "utf-8";
|
const wantText = encoding === "utf8" || encoding === "utf-8";
|
||||||
const resolved = resolvePath(path);
|
const resolved = resolvePath(path);
|
||||||
|
|
||||||
|
// Virtual plugin source overrides any cache/transport version.
|
||||||
|
if (hasVirtualFile(resolved)) {
|
||||||
|
const content = getVirtualFile(resolved);
|
||||||
|
|
||||||
|
if (wantText) {
|
||||||
|
return typeof content === "string"
|
||||||
|
? content
|
||||||
|
: new TextDecoder().decode(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeof content === "string"
|
||||||
|
? new TextEncoder().encode(content)
|
||||||
|
: content;
|
||||||
|
}
|
||||||
|
|
||||||
let result = null;
|
let result = null;
|
||||||
|
|
||||||
// Check input cache for files picked via browser file dialogs.
|
// Check input cache for files picked via browser file dialogs.
|
||||||
|
|
|
||||||
23
packages/shim/src/fs/virtual-files.js
Normal file
23
packages/shim/src/fs/virtual-files.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Virtual plugin source served from memory; the fs shim's read path checks here before disk.
|
||||||
|
|
||||||
|
function normalize(p) {
|
||||||
|
return (p || "").replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const virtualFiles = new Map();
|
||||||
|
|
||||||
|
export function setVirtualFile(path, content) {
|
||||||
|
virtualFiles.set(normalize(path), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeVirtualFile(path) {
|
||||||
|
virtualFiles.delete(normalize(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVirtualFile(path) {
|
||||||
|
return virtualFiles.get(normalize(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasVirtualFile(path) {
|
||||||
|
return virtualFiles.has(normalize(path));
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,24 @@ import { installCssOverrides } from "./css-overrides.js";
|
||||||
import { initialize } from "./init.js";
|
import { initialize } from "./init.js";
|
||||||
import { fsShim } from "./fs/index.js";
|
import { fsShim } from "./fs/index.js";
|
||||||
import { registerUI } from "./ui-registry.js";
|
import { registerUI } from "./ui-registry.js";
|
||||||
|
import { extractObsidianModule } from "./virtual-plugin-loader.js";
|
||||||
|
|
||||||
// __IGNIS_VERSION__ is replaced at build time from package.json.
|
// __IGNIS_VERSION__ is replaced at build time from package.json.
|
||||||
window.__ignis = { version: __IGNIS_VERSION__ };
|
window.__ignis = { version: __IGNIS_VERSION__ };
|
||||||
window.__ignis_registerUI = registerUI;
|
window.__ignis_registerUI = registerUI;
|
||||||
|
|
||||||
|
const BRIDGE_MANIFEST = {
|
||||||
|
id: "ignis-bridge",
|
||||||
|
name: "Ignis Bridge",
|
||||||
|
version: __IGNIS_VERSION__,
|
||||||
|
minAppVersion: "1.12.4",
|
||||||
|
description:
|
||||||
|
"Additional Ignis specific functionality and ignis plugin management.",
|
||||||
|
author: "Nystik",
|
||||||
|
authorUrl: "https://github.com/Nystik-gh/ignis",
|
||||||
|
isDesktopOnly: false,
|
||||||
|
};
|
||||||
|
|
||||||
installGlobals(); // process, Buffer, window overrides (before require so Buffer is available)
|
installGlobals(); // process, Buffer, window overrides (before require so Buffer is available)
|
||||||
installRequire(); // shim registry, window.require
|
installRequire(); // shim registry, window.require
|
||||||
installCssOverrides(); // browser-specific CSS fixes
|
installCssOverrides(); // browser-specific CSS fixes
|
||||||
|
|
@ -27,4 +40,15 @@ if (window.__currentVaultId) {
|
||||||
fsShim._watcherClient.connect(window.__currentVaultId);
|
fsShim._watcherClient.connect(window.__currentVaultId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extractObsidianModule()
|
||||||
|
.then(async () => {
|
||||||
|
// Dynamic import so bridge's top-level require("obsidian") fires after installRequire + extractObsidianModule.
|
||||||
|
const mod = await import("@ignis/bridge");
|
||||||
|
const IgnisBridgePlugin = mod.default || mod;
|
||||||
|
const bridge = new IgnisBridgePlugin(window.app, BRIDGE_MANIFEST);
|
||||||
|
await bridge.onload();
|
||||||
|
console.log("[ignis] bridge loaded");
|
||||||
|
})
|
||||||
|
.catch((e) => console.error("[ignis] bridge load failed:", e));
|
||||||
|
|
||||||
console.log("[ignis] Shim loader initialized");
|
console.log("[ignis] Shim loader initialized");
|
||||||
|
|
|
||||||
|
|
@ -67,3 +67,8 @@ export function installRequire() {
|
||||||
|
|
||||||
installDebugHelpers(rawRegistry);
|
installDebugHelpers(rawRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For modules captured at runtime, e.g. the obsidian module via the virtual-plugin loader.
|
||||||
|
export function registerShim(name, mod) {
|
||||||
|
shimRegistry[name] = mod;
|
||||||
|
}
|
||||||
|
|
|
||||||
105
packages/shim/src/virtual-plugin-loader.js
Normal file
105
packages/shim/src/virtual-plugin-loader.js
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Capture the obsidian module via a one-shot synthetic plugin so virtual plugins (bridge, future bundled) can require("obsidian").
|
||||||
|
|
||||||
|
import { setVirtualFile, removeVirtualFile } from "./fs/virtual-files.js";
|
||||||
|
import { registerShim } from "./require.js";
|
||||||
|
|
||||||
|
const EXTRACTOR_ID = "ignis-obsidian-extractor";
|
||||||
|
const EXTRACTOR_DIR = ".ignis/virtual/" + EXTRACTOR_ID;
|
||||||
|
const EXTRACTOR_PATH = EXTRACTOR_DIR + "/main.js";
|
||||||
|
|
||||||
|
const EXTRACTOR_SRC = `
|
||||||
|
const obsidian = require("obsidian");
|
||||||
|
window.__ignisCapturedObsidian = obsidian;
|
||||||
|
module.exports = class extends obsidian.Plugin {
|
||||||
|
onload() {}
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const EXTRACTOR_MANIFEST = {
|
||||||
|
id: EXTRACTOR_ID,
|
||||||
|
name: "Ignis Obsidian Module Extractor",
|
||||||
|
version: "0.0.0",
|
||||||
|
minAppVersion: "1.0.0",
|
||||||
|
description: "Internal: captures the obsidian module for virtual plugins.",
|
||||||
|
author: "ignis",
|
||||||
|
authorUrl: "",
|
||||||
|
isDesktopOnly: false,
|
||||||
|
dir: EXTRACTOR_DIR,
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForApp() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (window.app && window.app.plugins && window.app.workspace) {
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (window.app && window.app.plugins && window.app.workspace) {
|
||||||
|
clearInterval(interval);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function extractObsidianModule() {
|
||||||
|
if (window.__obsidian) {
|
||||||
|
return window.__obsidian;
|
||||||
|
}
|
||||||
|
|
||||||
|
await waitForApp();
|
||||||
|
|
||||||
|
const plugins = window.app.plugins;
|
||||||
|
|
||||||
|
// loadPlugin gates on isEnabled(). Force-enable, restore on cleanup.
|
||||||
|
const wasEnabled = plugins.isEnabled();
|
||||||
|
let toggledOn = false;
|
||||||
|
|
||||||
|
if (!wasEnabled) {
|
||||||
|
try {
|
||||||
|
await plugins.setEnable(true);
|
||||||
|
toggledOn = true;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(
|
||||||
|
"[ignis] could not enable community plugins for extractor:",
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setVirtualFile(EXTRACTOR_PATH, EXTRACTOR_SRC);
|
||||||
|
plugins.manifests[EXTRACTOR_ID] = EXTRACTOR_MANIFEST;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await plugins.loadPlugin(EXTRACTOR_ID);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[ignis] extractor load failed:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const captured = window.__ignisCapturedObsidian;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await plugins.unloadPlugin(EXTRACTOR_ID);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
delete plugins.manifests[EXTRACTOR_ID];
|
||||||
|
removeVirtualFile(EXTRACTOR_PATH);
|
||||||
|
delete window.__ignisCapturedObsidian;
|
||||||
|
|
||||||
|
if (toggledOn) {
|
||||||
|
try {
|
||||||
|
await plugins.setEnable(false);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!captured) {
|
||||||
|
console.error("[ignis] obsidian module extraction failed");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.__obsidian = captured;
|
||||||
|
registerShim("obsidian", captured);
|
||||||
|
|
||||||
|
console.log("[ignis] obsidian module captured");
|
||||||
|
return captured;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue