@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
150 lines (149 loc) • 5.15 kB
JavaScript
import fs from 'node:fs';
import path from 'node:path';
import JSZip from 'jszip';
import { generateMD5Hash } from '../utils/generateMD5Hash.js';
function getExt(fileName) {
const idx = fileName.lastIndexOf('.');
if (idx > -1) {
return fileName.substring(idx);
}
return '';
}
export class OdtProcessor {
constructor(contentAddressable = false) {
Object.defineProperty(this, "contentAddressable", {
enumerable: true,
configurable: true,
writable: true,
value: contentAddressable
});
Object.defineProperty(this, "contentXml", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "stylesXml", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "files", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "fileNameMap", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "xmlMap", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.fileNameMap = {};
this.xmlMap = {};
}
async load(odtPath) {
if (!fs.existsSync(odtPath)) {
return;
}
const jsZip = new JSZip();
const input = fs.readFileSync(odtPath);
const zip = await jsZip.loadAsync(input);
this.files = zip.folder('').files;
if (this.files['content.xml']) {
this.contentXml = await this.files['content.xml'].async('string');
}
if (this.files['styles.xml']) {
this.stylesXml = await this.files['styles.xml'].async('string');
}
await this.processMathMl();
}
async loadFromBuffer(input) {
const jsZip = new JSZip();
const zip = await jsZip.loadAsync(input);
this.files = zip.folder('').files;
if (this.files['content.xml']) {
this.contentXml = await this.files['content.xml'].async('string');
}
if (this.files['styles.xml']) {
this.stylesXml = await this.files['styles.xml'].async('string');
}
await this.processMathMl();
}
async processMathMl() {
for (const relativePath in this.files) {
if (!relativePath.endsWith('/content.xml')) {
continue;
}
const fileName = relativePath.replace('/content.xml', '.xml').replace(/\s/g, '_');
if (fileName.indexOf('/') === -1) {
const entry = this.files[relativePath];
const buffer = await entry.async('nodebuffer');
const mathMl = new TextDecoder().decode(buffer);
if (mathMl.indexOf('<math ') > -1) {
this.xmlMap[fileName] = mathMl;
}
}
}
}
async unzipAssets(destinationPath, destinationName) {
const assetsDirectory = path.join(destinationPath, destinationName.replace(/.md$/, '.assets'));
const written = [];
for (const relativePath in this.files) {
if (!relativePath.endsWith('.png') && !relativePath.endsWith('.jpg')) {
continue;
}
if (relativePath.endsWith('thumbnail.png')) {
continue;
}
const entry = this.files[relativePath];
const buffer = await entry.async('nodebuffer');
const fileName = relativePath.split('/').pop();
const ext = getExt(fileName);
if (this.contentAddressable && ext) {
this.fileNameMap[fileName] = await generateMD5Hash(buffer) + ext;
}
else {
this.fileNameMap[fileName] = fileName;
}
written.push(this.fileNameMap[fileName]);
if (!fs.existsSync(assetsDirectory)) {
fs.mkdirSync(assetsDirectory, { recursive: true });
}
fs.writeFileSync(path.join(assetsDirectory, this.fileNameMap[fileName]), buffer);
}
if (fs.existsSync(assetsDirectory)) {
const files = fs.readdirSync(assetsDirectory);
for (const file of files) {
if (written.indexOf(file) === -1) {
fs.unlinkSync(path.join(assetsDirectory, file));
}
}
}
if (written.length === 0) {
if (fs.existsSync(assetsDirectory)) {
fs.rmSync(assetsDirectory, { recursive: true, force: true });
}
}
}
getContentXml() {
return this.contentXml;
}
getStylesXml() {
return this.stylesXml;
}
getFileNameMap() {
return this.fileNameMap;
}
getXmlMap() {
return this.xmlMap;
}
}