UNPKG

@mieweb/wikigdrive

Version:

Google Drive to MarkDown synchronization

222 lines (221 loc) 9.71 kB
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; } } }