116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
import { editor, space, system } from "@silverbulletmd/silverbullet/syscalls";
|
|
import { listFilesCached, readFile } from "./federation.ts";
|
|
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
|
|
import { federatedPathToLocalPath, wildcardPathToRegex } from "./util.ts";
|
|
import type { LibraryDef } from "@silverbulletmd/silverbullet/type/config";
|
|
|
|
export async function updateLibrariesCommand() {
|
|
if (
|
|
await editor.confirm(
|
|
"Are you sure you want to update all libraries?",
|
|
)
|
|
) {
|
|
await editor.flashNotification("Updating all libraries...");
|
|
const updateStats: UpdateStats = await system.invokeFunction(
|
|
"federation.updateLibraries",
|
|
);
|
|
await editor.reloadConfigAndCommands();
|
|
await editor.flashNotification(
|
|
`Updated ${updateStats.libraries} libraries containing a total of ${updateStats.items} items.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
type UpdateStats = {
|
|
libraries: number;
|
|
items: number;
|
|
};
|
|
|
|
// Run on the server for efficiency and CORS avoidance
|
|
export async function updateLibraries(): Promise<UpdateStats> {
|
|
const updateStats: UpdateStats = { libraries: 0, items: 0 };
|
|
const libraries =
|
|
((await system.reloadConfig())?.libraries || []) as LibraryDef[];
|
|
console.log("Libraries", await system.getSpaceConfig());
|
|
for (const lib of libraries) {
|
|
// Handle deprecated 'source' field
|
|
if (lib.source) {
|
|
lib.import = lib.source;
|
|
}
|
|
if (!lib.import) {
|
|
console.warn("Library source not set, skipping", lib);
|
|
continue;
|
|
}
|
|
const pageUri = parsePageRef(lib.import).page;
|
|
|
|
if (!pageUri.startsWith("!")) {
|
|
console.warn(
|
|
"Library source must be a federated page, skipping",
|
|
pageUri,
|
|
);
|
|
continue;
|
|
}
|
|
|
|
console.log("Now updating library", pageUri);
|
|
|
|
const localLibraryPath = federatedPathToLocalPath(pageUri);
|
|
|
|
// Sanity check the `source` pattern to avoid disaster (like wiping out the whole space)
|
|
if (!/^Library\/.+/.test(localLibraryPath)) {
|
|
console.warn(
|
|
"Skipping library",
|
|
pageUri,
|
|
"as it does not start with Library/",
|
|
);
|
|
continue;
|
|
}
|
|
|
|
// Fetch new list of pages
|
|
let newPages = await listFilesCached(pageUri, true);
|
|
console.log("All pages", newPages.length);
|
|
if (lib.exclude) {
|
|
for (const exclude of lib.exclude) {
|
|
const excludeUri = parsePageRef(exclude).page;
|
|
const excludeRegex = wildcardPathToRegex(excludeUri + ".md");
|
|
newPages = newPages.filter((p) => {
|
|
if (excludeRegex.test(p.name)) {
|
|
console.info("Excluding", p.name);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
// Compile existing page list in local space (to be removed)
|
|
const localPages = await space.listPages();
|
|
const localSourceRegex = wildcardPathToRegex(localLibraryPath);
|
|
|
|
// Remove pages that match the source pattern, but in their "local" form
|
|
const pagesToRemove = localPages.filter((p) =>
|
|
localSourceRegex.test(p.name)
|
|
);
|
|
console.log("Pages to remove", pagesToRemove.length);
|
|
for (const page of pagesToRemove) {
|
|
console.info("Deleting", page.name);
|
|
await space.deletePage(page.name);
|
|
}
|
|
|
|
// Import the new pages
|
|
for (const page of newPages) {
|
|
console.info("Importing", page.name);
|
|
// Fetch the file
|
|
const buf = (await readFile(page.name)).data;
|
|
|
|
// Write to local space
|
|
await space.writeFile(federatedPathToLocalPath(page.name), buf);
|
|
|
|
updateStats.items++;
|
|
}
|
|
|
|
updateStats.libraries++;
|
|
console.log("Done with library", pageUri);
|
|
}
|
|
return updateStats;
|
|
}
|