UNPKG

@mieweb/wikigdrive

Version:

Google Drive to MarkDown synchronization

232 lines (231 loc) 9.31 kB
import yaml from 'js-yaml'; import { frontmatter } from './frontmatters/frontmatter.js'; import { MimeTypes } from '../../model/GoogleFile.js'; import { LOG_NAME } from './LocalLog.js'; export const RESERVED_NAMES = [LOG_NAME, '.wgd-directory.yaml', '.wgd-local-log.csv', '.wgd-local-links.csv', '.tree.json', '.private']; export const RESERVED_DIR_NAMES = ['.git']; export function isTextFileName(fileName) { if (fileName.endsWith('.txt')) { return true; } if (fileName.endsWith('.gitignore')) { return true; } if (fileName.endsWith('.ts')) { return true; } if (fileName.endsWith('.css')) { return true; } if (fileName.endsWith('.md')) { return true; } return false; } export function stripConflict(localPath) { const parts = localPath.split('.'); parts[0] = parts[0].replace(/@[0-9]+$/, ''); return parts.join('.'); } export function appendConflict(localPath, no) { const parts = localPath.split('.'); parts[0] = parts[0] + `@${no}`; return parts.join('.'); } export class DirectoryScanner { constructor() { Object.defineProperty(this, "files", { enumerable: true, configurable: true, writable: true, value: {} }); } parseMarkdown(markdown, localPath) { const parsed = frontmatter(markdown); const props = parsed.data; if (!props) { return null; } if (Array.isArray(props.conflicting)) { const conflictFile = { type: 'conflict', id: props.id, title: props.title, modifiedTime: props.date, mimeType: props.mimeType || MimeTypes.MARKDOWN, conflicting: Array.isArray(props.conflicting) ? props.conflicting : [], fileName: stripConflict(localPath), }; return conflictFile; } if (props.redirectTo) { const redirFile = { type: 'redir', id: props.id, title: props.title, modifiedTime: props.date, mimeType: props.mimeType || MimeTypes.MARKDOWN, fileName: stripConflict(localPath), redirectTo: props.redirectTo, }; return redirFile; } const mdFile = { type: 'md', id: props.id, version: props.version, title: props.title, modifiedTime: props.date, mimeType: props.mimeType || MimeTypes.MARKDOWN, lastAuthor: props.lastAuthor, fileName: stripConflict(localPath) }; return mdFile; } async scanDir(existingDirectory) { if (await existingDirectory.exists('.wgd-directory.yaml')) { const yamlContent = await existingDirectory.readFile('/.wgd-directory.yaml'); const props = yaml.load(yamlContent); if (props.type === 'directory' && props.id) { const map = props.fileMap || {}; return map; } } return {}; } async scan(existingDirectory) { this.files = {}; const files = await existingDirectory.list(); for (const realFileName of files) { if (RESERVED_NAMES.indexOf(realFileName) > -1) { continue; } if (!await existingDirectory.exists(realFileName)) { continue; } if (await existingDirectory.isDirectory(realFileName)) { if (realFileName.endsWith('.assets')) { continue; } const props = await (async () => { if (await existingDirectory.exists(`${realFileName}/.wgd-directory.yaml`)) { const yamlContent = await existingDirectory.readFile(`${realFileName}/.wgd-directory.yaml`); return yaml.load(yamlContent); } return undefined; })(); if (props && props.type === 'directory' && props.id) { const directory = { type: 'directory', fileName: stripConflict(realFileName), id: props.id, mimeType: MimeTypes.FOLDER_MIME, modifiedTime: props.date, title: props.title, version: props.version }; this.files[realFileName] = directory; } else { if (RESERVED_DIR_NAMES.indexOf(realFileName) > -1) { continue; } const directory = { type: 'directory', fileName: stripConflict(realFileName), id: 'TO_FILL', mimeType: MimeTypes.FOLDER_MIME, modifiedTime: 'TO_FILL', title: stripConflict(realFileName) }; this.files[realFileName] = directory; } continue; } if (realFileName.endsWith('.debug.xml')) { continue; } const props = await (async () => { if (await existingDirectory.exists('.wgd-directory.yaml')) { const yamlContent = await existingDirectory.readFile('.wgd-directory.yaml'); return yaml.load(yamlContent); } return undefined; })(); const yamlFile = props?.fileMap && props.fileMap[realFileName] ? props.fileMap[realFileName] : null; if (realFileName.endsWith('.svg')) { const drawingFile = { type: 'drawing', fileName: stripConflict(realFileName), id: yamlFile ? yamlFile.id : 'TO_FILL', modifiedTime: yamlFile ? yamlFile.modifiedTime : 'TO_FILL', version: yamlFile ? yamlFile.version : undefined, mimeType: 'image/svg+xml', title: stripConflict(realFileName).replace(/.svg$/, '') }; this.files[realFileName] = drawingFile; } else if (realFileName.endsWith('.md')) { const markdown = (await existingDirectory.readFile(realFileName)).toString(); // const localPath = existingDirectory.getVirtualPath() + realFileName; const localFile = this.parseMarkdown(markdown, realFileName); if (localFile) { if (!localFile.modifiedTime) localFile.modifiedTime = yamlFile?.modifiedTime; if (!localFile.version) localFile.version = yamlFile?.version; this.files[realFileName] = localFile; } else { const mdFile = { type: 'md', fileName: stripConflict(realFileName), id: yamlFile ? yamlFile.id : 'TO_FILL', modifiedTime: yamlFile ? yamlFile.modifiedTime : 'TO_FILL', version: undefined, mimeType: 'text/x-markdown', title: stripConflict(realFileName).replace(/.md/, ''), lastAuthor: '' }; this.files[realFileName] = mdFile; } } else { const binaryFile = { type: 'binary', fileName: stripConflict(realFileName), id: yamlFile ? yamlFile.id : 'TO_FILL', modifiedTime: yamlFile ? yamlFile.modifiedTime : 'TO_FILL', version: yamlFile ? yamlFile.version : undefined, mimeType: (realFileName => { if (realFileName.endsWith('.pdf')) return 'application/pdf'; if (realFileName.endsWith('.json')) return 'text/json'; if (realFileName.endsWith('.js')) return 'text/javascript'; if (realFileName.endsWith('.sh')) return 'text/x-sh'; if (realFileName.endsWith('.toml')) return 'text/toml'; if (isTextFileName(realFileName)) return 'text/plain'; return 'application/binary'; })(realFileName), title: stripConflict(realFileName) }; this.files[realFileName] = binaryFile; } } return this.files; } getFiles() { return this.files; } getFileById(fileId) { const entry = Object.entries(this.files).find(f => f[1].id === fileId); return entry ? entry[1] : null; } }