class LineFeed { originalText = undefined; /** * @type {string[]} */ feed = undefined; /** * * @param {string} markdown */ constructor(markdown) { this.originalText = markdown; // TODO: Need to identify line endings this.feed = markdown.split("\n"); } /** * @returns {string} */ peek() { return this.feed[0] ?? undefined; } /** * * @returns {string} */ claim() { const line = this.feed.shift(); if (line === undefined) throw new Error("Feed is empty"); return line; } /** * * @returns {boolean} */ isEmpty() { return this.feed.length === 0; } } class Symbol { static canParse(line) { throw new Error("Not implemented"); } static create(lineFeed) { throw new Error("Not implemented"); } render() { throw new Error("Not implemented"); } } class Heading extends Symbol { /** * @type {string} */ text = ""; /** * @type {number} */ level = 1; /** * * @param {string} line * @returns {boolean} */ static canParse(line) { return line.trim().startsWith("#"); } /** * * @param {LineFeed} lineFeed * @returns {Symbol} */ static create(lineFeed) { const instance = new Heading(); const line = lineFeed.claim(); instance.text = line.replaceAll("#", "").trim(); instance.level = line.split("").reduce((aggregate, current) => { return current === "#" ? aggregate + 1 : aggregate; }, 0); return instance; } render() { return `${this.text}`; } } class CatchAll extends Symbol { /** * @type {string} */ text = ""; /** * * @param {string} line * @returns {boolean} */ static canParse(line) { return true; } /** * * @param {LineFeed} lineFeed * @returns {Symbol} */ static create(lineFeed) { const instance = new CatchAll(); instance.text = lineFeed.claim(); return instance; } render() { return `

${this.text}

`; } } const Factories = [Heading, CatchAll]; export const toHtml = (markdown) => { const symbols = toSymbols(markdown); const html = symbols.map((s) => s.render()).join(""); return `
${html}
`; }; const toSymbols = (markdown) => { const lineFeed = new LineFeed(markdown); const symbols = []; while (!lineFeed.isEmpty()) { const line = lineFeed.peek(); const factory = Factories.find((factory) => factory.canParse(line)); const symbol = factory.create(lineFeed); symbols.push(symbol); } return symbols; };