import {parse as parseYaml, stringify} from "https://deno.land/std@0.166.0/encoding/yaml.ts"; function trimSubstring(str: string, start: number, end: number) { while (str[start] === ' ' || str[start] === '\t' || str[start] === '\r' || str[start] === '\n') { start++; } return str.substring(start, end); } function splitTwo(str: string, separator: string): [string, string] { const index = str.indexOf(separator); if (index === -1) { return [str, '']; } return [str.substring(0, index), str.substring(index + separator.length)]; } export interface Doc { from_url?: string; name: string; author: string; star: number; fork: number; desc: string; url: string; tags: string[]; readme: string; } export class DocParser { constructor() { } parse(content: string) { if(!content.startsWith('---')){ throw new Error('Invalid doc format'); } const index = content.indexOf('\n---', 3); const meta = content.substring(3, index); const readme = trimSubstring(content, index + 4, content.length); const doc = parseYaml(meta) as Doc; doc.readme = readme; return doc; } parseAlternative(content: string): Doc { if(!content.startsWith('---')){ throw new Error('Invalid doc format'); } const index = content.indexOf('\n---', 3); const meta = content.substring(3, index); const readme = trimSubstring(content, index + 4, content.length); const doc = {} as Doc; const lines = meta.split('\n').map(line => line.trim()); for(const line of lines){ if(line.length === 0) continue; let [key, value] = splitTwo(line, ':'); value = value.trimStart(); if (value.startsWith('"') && value.endsWith('"')) { value = JSON.parse( value ); } key = key.trimEnd(); switch(key){ case 'name': doc.name = value; break; case 'author': doc.author = value; break; case 'star': doc.star = parseInt(value); break; case 'fork': doc.fork = parseInt(value); break; case 'desc': doc.desc = value; break; case 'url': doc.url = value; break; case 'tags': doc.tags = JSON.parse(value.replaceAll("'",'"')); break; case 'from_url': doc.from_url = value; break; default: throw new Error(`Invalid key: ${key}`); } } doc.readme = readme; return doc; } } export async function readDoc(path: string, alternative: boolean = false): Promise { const doc = await Deno.readTextFile(path); const parser = new DocParser(); if(alternative){ return parser.parseAlternative(doc); } return parser.parse(doc); } function genDocContent(doc: Doc): string { return `--- ${doc.from_url ? `from_url: ${doc.from_url}\n` : ''}name: "${doc.name}" author: "${doc.author}" star: ${doc.star} fork: ${doc.fork} desc: ${JSON.stringify(doc.desc)} url: ${doc.url} tags: ${JSON.stringify(doc.tags)} --- ${doc.readme} `; } export async function writeDoc(path: string, doc: Doc, options?: Omit) { const content = genDocContent(doc); await Deno.writeTextFile(path, content, options); } async function main() { const doc = await readDoc('./mysample.md'); console.log(stringify(doc as any)); const doc2 = await readDoc('./mysample.md', true); console.log(stringify(doc2 as any)); console.log(genDocContent(doc2)); } if (import.meta.main) { await main(); }