From 645720a725bc5cece492247b11b5ee8ba0a1e1ea Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 28 Dec 2025 16:50:16 +0200 Subject: [PATCH] feat(icon-pack-builder): build zip without content yet --- apps/icon-pack-builder/.gitignore | 1 + apps/icon-pack-builder/package.json | 4 +- apps/icon-pack-builder/src/index.ts | 65 ++++++++++++++++++- apps/icon-pack-builder/src/provider.ts | 7 ++ apps/icon-pack-builder/src/providers/mdi.ts | 12 +++- .../services/export/zip/abstract_provider.ts | 29 +++++---- apps/server/src/services/export/zip/html.ts | 17 +++-- 7 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 apps/icon-pack-builder/.gitignore create mode 100644 apps/icon-pack-builder/src/provider.ts diff --git a/apps/icon-pack-builder/.gitignore b/apps/icon-pack-builder/.gitignore new file mode 100644 index 000000000..6f66c74b0 --- /dev/null +++ b/apps/icon-pack-builder/.gitignore @@ -0,0 +1 @@ +*.zip \ No newline at end of file diff --git a/apps/icon-pack-builder/package.json b/apps/icon-pack-builder/package.json index da23553f9..c529fb2ef 100644 --- a/apps/icon-pack-builder/package.json +++ b/apps/icon-pack-builder/package.json @@ -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": [] } diff --git a/apps/icon-pack-builder/src/index.ts b/apps/icon-pack-builder/src/index.ts index 1e947acf4..58907e2f6 100644 --- a/apps/icon-pack-builder/src/index.ts +++ b/apps/icon-pack-builder/src/index.ts @@ -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((resolve) => { + fileOutputStream.on("finish", resolve); + }); + + console.log(`Built icon pack: ${iconPack.name} (${zipFilePath})`); + }); + } + + await Promise.all(builtIconPacks.map(buildIconPack)); +} + +cls.init(() => { + main(); +}); diff --git a/apps/icon-pack-builder/src/provider.ts b/apps/icon-pack-builder/src/provider.ts new file mode 100644 index 000000000..0a1de81af --- /dev/null +++ b/apps/icon-pack-builder/src/provider.ts @@ -0,0 +1,7 @@ +import { IconPackManifest } from "@triliumnext/server/src/services/icon_packs"; + +export interface IconPackData { + name: string; + prefix: string; + // manifest: IconPackManifest; +} diff --git a/apps/icon-pack-builder/src/providers/mdi.ts b/apps/icon-pack-builder/src/providers/mdi.ts index 3825c1789..71a8d68b2 100644 --- a/apps/icon-pack-builder/src/providers/mdi.ts +++ b/apps/icon-pack-builder/src/providers/mdi.ts @@ -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"), + // }, + }; } diff --git a/apps/server/src/services/export/zip/abstract_provider.ts b/apps/server/src/services/export/zip/abstract_provider.ts index 9d135de20..5eda4b076 100644 --- a/apps/server/src/services/export/zip/abstract_provider.ts +++ b/apps/server/src/services/export/zip/abstract_provider.ts @@ -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"; + + } } diff --git a/apps/server/src/services/export/zip/html.ts b/apps/server/src/services/export/zip/html.ts index 0cab8193c..14fb44acc 100644 --- a/apps/server/src/services/export/zip/html.ts +++ b/apps/server/src/services/export/zip/html.ts @@ -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."); }