chore(core): fix most errors with export

This commit is contained in:
Elian Doran
2026-03-27 22:20:59 +02:00
parent 21e2cf10c2
commit a59d6dfb11
19 changed files with 70 additions and 91 deletions

View File

@@ -54,8 +54,8 @@ async function registerHandlers() {
}
async function exportData() {
const { exportToZipFile } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
await exportToZipFile("root", "html", DEMO_ZIP_PATH);
const { zipExportService } = (await import("@triliumnext/core"));
await zipExportService.exportToZipFile("root", "html", DEMO_ZIP_PATH);
}
main();

View File

@@ -1,7 +1,6 @@
import debounce from "@triliumnext/client/src/services/debounce.js";
import type { AdvancedExportOptions, ExportFormat } from "@triliumnext/core";
import cls from "@triliumnext/server/src/services/cls.js";
import type { AdvancedExportOptions, ExportFormat } from "@triliumnext/server/src/services/export/zip/abstract_provider.js";
import { initializeTranslations } from "@triliumnext/server/src/services/i18n.js";
import { parseNoteMetaFile } from "@triliumnext/server/src/services/in_app_help.js";
import type { NoteMetaFile } from "@triliumnext/server/src/services/meta/note_meta.js";
import type NoteMeta from "@triliumnext/server/src/services/meta/note_meta.js";
@@ -11,7 +10,7 @@ import yaml from "js-yaml";
import path from "path";
import packageJson from "../package.json" with { type: "json" };
import { extractZip, importData, initializeDatabase, startElectron } from "./utils.js";
import { extractZip, importData, startElectron } from "./utils.js";
interface NoteMapping {
rootNoteId: string;
@@ -153,7 +152,7 @@ async function exportData(noteId: string, format: ExportFormat, outputPath: stri
await fsExtra.mkdir(outputPath);
// First export as zip.
const { exportToZipFile } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
const { zipExportService } = (await import("@triliumnext/core"));
const exportOpts: AdvancedExportOptions = {};
if (format === "html") {
@@ -205,7 +204,7 @@ async function exportData(noteId: string, format: ExportFormat, outputPath: stri
};
}
await exportToZipFile(noteId, format, zipFilePath, exportOpts);
await zipExportService.exportToZipFile(noteId, format, zipFilePath, exportOpts);
await extractZip(zipFilePath, outputPath, ignoredFiles);
} finally {
if (await fsExtra.exists(zipFilePath)) {

View File

@@ -41,7 +41,6 @@
"@triliumnext/core": "workspace:*",
"@triliumnext/express-partial-content": "workspace:*",
"@triliumnext/highlightjs": "workspace:*",
"@triliumnext/turndown-plugin-gfm": "workspace:*",
"@types/archiver": "7.0.0",
"@types/better-sqlite3": "7.6.13",
"@types/cls-hooked": "4.3.9",

View File

@@ -1,10 +1,8 @@
import { NoteParams, SearchParams, zipImportService } from "@triliumnext/core";
import { type ExportFormat, NoteParams, SearchParams, zipExportService, zipImportService } from "@triliumnext/core";
import type { Request, Router } from "express";
import type { ParsedQs } from "qs";
import becca from "../becca/becca.js";
import zipExportService from "../services/export/zip.js";
import type { ExportFormat } from "../services/export/zip/abstract_provider.js";
import noteService from "../services/notes.js";
import SearchContext from "../services/search/search_context.js";
import searchService from "../services/search/services/search.js";

View File

@@ -1,30 +0,0 @@
import { RenderMarkdownResponse, ToMarkdownResponse } from "@triliumnext/commons";
import { markdownImportService } from "@triliumnext/core";
import type { Request } from "express";
import markdown from "../../services/export/markdown.js";
function renderMarkdown(req: Request) {
const { markdownContent } = req.body;
if (!markdownContent || typeof markdownContent !== 'string') {
throw new Error('markdownContent parameter is required and must be a string');
}
return {
htmlContent: markdownImportService.renderToHtml(markdownContent, "")
} satisfies RenderMarkdownResponse;
}
function toMarkdown(req: Request) {
const { htmlContent } = req.body;
if (!htmlContent || typeof htmlContent !== 'string') {
throw new Error('htmlContent parameter is required and must be a string');
}
return {
markdownContent: markdown.toMarkdown(htmlContent)
} satisfies ToMarkdownResponse;
}
export default {
renderMarkdown,
toMarkdown
};

View File

@@ -26,7 +26,6 @@ import filesRoute from "./api/files.js";
import fontsRoute from "./api/fonts.js";
import loginApiRoute from "./api/login.js";
import metricsRoute from "./api/metrics.js";
import otherRoute from "./api/other.js";
import passwordApiRoute from "./api/password.js";
import recoveryCodes from './api/recovery_codes.js';
import scriptRoute from "./api/script.js";
@@ -193,8 +192,6 @@ function register(app: express.Application) {
asyncApiRoute(GET, "/api/backend-log", backendLogRoute.getBackendLog);
route(GET, "/api/fonts", [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
apiRoute(PST, "/api/other/render-markdown", otherRoute.renderMarkdown);
apiRoute(PST, "/api/other/to-markdown", otherRoute.toMarkdown);
shareRoutes.register(router);

View File

@@ -1,5 +1,5 @@
import { type AttributeRow, dayjs, formatLogMessage } from "@triliumnext/commons";
import { type AbstractBeccaEntity, Becca, branches as branchService, NoteParams, SearchContext, sync_mutex as syncMutex } from "@triliumnext/core";
import { type AbstractBeccaEntity, Becca, branches as branchService, NoteParams, SearchContext, sync_mutex as syncMutex,zipExportService } from "@triliumnext/core";
import axios from "axios";
import * as cheerio from "cheerio";
import xml2js from "xml2js";
@@ -19,7 +19,6 @@ import backupService from "./backup.js";
import cloningService from "./cloning.js";
import config from "./config.js";
import dateNoteService from "./date_notes.js";
import exportService from "./export/zip.js";
import log from "./log.js";
import noteService from "./notes.js";
import optionsService from "./options.js";
@@ -662,7 +661,7 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) {
return { note: launcherNote };
};
this.exportSubtreeToZipFile = async (noteId, format, zipFilePath) => await exportService.exportToZipFile(noteId, format, zipFilePath);
this.exportSubtreeToZipFile = async (noteId, format, zipFilePath) => await zipExportService.exportToZipFile(noteId, format, zipFilePath);
this.runOnFrontend = async (_script, params = []) => {
let script: string;

View File

@@ -1,3 +1,4 @@
import { ExportFormat, icon_packs as iconPackService, ZipExportProvider } from "@triliumnext/core";
import ejs from "ejs";
import fs, { readdirSync, readFileSync } from "fs";
import { convert as convertToText } from "html-to-text";
@@ -9,12 +10,10 @@ import type BBranch from "../../../becca/entities/bbranch.js";
import type BNote from "../../../becca/entities/bnote.js";
import { getClientDir, getShareThemeAssetDir } from "../../../routes/assets";
import { getDefaultTemplatePath, readTemplate, renderNoteForExport } from "../../../share/content_renderer";
import { icon_packs as iconPackService } from "@triliumnext/core";
import log from "../../log";
import NoteMeta, { NoteMetaFile } from "../../meta/note_meta";
import { RESOURCE_DIR } from "../../resource_dir";
import { getResourceDir, isDev } from "../../utils";
import { ExportFormat, ZipExportProvider } from "./abstract_provider.js";
const shareThemeAssetDir = getShareThemeAssetDir();

View File

@@ -9,6 +9,7 @@
"dependencies": {
"@braintree/sanitize-url": "7.1.1",
"@triliumnext/commons": "workspace:*",
"@triliumnext/turndown-plugin-gfm": "workspace:*",
"async-mutex": "0.5.0",
"chardet": "2.1.1",
"escape-html": "1.0.3",

View File

@@ -103,6 +103,10 @@ export * as routeHelpers from "./routes/helpers";
export { getZipProvider, type ZipProvider } from "./services/import/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 { ZipExportProvider } from "./services/export/zip/abstract_provider";
export { type ExportFormat } from "./meta";
export * as becca_easy_mocking from "./test/becca_easy_mocking";
export * as becca_mocking from "./test/becca_mocking";

View File

@@ -1,5 +1,31 @@
import becca from "../../becca/becca";
import { RenderMarkdownResponse, ToMarkdownResponse } from "@triliumnext/commons";
import type { Request } from "express";
import markdown from "../../services/export/markdown.js";
import { markdownImportService } from "../..";
function renderMarkdown(req: Request) {
const { markdownContent } = req.body;
if (!markdownContent || typeof markdownContent !== 'string') {
throw new Error('markdownContent parameter is required and must be a string');
}
return {
htmlContent: markdownImportService.renderToHtml(markdownContent, "")
} satisfies RenderMarkdownResponse;
}
function toMarkdown(req: Request) {
const { htmlContent } = req.body;
if (!htmlContent || typeof htmlContent !== 'string') {
throw new Error('htmlContent parameter is required and must be a string');
}
return {
markdownContent: markdown.toMarkdown(htmlContent)
} satisfies ToMarkdownResponse;
}
function getIconUsage() {
const iconClassToCountMap: Record<string, number> = {};
@@ -25,5 +51,7 @@ function getIconUsage() {
}
export default {
getIconUsage
getIconUsage,
renderMarkdown,
toMarkdown
}

View File

@@ -198,7 +198,11 @@ export function buildSharedApiRoutes({ route, asyncRoute, apiRoute, asyncApiRout
apiRoute(PST, "/api/bulk-action/affected-notes", bulkActionRoute.getAffectedNoteCount);
apiRoute(GET, "/api/app-info", appInfoRoute.getAppInfo);
apiRoute(GET, "/api/other/icon-usage", otherRoute.getIconUsage);
apiRoute(PST, "/api/other/render-markdown", otherRoute.renderMarkdown);
apiRoute(PST, "/api/other/to-markdown", otherRoute.toMarkdown);
asyncApiRoute(GET, "/api/similar-notes/:noteId", similarNotesRoute.getSimilarNotes);
apiRoute(PST, "/api/relation-map", relationMapApiRoute.getRelationMap);
apiRoute(GET, "/api/recent-changes/:ancestorNoteId", recentChangesApiRoute.getRecentChanges);

View File

@@ -1,9 +1,9 @@
import { utils } from "@triliumnext/core";
import type { Response } from "express";
import becca from "../../becca/becca.js";
import type BBranch from "../../becca/entities/bbranch.js";
import type TaskContext from "../task_context.js";
import { getContentDisposition, stripTags } from "../utils/index.js";
function exportToOpml(taskContext: TaskContext<"export">, branch: BBranch, version: string, res: Response) {
if (!["1.0", "2.0"].includes(version)) {
@@ -58,7 +58,7 @@ function exportToOpml(taskContext: TaskContext<"export">, branch: BBranch, versi
const filename = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.title}.opml`;
res.setHeader("Content-Disposition", utils.getContentDisposition(filename));
res.setHeader("Content-Disposition", getContentDisposition(filename));
res.setHeader("Content-Type", "text/x-opml");
res.write(`<?xml version="1.0" encoding="UTF-8"?>
@@ -82,7 +82,7 @@ function exportToOpml(taskContext: TaskContext<"export">, branch: BBranch, versi
function prepareText(text: string) {
const newLines = text.replace(/(<p[^>]*>|<br\s*\/?>)/g, "\n").replace(/&nbsp;/g, " "); // nbsp isn't in XML standard (only HTML)
const stripped = utils.stripTags(newLines);
const stripped = stripTags(newLines);
const escaped = escapeXmlAttribute(stripped);

View File

@@ -8,9 +8,9 @@ import becca from "../../becca/becca.js";
import type BBranch from "../../becca/entities/bbranch.js";
import type BNote from "../../becca/entities/bnote.js";
import type TaskContext from "../task_context.js";
import { escapeHtml,getContentDisposition } from "../utils.js";
import { escapeHtml,getContentDisposition } from "../utils/index.js";
import mdService from "./markdown.js";
import type { ExportFormat } from "./zip/abstract_provider.js";
import { ExportFormat } from "../../meta.js";
function exportSingleNote(taskContext: TaskContext<"export">, branch: BBranch, format: ExportFormat, res: Response) {
const note = branch.getNote();

View File

@@ -1,5 +1,4 @@
import { NoteType } from "@triliumnext/commons";
import { ValidationError } from "@triliumnext/core";
import archiver from "archiver";
import type { Response } from "express";
import fs from "fs";
@@ -10,19 +9,16 @@ import packageInfo from "../../../package.json" with { type: "json" };
import becca from "../../becca/becca.js";
import BBranch from "../../becca/entities/bbranch.js";
import type BNote from "../../becca/entities/bnote.js";
import dateUtils from "../date_utils.js";
import log from "../log.js";
import type AttachmentMeta from "../meta/attachment_meta.js";
import type AttributeMeta from "../meta/attribute_meta.js";
import type NoteMeta from "../meta/note_meta.js";
import type { NoteMetaFile } from "../meta/note_meta.js";
import dateUtils from "../utils/date.js";
import log, { getLog } from "../log.js";
import protectedSessionService from "../protected_session.js";
import TaskContext from "../task_context.js";
import { getContentDisposition, waitForStreamToFinish } from "../utils.js";
import { AdvancedExportOptions, type ExportFormat, ZipExportProviderData } from "./zip/abstract_provider.js";
import { getContentDisposition, waitForStreamToFinish } from "../utils/index"
import { AdvancedExportOptions, ZipExportProviderData } from "./zip/abstract_provider.js";
import HtmlExportProvider from "./zip/html.js";
import MarkdownExportProvider from "./zip/markdown.js";
import ShareThemeExportProvider from "./zip/share_theme.js";
import { AttachmentMeta, AttributeMeta, ExportFormat, NoteMeta, NoteMetaFile } from "../../meta";
import { ValidationError } from "../../errors";
async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, format: ExportFormat, res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) {
if (!["html", "markdown", "share"].includes(format)) {
@@ -34,6 +30,7 @@ async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch,
});
const rewriteFn = (zipExportOptions?.customRewriteLinks ? zipExportOptions?.customRewriteLinks(rewriteLinks, getNoteTargetUrl) : rewriteLinks);
const provider = buildProvider();
const log = getLog();
const noteIdToMeta: Record<string, NoteMeta> = {};
@@ -52,7 +49,8 @@ async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch,
case "markdown":
return new MarkdownExportProvider(providerData);
case "share":
return new ShareThemeExportProvider(providerData);
// TODO: Reintroduce share format.
// return new ShareThemeExportProvider(providerData);
default:
throw new Error();
}
@@ -483,7 +481,7 @@ async function exportToZipFile(noteId: string, format: ExportFormat, zipFilePath
await exportToZip(taskContext, note.getParentBranches()[0], format, fileOutputStream, false, zipExportOptions);
await waitForStreamToFinish(fileOutputStream);
log.info(`Exported '${noteId}' with format '${format}' to '${zipFilePath}'`);
getLog().info(`Exported '${noteId}' with format '${format}' to '${zipFilePath}'`);
}
export default {

View File

@@ -1,13 +1,10 @@
import { NoteType } from "@triliumnext/commons";
import { ExportFormat } from "@triliumnext/core";
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";
export type { ExportFormat, NoteMeta } from "@triliumnext/core";
import { ExportFormat, NoteMeta, NoteMetaFile } from "../../../meta.js";
type RewriteLinksFn = (content: string, noteMeta: NoteMeta) => string;

View File

@@ -2,9 +2,9 @@ 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 { escapeHtml, getResourceDir, isDev } from "../../utils/index";
import { ZipExportProvider } from "./abstract_provider.js";
import { NoteMeta } from "../../../meta";
export default class HtmlExportProvider extends ZipExportProvider {

View File

@@ -1,4 +1,4 @@
import NoteMeta from "../../meta/note_meta";
import { NoteMeta } from "../../../meta.js";
import mdService from "../markdown.js";
import { ZipExportProvider } from "./abstract_provider.js";

20
pnpm-lock.yaml generated
View File

@@ -803,9 +803,6 @@ importers:
'@triliumnext/highlightjs':
specifier: workspace:*
version: link:../../packages/highlightjs
'@triliumnext/turndown-plugin-gfm':
specifier: workspace:*
version: link:../../packages/turndown-plugin-gfm
'@types/archiver':
specifier: 7.0.0
version: 7.0.0
@@ -1704,6 +1701,9 @@ importers:
'@triliumnext/commons':
specifier: workspace:*
version: link:../commons
'@triliumnext/turndown-plugin-gfm':
specifier: workspace:*
version: link:../turndown-plugin-gfm
async-mutex:
specifier: 0.5.0
version: 0.5.0
@@ -17587,8 +17587,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.6.1
'@ckeditor/ckeditor5-widget': 47.6.1
ckeditor5: 47.6.1
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-html-embed@47.6.1':
dependencies:
@@ -17598,8 +17596,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.6.1
'@ckeditor/ckeditor5-widget': 47.6.1
ckeditor5: 47.6.1
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-html-support@47.6.1':
dependencies:
@@ -17615,8 +17611,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.6.1
ckeditor5: 47.6.1
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-icons@47.6.1': {}
@@ -17634,8 +17628,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.6.1
ckeditor5: 47.6.1
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-import-word@47.6.1':
dependencies:
@@ -17659,8 +17651,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.6.1
'@ckeditor/ckeditor5-utils': 47.6.1
ckeditor5: 47.6.1
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-inspector@5.0.0': {}
@@ -17671,8 +17661,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.6.1
'@ckeditor/ckeditor5-utils': 47.6.1
ckeditor5: 47.6.1
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-line-height@47.6.1':
dependencies:
@@ -17697,8 +17685,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.6.1
ckeditor5: 47.6.1
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-list-multi-level@47.6.1':
dependencies: