trilium/src/services/export/tar.js

151 lines
4.2 KiB
JavaScript
Raw Normal View History

"use strict";
const html = require('html');
2018-11-25 03:58:38 +08:00
const tar = require('tar-stream');
const sanitize = require("sanitize-filename");
2018-11-24 21:44:56 +08:00
const mimeTypes = require('mime-types');
const TurndownService = require('turndown');
/**
* @param format - 'html' or 'markdown'
*/
async function exportToTar(branch, format, res) {
const turndownService = new TurndownService();
2018-11-25 17:26:45 +08:00
// path -> number of occurences
const existingPaths = {};
2018-11-25 03:58:38 +08:00
const pack = tar.pack();
const exportedNoteIds = [];
const name = await exportNoteInner(branch, '');
2018-11-25 17:26:45 +08:00
function getUniqueFilename(fileName) {
}
async function exportNoteInner(branch, directory, existingNames) {
const note = await branch.getNote();
2018-11-25 17:26:45 +08:00
const baseFileName = directory + sanitize(note.title);
if (exportedNoteIds.includes(note.noteId)) {
2018-11-25 17:26:45 +08:00
saveMetadataFile(baseFileName, {
version: 1,
clone: true,
noteId: note.noteId,
prefix: branch.prefix
});
return;
}
const metadata = {
version: 1,
clone: false,
noteId: note.noteId,
title: note.title,
prefix: branch.prefix,
isExpanded: branch.isExpanded,
type: note.type,
mime: note.mime,
// we don't export dateCreated and dateModified of any entity since that would be a bit misleading
attributes: (await note.getOwnedAttributes()).map(attribute => {
return {
type: attribute.type,
name: attribute.name,
value: attribute.value,
isInheritable: attribute.isInheritable,
position: attribute.position
};
}),
links: (await note.getLinks()).map(link => {
return {
type: link.type,
targetNoteId: link.targetNoteId
}
})
};
2018-11-24 21:44:56 +08:00
if (note.type === 'text') {
metadata.format = format;
}
if (await note.hasLabel('excludeFromExport')) {
return;
}
2018-11-25 17:26:45 +08:00
saveMetadataFile(baseFileName, metadata);
saveDataFile(baseFileName, note);
exportedNoteIds.push(note.noteId);
const childBranches = await note.getChildBranches();
if (childBranches.length > 0) {
2018-11-25 17:26:45 +08:00
saveDirectory(baseFileName);
}
for (const childBranch of childBranches) {
2018-11-25 17:26:45 +08:00
await exportNoteInner(childBranch, baseFileName + "/");
}
2018-11-25 17:26:45 +08:00
return baseFileName;
}
function saveDataFile(childFileName, note) {
2018-11-24 21:44:56 +08:00
let content = note.content;
2018-11-25 17:26:45 +08:00
let extension;
2018-11-24 21:44:56 +08:00
if (note.type === 'text') {
if (format === 'html') {
content = html.prettyPrint(note.content, {indent_size: 2});
}
else if (format === 'markdown') {
content = turndownService.turndown(note.content);
2018-11-25 17:26:45 +08:00
extension = 'md';
2018-11-24 21:44:56 +08:00
}
else {
throw new Error("Unknown format: " + format);
}
}
2018-11-25 17:26:45 +08:00
if (!extension) {
extension = mimeTypes.extension(note.mime)
|| getExceptionalExtension(note.mime)
|| "dat";
}
2018-11-24 21:44:56 +08:00
if (!childFileName.toLowerCase().endsWith(extension)) {
childFileName += "." + extension;
}
pack.entry({name: childFileName, size: content.length}, content);
}
function getExceptionalExtension(mime) {
if (mime === 'application/x-javascript') {
return 'js';
}
}
2018-11-25 17:26:45 +08:00
function saveMetadataFile(baseFileName, metadata) {
const metadataJson = JSON.stringify(metadata, null, '\t');
2018-11-25 17:26:45 +08:00
pack.entry({name: getUniqueFilename(baseFileName + ".meta"), size: metadataJson.length}, metadataJson);
}
2018-11-25 17:26:45 +08:00
function saveDirectory(baseFileName) {
pack.entry({name: baseFileName, type: 'directory'});
}
pack.finalize();
res.setHeader('Content-Disposition', 'file; filename="' + name + '.tar"');
res.setHeader('Content-Type', 'application/tar');
pack.pipe(res);
}
module.exports = {
exportToTar
};