143 lines
2.5 KiB
JavaScript
143 lines
2.5 KiB
JavaScript
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 `<h${this.level}>${this.text}</h${this.level}>`;
|
|
}
|
|
}
|
|
|
|
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 `<p>${this.text}</p>`;
|
|
}
|
|
}
|
|
|
|
const Factories = [Heading, CatchAll];
|
|
|
|
export const toHtml = (markdown) => {
|
|
const symbols = toSymbols(markdown);
|
|
const html = symbols.map((s) => s.render()).join("");
|
|
return `<div>${html}</div>`;
|
|
};
|
|
|
|
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;
|
|
};
|