@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
222 lines (221 loc) • 9.71 kB
JavaScript
import fs from 'node:fs';
import { QueueTask } from '../google_folder/QueueTask.js';
import { SvgTransform } from '../../SvgTransform.js';
import { generateDocumentFrontMatter } from './frontmatters/generateDocumentFrontMatter.js';
import { generateConflictMarkdown } from './frontmatters/generateConflictMarkdown.js';
import { googleMimeToExt } from './TaskLocalFileTransform.js';
import { getUrlHash, urlToFolderId } from '../../utils/idParsers.js';
export class TaskGoogleMarkdownTransform extends QueueTask {
constructor(logger, jobManagerContainer, realFileName, googleFolder, googleFile, destinationDirectory, localFile, localLinks, userConfig) {
super(logger);
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
Object.defineProperty(this, "jobManagerContainer", {
enumerable: true,
configurable: true,
writable: true,
value: jobManagerContainer
});
Object.defineProperty(this, "realFileName", {
enumerable: true,
configurable: true,
writable: true,
value: realFileName
});
Object.defineProperty(this, "googleFolder", {
enumerable: true,
configurable: true,
writable: true,
value: googleFolder
});
Object.defineProperty(this, "googleFile", {
enumerable: true,
configurable: true,
writable: true,
value: googleFile
});
Object.defineProperty(this, "destinationDirectory", {
enumerable: true,
configurable: true,
writable: true,
value: destinationDirectory
});
Object.defineProperty(this, "localFile", {
enumerable: true,
configurable: true,
writable: true,
value: localFile
});
Object.defineProperty(this, "localLinks", {
enumerable: true,
configurable: true,
writable: true,
value: localLinks
});
Object.defineProperty(this, "userConfig", {
enumerable: true,
configurable: true,
writable: true,
value: userConfig
});
this.retries = 0;
if (!this.localFile.fileName) {
throw new Error(`No fileName for: ${this.localFile.id}`);
}
}
async run() {
await this.generate(this.localFile);
return [];
}
async generateBinary(binaryFile) {
await new Promise((resolve, reject) => {
try {
const dest = this.destinationDirectory.createWriteStream(this.realFileName);
dest.on('error', err => {
reject(err);
});
const ext = googleMimeToExt(this.googleFile.mimeType, this.googleFile.name);
const stream = this.googleFolder.createReadStream(`${binaryFile.id}${ext ? '.' + ext : ''}`)
.on('error', err => {
reject(err);
})
.pipe(dest);
stream.on('finish', () => {
resolve();
});
stream.on('error', err => {
reject(err);
});
}
catch (err) {
reject(err);
}
});
}
async generateDrawing(drawingFile) {
await new Promise((resolve, reject) => {
try {
// await this.destinationDirectory.mkdir(getFileDir(targetPath));
const dest = this.destinationDirectory.createWriteStream(this.realFileName);
const svgTransform = new SvgTransform(drawingFile.fileName);
// const svgPath = this.googleScanner.getFilePathPrefix(drawingFile.id) + '.svg';
dest.on('error', err => {
reject(err);
});
const stream = this.googleFolder.createReadStream(`${drawingFile.id}.svg`)
.on('error', err => {
reject(err);
})
.pipe(svgTransform)
.pipe(dest);
stream.on('finish', () => {
this.localLinks.append(drawingFile.id, drawingFile.fileName, Array.from(svgTransform.links));
resolve();
});
stream.on('error', err => {
reject(err);
});
}
catch (err) {
reject(err);
}
});
}
async generateDocument(localFile) {
const links = new Set();
const mdPath = this.googleFolder.getRealPath() + '/' + localFile.id + '.md';
const input = fs.readFileSync(mdPath);
const originalMarkdown = new TextDecoder().decode(input);
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+?)\)/g;
function replaceMarkdownLinks(markdown, replacerFunction) {
return markdown.replace(markdownLinkRegex, (match, linkText, url) => {
// Call the replacer function with the link text, URL, and title
return replacerFunction(linkText, url);
});
}
function customReplacer(linkText, href) {
href = href.replaceAll('\\', '');
const id = urlToFolderId(href);
const hash = getUrlHash(href);
if (id) {
href = 'gdoc:' + id + hash;
}
if (href && !href.startsWith('#') && href.indexOf(':') > -1) {
links.add(href);
}
return `[${linkText}](${href})`;
}
const markdownRewrittenLinks = replaceMarkdownLinks(originalMarkdown, customReplacer);
const pattern = /\*\{\{%\s+.*?\s+%\}\}\*/g;
const markdown = markdownRewrittenLinks.replace(pattern, (match) => {
// Remove the surrounding asterisks
return match.slice(1, -1);
});
const frontMatterOverload = {};
if (markdown.match(/^ *A. {2}/igm)) {
frontMatterOverload['markup'] = 'pandoc';
}
// links = Array.from(converter.links);
const frontMatter = generateDocumentFrontMatter(localFile, Array.from(links), this.userConfig.fm_without_version, frontMatterOverload);
const errors = [];
this.warnings = errors.length;
for (const errorMsg of errors) {
this.logger.warn('Error in: [' + this.localFile.fileName + '](' + this.localFile.fileName + ') ' + errorMsg, {
errorMdFile: this.localFile.fileName,
errorMdMsg: errorMsg
});
}
await this.destinationDirectory.writeFile(this.realFileName, frontMatter + markdown);
this.localLinks.append(localFile.id, localFile.fileName, Array.from(links));
}
async generate(localFile) {
try {
const verStr = this.localFile.version ? ' #' + this.localFile.version : ' ';
if (localFile.type === 'conflict') {
this.logger.info('Transforming conflict: ' + this.localFile.fileName);
const md = generateConflictMarkdown(localFile);
await this.destinationDirectory.writeFile(this.realFileName, md);
}
else if (localFile.type === 'redir') { // TODO
this.logger.info('Transforming redir: ' + this.localFile.fileName);
// const redirectToFile = this.toGenerate.find(f => f.id === localFile.redirectTo);
// const redirectToFile = this.generatedScanner.getFileById(localFile.redirectTo);
// const md = generateRedirectMarkdown(localFile, redirectToFile, this.linkTranslator);
// await this.destinationDirectory.mkdir(getFileDir(targetPath));
// await this.destinationDirectory.writeFile(targetPath, md);
// await this.generatedScanner.update(targetPath, md);
}
else if (localFile.type === 'md') {
this.logger.info('Transforming markdown: ' + this.localFile.fileName + verStr);
// const googleFile = await this.googleScanner.getFileById(localFile.id);
// const downloadFile = await this.downloadFilesStorage.findFile(f => f.id === localFile.id);
if (this.googleFile) { // && downloadFile
await this.generateDocument(localFile);
}
}
else if (localFile.type === 'drawing') {
this.logger.info('Transforming drawing: ' + this.localFile.fileName + verStr);
// const googleFile = await this.googleScanner.getFileById(localFile.id);
// const downloadFile = await this.downloadFilesStorage.findFile(f => f.id === localFile.id);
if (this.googleFile) { // && downloadFile
await this.generateDrawing(localFile);
}
}
else if (localFile.type === 'binary') {
this.logger.info('Transforming binary: ' + this.localFile.fileName + verStr);
if (this.googleFile) { // && downloadFile
await this.generateBinary(localFile);
}
}
this.logger.info('Transformed: ' + this.localFile.fileName + verStr);
}
catch (err) {
this.logger.error('Error transforming ' + localFile.fileName + ' ' + err.stack ? err.stack : err.message);
throw err;
}
}
}