diff --git a/apps/client-standalone/src/lightweight/zip_export_provider_factory.ts b/apps/client-standalone/src/lightweight/zip_export_provider_factory.ts new file mode 100644 index 0000000000..2d967c7ee5 --- /dev/null +++ b/apps/client-standalone/src/lightweight/zip_export_provider_factory.ts @@ -0,0 +1,16 @@ +import { type ExportFormat, type ZipExportProviderData, ZipExportProvider } from "@triliumnext/core"; + +export async function standaloneZipExportProviderFactory(format: ExportFormat, data: ZipExportProviderData): Promise { + switch (format) { + case "html": { + const { default: HtmlExportProvider } = await import("@triliumnext/core/src/services/export/zip/html.js"); + return new HtmlExportProvider(data); + } + case "markdown": { + const { default: MarkdownExportProvider } = await import("@triliumnext/core/src/services/export/zip/markdown.js"); + return new MarkdownExportProvider(data); + } + default: + throw new Error(`Unsupported export format: '${format}'`); + } +} diff --git a/apps/client-standalone/src/local-server-worker.ts b/apps/client-standalone/src/local-server-worker.ts index 6b54a947a5..7accb40430 100644 --- a/apps/client-standalone/src/local-server-worker.ts +++ b/apps/client-standalone/src/local-server-worker.ts @@ -157,6 +157,7 @@ async function initialize(): Promise { executionContext: new BrowserExecutionContext(), crypto: new BrowserCryptoProvider(), zip: new BrowserZipProvider(), + zipExportProviderFactory: (await import("./lightweight/zip_export_provider_factory.js")).standaloneZipExportProviderFactory, messaging: messagingProvider!, request: new FetchRequestProvider(), platform: new StandalonePlatformProvider(queryString), diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 8aef99bae6..61698721f9 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -135,6 +135,7 @@ async function main() { }, crypto: new NodejsCryptoProvider(), zip: new NodejsZipProvider(), + zipExportProviderFactory: (await import("@triliumnext/server/src/services/export/zip/factory.js")).serverZipExportProviderFactory, request: new NodeRequestProvider(), executionContext: new ClsHookedExecutionContext(), messaging: new WebSocketMessagingProvider(), diff --git a/apps/server/spec/setup.ts b/apps/server/spec/setup.ts index cd0ef855cc..484d582465 100644 --- a/apps/server/spec/setup.ts +++ b/apps/server/spec/setup.ts @@ -2,6 +2,7 @@ import { beforeAll } from "vitest"; import { readFileSync } from "fs"; import { join } from "path"; import { initializeCore } from "@triliumnext/core"; +import { serverZipExportProviderFactory } from "../src/services/export/zip/factory.js"; import ClsHookedExecutionContext from "../src/cls_provider.js"; import NodejsCryptoProvider from "../src/crypto_provider.js"; import NodejsZipProvider from "../src/zip_provider.js"; @@ -29,6 +30,7 @@ beforeAll(async () => { }, crypto: new NodejsCryptoProvider(), zip: new NodejsZipProvider(), + zipExportProviderFactory: serverZipExportProviderFactory, executionContext: new ClsHookedExecutionContext(), schema: readFileSync(require.resolve("@triliumnext/core/src/assets/schema.sql"), "utf-8"), platform: new ServerPlatformProvider(), diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 5a5c58c150..66572d1c8a 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -3,7 +3,7 @@ * are loaded later and will result in an empty string. */ -import { getLog,initializeCore, sql_init } from "@triliumnext/core"; +import { getLog, initializeCore, sql_init } from "@triliumnext/core"; import fs from "fs"; import { t } from "i18next"; import path from "path"; @@ -53,6 +53,7 @@ async function startApplication() { }, crypto: new NodejsCryptoProvider(), zip: new NodejsZipProvider(), + zipExportProviderFactory: (await import("./services/export/zip/factory.js")).serverZipExportProviderFactory, request: new NodeRequestProvider(), executionContext: new ClsHookedExecutionContext(), messaging: new WebSocketMessagingProvider(), diff --git a/apps/server/src/services/export/zip/factory.ts b/apps/server/src/services/export/zip/factory.ts new file mode 100644 index 0000000000..d3d9a29508 --- /dev/null +++ b/apps/server/src/services/export/zip/factory.ts @@ -0,0 +1,20 @@ +import { type ExportFormat, type ZipExportProviderData, ZipExportProvider } from "@triliumnext/core"; + +export async function serverZipExportProviderFactory(format: ExportFormat, data: ZipExportProviderData): Promise { + switch (format) { + case "html": { + const { default: HtmlExportProvider } = await import("@triliumnext/core/src/services/export/zip/html.js"); + return new HtmlExportProvider(data); + } + case "markdown": { + const { default: MarkdownExportProvider } = await import("@triliumnext/core/src/services/export/zip/markdown.js"); + return new MarkdownExportProvider(data); + } + case "share": { + const { default: ShareThemeExportProvider } = await import("./share_theme.js"); + return new ShareThemeExportProvider(data); + } + default: + throw new Error(`Unsupported export format: '${format}'`); + } +} diff --git a/apps/server/src/share/content_renderer.ts b/apps/server/src/share/content_renderer.ts index 6f3a2c6083..c4d81dde8a 100644 --- a/apps/server/src/share/content_renderer.ts +++ b/apps/server/src/share/content_renderer.ts @@ -1,6 +1,5 @@ import { renderSpreadsheetToHtml } from "@triliumnext/commons"; -import { sanitize } from "@triliumnext/core"; -import { icon_packs as iconPackService } from "@triliumnext/core"; +import { icon_packs as iconPackService, sanitize, utils } from "@triliumnext/core"; import { highlightAuto } from "@triliumnext/highlightjs"; import ejs from "ejs"; import escapeHtml from "escape-html"; @@ -16,7 +15,7 @@ import BNote from "../becca/entities/bnote.js"; import assetPath, { assetUrlFragment } from "../services/asset_path.js"; import log from "../services/log.js"; import options from "../services/options.js"; -import utils, { getResourceDir, isDev, safeExtractMessageAndStackFromError } from "../services/utils.js"; +import { getResourceDir, isDev } from "../services/utils.js"; import SAttachment from "./shaca/entities/sattachment.js"; import SBranch from "./shaca/entities/sbranch.js"; import type SNote from "./shaca/entities/snote.js"; @@ -224,7 +223,7 @@ function renderNoteContentInternal(note: SNote | BNote, renderArgs: RenderArgs) return ejs.render(content, opts, { includer }); } } catch (e: unknown) { - const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); + const [errMessage, errStack] = utils.safeExtractMessageAndStackFromError(e); log.error(`Rendering user provided share template (${templateId}) threw exception ${errMessage} with stacktrace: ${errStack}`); } } diff --git a/packages/trilium-core/src/index.ts b/packages/trilium-core/src/index.ts index efe94b2bff..3c1c2071fe 100644 --- a/packages/trilium-core/src/index.ts +++ b/packages/trilium-core/src/index.ts @@ -10,6 +10,7 @@ import { initSchema, initDemoArchive } from "./services/sql_init"; import appInfo from "./services/app_info"; import { type PlatformProvider, initPlatform } from "./services/platform"; import { type ZipProvider, initZipProvider } from "./services/zip_provider"; +import { type ZipExportProviderFactory, initZipExportProviderFactory } from "./services/export/zip_export_provider_factory"; import markdown from "./services/import/markdown"; export { getLog } from "./services/log"; @@ -104,8 +105,9 @@ export * as routeHelpers from "./routes/helpers"; export { getZipProvider, type ZipArchive, type ZipProvider } from "./services/zip_provider"; export { default as zipImportService } from "./services/import/zip"; export { default as zipExportService } from "./services/export/zip"; -export { type AdvancedExportOptions } from "./services/export/zip/abstract_provider"; +export { type AdvancedExportOptions, type ZipExportProviderData } from "./services/export/zip/abstract_provider"; export { ZipExportProvider } from "./services/export/zip/abstract_provider"; +export { type ZipExportProviderFactory } from "./services/export/zip_export_provider_factory"; export { type ExportFormat } from "./meta"; export * as becca_easy_mocking from "./test/becca_easy_mocking"; @@ -113,7 +115,7 @@ export * as becca_mocking from "./test/becca_mocking"; export { default as markdownImportService } from "./services/import/markdown"; -export async function initializeCore({ dbConfig, executionContext, crypto, zip, translations, messaging, request, schema, extraAppInfo, platform, getDemoArchive }: { +export async function initializeCore({ dbConfig, executionContext, crypto, zip, zipExportProviderFactory, translations, messaging, request, schema, extraAppInfo, platform, getDemoArchive }: { dbConfig: SqlServiceParams, executionContext: ExecutionContext, crypto: CryptoProvider, @@ -121,6 +123,7 @@ export async function initializeCore({ dbConfig, executionContext, crypto, zip, translations: TranslationProvider, platform: PlatformProvider, schema: string, + zipExportProviderFactory: ZipExportProviderFactory, messaging?: MessagingProvider, request?: RequestProvider, getDemoArchive?: () => Promise, @@ -134,6 +137,7 @@ export async function initializeCore({ dbConfig, executionContext, crypto, zip, await initTranslations(translations); initCrypto(crypto); initZipProvider(zip); + initZipExportProviderFactory(zipExportProviderFactory); initContext(executionContext); initSql(new SqlService(dbConfig, getLog())); initSchema(schema); diff --git a/packages/trilium-core/src/services/export/zip.ts b/packages/trilium-core/src/services/export/zip.ts index 75f87c15c7..8aa4914750 100644 --- a/packages/trilium-core/src/services/export/zip.ts +++ b/packages/trilium-core/src/services/export/zip.ts @@ -11,27 +11,22 @@ import protectedSessionService from "../protected_session.js"; import TaskContext from "../task_context.js"; import { getZipProvider } from "../zip_provider.js"; import { getContentDisposition } from "../utils/index" -import { AdvancedExportOptions, ZipExportProvider, ZipExportProviderData } from "./zip/abstract_provider.js"; -import HtmlExportProvider from "./zip/html.js"; -import MarkdownExportProvider from "./zip/markdown.js"; +import { AdvancedExportOptions, ZipExportProviderData } from "./zip/abstract_provider.js"; +import { getZipExportProviderFactory } from "./zip_export_provider_factory.js"; import { AttachmentMeta, AttributeMeta, ExportFormat, NoteMeta, NoteMetaFile } from "../../meta"; import { ValidationError } from "../../errors"; import { extname } from "../utils/path"; // eslint-disable-next-line @typescript-eslint/no-explicit-any async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, format: ExportFormat, res: Record, setHeaders = true, zipExportOptions?: AdvancedExportOptions) { - if (!["html", "markdown", "share"].includes(format)) { - throw new ValidationError(`Only 'html', 'markdown' and 'share' allowed as export format, '${format}' given`); - } - const archive = getZipProvider().createZipArchive(); const rewriteFn = (zipExportOptions?.customRewriteLinks ? zipExportOptions?.customRewriteLinks(rewriteLinks, getNoteTargetUrl) : rewriteLinks); - const provider = buildProvider(); + const provider = await buildProvider(); const log = getLog(); const noteIdToMeta: Record = {}; - function buildProvider(): ZipExportProvider { + async function buildProvider() { const providerData: ZipExportProviderData = { getNoteTargetUrl, archive, @@ -40,17 +35,7 @@ async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, zipExportOptions }; - switch (format) { - case "html": - return new HtmlExportProvider(providerData); - case "markdown": - return new MarkdownExportProvider(providerData); - case "share": - // TODO: Reintroduce share format. - // return new ShareThemeExportProvider(providerData); - default: - throw new Error(); - } + return getZipExportProviderFactory()(format, providerData); } function getUniqueFilename(existingFileNames: Record, fileName: string) { diff --git a/packages/trilium-core/src/services/export/zip_export_provider_factory.ts b/packages/trilium-core/src/services/export/zip_export_provider_factory.ts new file mode 100644 index 0000000000..6f97f329da --- /dev/null +++ b/packages/trilium-core/src/services/export/zip_export_provider_factory.ts @@ -0,0 +1,15 @@ +import type { ExportFormat } from "../../meta.js"; +import type { ZipExportProvider, ZipExportProviderData } from "./zip/abstract_provider.js"; + +export type ZipExportProviderFactory = (format: ExportFormat, data: ZipExportProviderData) => Promise; + +let factory: ZipExportProviderFactory | null = null; + +export function initZipExportProviderFactory(f: ZipExportProviderFactory) { + factory = f; +} + +export function getZipExportProviderFactory(): ZipExportProviderFactory { + if (!factory) throw new Error("ZipExportProviderFactory not initialized."); + return factory; +}