feat(icon-pack-builder): build zip without content yet

This commit is contained in:
Elian Doran 2025-12-28 16:50:16 +02:00
parent a6c74449aa
commit 645720a725
No known key found for this signature in database
7 changed files with 110 additions and 25 deletions

1
apps/icon-pack-builder/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.zip

View File

@ -2,9 +2,9 @@
"name": "@triliumnext/icon-pack-builder",
"version": "1.0.0",
"description": "",
"main": "index.js",
"main": "src/index.ts",
"scripts": {
"start": "tsx ./src/index.ts"
"start": "tsx ."
},
"keywords": []
}

View File

@ -1,3 +1,66 @@
import { createWriteStream } from "node:fs";
import type { IconPackData } from "./provider";
import mdi from "./providers/mdi";
import cls from "@triliumnext/server/src/services/cls.js";
mdi();
process.env.TRILIUM_INTEGRATION_TEST = "memory-no-store";
process.env.TRILIUM_RESOURCE_DIR = "../server/src";
process.env.NODE_ENV = "development";
async function main() {
const i18n = await import("@triliumnext/server/src/services/i18n.js");
await i18n.initializeTranslations();
const sqlInit = (await import("../../server/src/services/sql_init.js")).default;
await sqlInit.createInitialDatabase(true);
// Wait for becca to be loaded before importing data
const beccaLoader = await import("../../server/src/becca/becca_loader.js");
await beccaLoader.beccaLoaded;
const becca = (await import("../../server/src/becca/becca.js")).default;
const rootNote = becca.getNote("root");
const notesService = (await import("../../server/src/services/notes.js")).default;
const builtIconPacks = [
mdi()
];
function buildIconPack(iconPack: IconPackData) {
return new Promise(async (res, rej) => {
// Create the icon pack note.
const { note, branch } = notesService.createNewNote({
parentNoteId: "root",
type: "file",
title: iconPack.name,
mime: "application/json",
content: "Hello"
});
note.setLabel("iconPack", iconPack.prefix);
// Export to zip.
const zipFilePath = `icon-pack-${iconPack.prefix}.zip`;
const fileOutputStream = createWriteStream(zipFilePath);
const { exportToZip } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
const taskContext = new (await import("@triliumnext/server/src/services/task_context.js")).default(
"no-progress-reporting",
"export",
null
);
await exportToZip(taskContext, branch, "html", fileOutputStream, false, {
skipExtraFiles: true
});
await new Promise<void>((resolve) => {
fileOutputStream.on("finish", resolve);
});
console.log(`Built icon pack: ${iconPack.name} (${zipFilePath})`);
});
}
await Promise.all(builtIconPacks.map(buildIconPack));
}
cls.init(() => {
main();
});

View File

@ -0,0 +1,7 @@
import { IconPackManifest } from "@triliumnext/server/src/services/icon_packs";
export interface IconPackData {
name: string;
prefix: string;
// manifest: IconPackManifest;
}

View File

@ -1,13 +1,19 @@
import { readFileSync } from "fs";
import { join } from "path";
import { extractClassNamesFromCss } from "../utils";
import type { IconPackData } from "../provider";
export default function buildIcons() {
export default function buildIcons(): IconPackData {
const baseDir = join(__dirname, "../../../../node_modules/@mdi/font");
const cssFilePath = join(baseDir, "css", "materialdesignicons.min.css");
const cssFileContent = readFileSync(cssFilePath, "utf-8");
console.log(extractClassNamesFromCss(cssFileContent, "mdi"));
return {
name: "Material Design Icons",
prefix: "mdi",
// manifest: {
// icons: extractClassNamesFromCss(cssFileContent, "mdi"),
// },
};
}

View File

@ -1,9 +1,10 @@
import { Archiver } from "archiver";
import type { default as NoteMeta, NoteMetaFile } from "../../meta/note_meta.js";
import type BNote from "../../../becca/entities/bnote.js";
import type BBranch from "../../../becca/entities/bbranch.js";
import mimeTypes from "mime-types";
import { NoteType } from "@triliumnext/commons";
import { Archiver } from "archiver";
import mimeTypes from "mime-types";
import type BBranch from "../../../becca/entities/bbranch.js";
import type BNote from "../../../becca/entities/bnote.js";
import type { default as NoteMeta, NoteMetaFile } from "../../meta/note_meta.js";
type RewriteLinksFn = (content: string, noteMeta: NoteMeta) => string;
@ -15,6 +16,8 @@ export interface AdvancedExportOptions {
*/
skipHtmlTemplate?: boolean;
skipExtraFiles?: boolean;
/**
* Provides a custom function to rewrite the links found in HTML or Markdown notes. This method is called for every note imported, if it's of the right type.
*
@ -75,15 +78,15 @@ export abstract class ZipExportProvider {
} else if (existingExtension.length > 0) {
// if the page already has an extension, then we'll just keep it
return null;
} else {
if (mime?.toLowerCase()?.trim() === "image/jpg") {
return "jpg";
} else if (mime?.toLowerCase()?.trim() === "text/mermaid") {
return "txt";
} else {
return mimeTypes.extension(mime) || "dat";
}
}
if (mime?.toLowerCase()?.trim() === "image/jpg") {
return "jpg";
} else if (mime?.toLowerCase()?.trim() === "text/mermaid") {
return "txt";
}
return mimeTypes.extension(mime) || "dat";
}
}

View File

@ -1,9 +1,10 @@
import fs from "fs";
import html from "html";
import path from "path";
import type NoteMeta from "../../meta/note_meta.js";
import { escapeHtml, getResourceDir, isDev } from "../../utils";
import html from "html";
import { ZipExportProvider } from "./abstract_provider.js";
import path from "path";
import fs from "fs";
export default class HtmlExportProvider extends ZipExportProvider {
@ -12,6 +13,8 @@ export default class HtmlExportProvider extends ZipExportProvider {
private cssMeta: NoteMeta | null = null;
prepareMeta(metaFile) {
if (this.zipExportOptions?.skipExtraFiles) return;
this.navigationMeta = {
noImport: true,
dataFileName: "navigation.html"
@ -61,16 +64,18 @@ export default class HtmlExportProvider extends ZipExportProvider {
}
if (content.length < 100_000) {
content = html.prettyPrint(content, { indent_size: 2 })
content = html.prettyPrint(content, { indent_size: 2 });
}
content = this.rewriteFn(content as string, noteMeta);
return content;
} else {
return content;
}
return content;
}
afterDone(rootMeta: NoteMeta) {
if (this.zipExportOptions?.skipExtraFiles) return;
if (!this.navigationMeta || !this.indexMeta || !this.cssMeta) {
throw new Error("Missing meta.");
}