Restructured toolchain to its own workspace

This commit is contained in:
2024-01-28 20:34:07 +01:00
parent d333ed98af
commit 502946376f
23 changed files with 312 additions and 196 deletions

3
.gitignore vendored
View File

@@ -65,5 +65,6 @@ node_modules/
# dotenv environment variables file # dotenv environment variables file
.env .env
.dist dist
temp temp
bin

View File

@@ -1,97 +0,0 @@
import * as fs from "fs";
import { toHtml } from "./markdown.js";
const paths = {
output: ".dist",
notes: {
root: "notes",
assets: "assets",
note: "note.md",
},
templates: {
root: "templates",
note: "note.html",
index: "index.html",
notFound: "404.html",
},
};
if (fs.readdirSync(".").includes(paths.output)) {
fs.rmSync(paths.output, { recursive: true, force: true });
console.log("Starting rebuild");
} else {
console.log("Starting build");
}
fs.mkdirSync(paths.output);
console.log("Indexing content..");
const directoryNames = fs.readdirSync(paths.notes.root);
const manifest = directoryNames.map((noteDirectory) => ({
directoryName: noteDirectory,
name: noteDirectory,
markdown: fs.readFileSync(
`${paths.notes.root}/${noteDirectory}/${paths.notes.note}`,
{ encoding: "utf-8" }
),
assetDirectoryPath: `${paths.notes.root}/${noteDirectory}/${paths.notes.assets}`,
publicAssetDirectoryPath: `/${noteDirectory}_`,
}));
console.log("Parsing content..");
manifest.forEach((m) => {
const notePath = `${paths.templates.root}/${paths.templates.note}`;
let htmlTemplate = fs.readFileSync(notePath, {
encoding: "utf-8",
});
htmlTemplate = htmlTemplate.replace(
"{{markdown}}",
toHtml(m.markdown, m.publicAssetDirectoryPath)
);
fs.writeFileSync(`${paths.output}/${m.directoryName}.html`, htmlTemplate, {
encoding: "utf-8",
flag: "ax",
});
if (!fs.existsSync(m.assetDirectoryPath)) return;
const assetsList = fs.readdirSync(m.assetDirectoryPath);
assetsList.forEach((assetName) =>
fs.cpSync(
`${m.assetDirectoryPath}/${assetName}`,
`${paths.output}/${m.name}_${assetName}`
)
);
});
console.log("Moving static templates..");
[paths.templates.notFound].forEach((filename) => {
fs.copyFileSync(
`${paths.templates.root}/${filename}`,
`${paths.output}/${filename}`
);
});
console.log("Building startpage..");
let htmlTemplate = fs.readFileSync(
`${paths.templates.root}/${paths.templates.index}`,
{
encoding: "utf-8",
}
);
const links = manifest
.map((m) => `<a href='/${m.directoryName}.html'>${m.name}</a>`)
.reverse();
const unorderedListItems = links.map((l) => `<li>${l}</li>`).join("\r\n");
const html = `
<ul>
${unorderedListItems}
<ul>
`;
htmlTemplate = htmlTemplate.replace("{{content}}", html);
fs.writeFileSync(`${paths.output}/index.html`, htmlTemplate, {
encoding: "utf-8",
flag: "ax",
});
console.log("Done");

View File

@@ -1,6 +1,6 @@
{ {
"hosting": { "hosting": {
"public": ".dist", "public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"] "ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
} }
} }

14
package-lock.json generated
View File

@@ -8,6 +8,9 @@
"name": "blog", "name": "blog",
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"workspaces": [
"packages/@zblog/toolchain"
],
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.5", "@types/node": "^20.11.5",
"concurrently": "^8.2.1", "concurrently": "^8.2.1",
@@ -37,6 +40,10 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@zblog/toolchain": {
"resolved": "packages/@zblog/toolchain",
"link": true
},
"node_modules/@zeit/schemas": { "node_modules/@zeit/schemas": {
"version": "2.29.0", "version": "2.29.0",
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz", "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz",
@@ -1721,6 +1728,13 @@
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
},
"packages/@zblog/toolchain": {
"version": "1.0.0",
"license": "ISC",
"bin": {
"zblog-toolchain": "bin/zblog-toolchain.js"
}
} }
} }
} }

View File

@@ -1,17 +1,21 @@
{ {
"name": "blog", "name": "blog",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "A static blog generator",
"type": "module", "type": "module",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"dev": "concurrently --kill-others -n Build,Serve -c auto \"npm run watch-build\" \"npm run serve\"", "dev": "concurrently --kill-others -n Build,Serve -c auto \"npm run watch-build\" \"npm run serve\"",
"watch-build": "nodemon --watch notes --watch build.js --watch markdown.js --watch templates -e js,md,html,css build.js", "watch-build": "nodemon --watch notes --watch src --watch markdown.ts --exec \"npm run build && npm run generate-blog\"",
"build": "node build.js", "build": "tsc -p tsconfig.json",
"init": "npm install -g firebase-tools && firebase login", "serve": "serve dist",
"serve": "serve .dist" "generate-blog": "node bin/index.js",
"initialize-firebase": "npm install -g firebase-tools && firebase login"
}, },
"author": "", "workspaces": [
"packages/@zblog/toolchain"
],
"author": "Wholteza (Zackarias Montell)",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.5", "@types/node": "^20.11.5",

3
packages/@zblog/toolchain/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules
bin
dist

View File

@@ -0,0 +1,17 @@
{
"name": "@zblog/toolchain",
"version": "1.0.0",
"description": "",
"type": "module",
"module": "bin/zblog-toolchain.js",
"main": "bin/zblog-toolchain.js",
"bin": {
"zblog-toolchain": "./bin/zblog-toolchain.js"
},
"types": "./bin/zblog-toolchain.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json"
},
"author": "",
"license": "ISC"
}

View File

@@ -1,14 +1,14 @@
class LineFeed { class LineFeed {
originalText = undefined; originalText: string | undefined = undefined;
/** /**
* @type {string[]} * @type {string[]}
*/ */
feed = undefined; feed: string[] | undefined = undefined;
/** /**
* *
* @param {string} markdown * @param {string} markdown
*/ */
constructor(markdown) { constructor(markdown: string) {
this.originalText = markdown; this.originalText = markdown;
// TODO: Need to identify line endings // TODO: Need to identify line endings
this.feed = markdown.split("\n"); this.feed = markdown.split("\n");
@@ -17,16 +17,16 @@ class LineFeed {
/** /**
* @returns {string} * @returns {string}
*/ */
peek() { peek(): string {
return this.feed[0] ?? undefined; return this.feed?.[0] ?? "";
} }
/** /**
* *
* @returns {string} * @returns {string}
*/ */
claim() { claim(): string {
const line = this.feed.shift(); const line = this.feed?.shift();
if (line === undefined) throw new Error("Feed is empty"); if (line === undefined) throw new Error("Feed is empty");
return line; return line;
} }
@@ -35,25 +35,25 @@ class LineFeed {
* *
* @param {string} line * @param {string} line
*/ */
push(line) { push(line: string | null | undefined) {
if (line === undefined || line === null) return; if (line === undefined || line === null) return;
this.feed = [line, ...this.feed]; this.feed = [line, ...(this.feed ?? [])];
} }
/** /**
* *
* @returns {boolean} * @returns {boolean}
*/ */
isEmpty() { isEmpty(): boolean {
return this.feed.length === 0; return this.feed?.length === 0;
} }
} }
class Symbol { class Symbol {
static canParse(line) { static canParse(line: string) {
throw new Error("Not implemented"); throw new Error("Not implemented");
} }
static create(lineFeed, assetDirectory) { static create(lineFeed: LineFeed, assetDirectory: string): Symbol {
throw new Error("Not implemented"); throw new Error("Not implemented");
} }
/** /**
@@ -61,7 +61,7 @@ class Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
throw new Error("Not Implemented"); throw new Error("Not Implemented");
} }
render() { render() {
@@ -73,17 +73,17 @@ class Heading extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {number} * @type {number}
*/ */
level = 1; level: number = 1;
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line.trim().startsWith("#"); return line.trim().startsWith("#");
} }
@@ -92,7 +92,7 @@ class Heading extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new Heading(); const instance = new Heading();
const line = lineFeed.claim(); const line = lineFeed.claim();
instance.text = line.replaceAll("#", "").trim(); instance.text = line.replaceAll("#", "").trim();
@@ -107,7 +107,7 @@ class Heading extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -122,7 +122,7 @@ class JustALineBreak extends Symbol {
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line === ""; return line === "";
} }
@@ -131,7 +131,7 @@ class JustALineBreak extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new JustALineBreak(); const instance = new JustALineBreak();
lineFeed.claim(); lineFeed.claim();
return instance; return instance;
@@ -142,7 +142,7 @@ class JustALineBreak extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -155,17 +155,17 @@ class Paragraph extends Symbol {
/** /**
* @type {string[]} * @type {string[]}
*/ */
lines = []; lines: string[] = [];
/** /**
* @type {Symbol[]} * @type {Symbol[]}
*/ */
children = []; children: Symbol[] = [];
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line === ""; return line === "";
} }
@@ -174,7 +174,7 @@ class Paragraph extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new Paragraph(); const instance = new Paragraph();
lineFeed.claim(); lineFeed.claim();
@@ -198,7 +198,7 @@ class Paragraph extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -211,17 +211,17 @@ class UnorderedListItem extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {number} * @type {number}
*/ */
level = 0; level: number = 0;
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line.trim().startsWith("- "); return line.trim().startsWith("- ");
} }
@@ -230,7 +230,7 @@ class UnorderedListItem extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new UnorderedListItem(); const instance = new UnorderedListItem();
const line = lineFeed.claim(); const line = lineFeed.claim();
instance.text = line.replaceAll("-", "").trim(); instance.text = line.replaceAll("-", "").trim();
@@ -243,7 +243,7 @@ class UnorderedListItem extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -256,17 +256,17 @@ class OrderedListItem extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {number} * @type {number}
*/ */
level = 0; level: number = 0;
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return new RegExp(/^\d\. /).test(line.trim()); return new RegExp(/^\d\. /).test(line.trim());
} }
@@ -275,7 +275,7 @@ class OrderedListItem extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new UnorderedListItem(); const instance = new UnorderedListItem();
const line = lineFeed.claim(); const line = lineFeed.claim();
instance.text = line.trim().replace(/^\d\. /, ""); instance.text = line.trim().replace(/^\d\. /, "");
@@ -288,7 +288,7 @@ class OrderedListItem extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -300,25 +300,27 @@ class Link extends Symbol {
/** /**
* @type {RegExp} * @type {RegExp}
*/ */
static canParseRegExp = new RegExp(/^\[.*\]\(.*\)/); static canParseRegExp: RegExp = new RegExp(/^\[.*\]\(.*\)/);
/** /**
* @type {RegExp} * @type {RegExp}
*/ */
static textAndLinkRegExp = new RegExp(/\[(?<text>.*)\]\((?<link>.*)\)/); static textAndLinkRegExp: RegExp = new RegExp(
/\[(?<text>.*)\]\((?<link>.*)\)/
);
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {string} * @type {string}
*/ */
link = ""; link: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return Link.canParseRegExp.test(line.trim()); return Link.canParseRegExp.test(line.trim());
} }
@@ -327,7 +329,7 @@ class Link extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new Link(); const instance = new Link();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const [linkLine, rest] = extractTokenAndRest(Link.textAndLinkRegExp, line); const [linkLine, rest] = extractTokenAndRest(Link.textAndLinkRegExp, line);
@@ -343,7 +345,7 @@ class Link extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -356,29 +358,31 @@ class ImageLink extends Symbol {
/** /**
* @type {RegExp} * @type {RegExp}
*/ */
static canParseRegExp = new RegExp(/^!\[.*\]\(.*\)/); static canParseRegExp: RegExp = new RegExp(/^!\[.*\]\(.*\)/);
/** /**
* @type {RegExp} * @type {RegExp}
*/ */
static textAndLinkRegExp = new RegExp(/!\[(?<text>.*)\]\((?<link>.*)\)/); static textAndLinkRegExp: RegExp = new RegExp(
/!\[(?<text>.*)\]\((?<link>.*)\)/
);
/** /**
* @type {string} * @type {string}
*/ */
static assetDirectoryToken = "@asset/"; static assetDirectoryToken: string = "@asset/";
/** /**
* @type {string} * @type {string}
*/ */
alt = ""; alt: string = "";
/** /**
* @type {string} * @type {string}
*/ */
link = ""; link: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return ImageLink.canParseRegExp.test(line.trim()); return ImageLink.canParseRegExp.test(line.trim());
} }
@@ -387,7 +391,7 @@ class ImageLink extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed, assetDirectory) { static create(lineFeed: LineFeed, assetDirectory: string): Symbol {
const instance = new ImageLink(); const instance = new ImageLink();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const [linkLine, rest] = extractTokenAndRest( const [linkLine, rest] = extractTokenAndRest(
@@ -408,7 +412,7 @@ class ImageLink extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -421,13 +425,13 @@ class Italic extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line.startsWith("_"); return line.startsWith("_");
} }
@@ -436,7 +440,7 @@ class Italic extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new Italic(); const instance = new Italic();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(1, line.length); const lineWithoutStartingUnderscore = line.slice(1, line.length);
@@ -465,7 +469,7 @@ class Italic extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -478,13 +482,13 @@ class Bold extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line.startsWith("**"); return line.startsWith("**");
} }
@@ -493,7 +497,7 @@ class Bold extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new Bold(); const instance = new Bold();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(2, line.length); const lineWithoutStartingUnderscore = line.slice(2, line.length);
@@ -522,7 +526,7 @@ class Bold extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -535,13 +539,13 @@ class SingleLineCode extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return line.startsWith("`"); return line.startsWith("`");
} }
@@ -550,7 +554,7 @@ class SingleLineCode extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new SingleLineCode(); const instance = new SingleLineCode();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(1, line.length); const lineWithoutStartingUnderscore = line.slice(1, line.length);
@@ -579,7 +583,7 @@ class SingleLineCode extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -592,13 +596,13 @@ class MultiLineCode extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
const trimmedLine = line.trim(); const trimmedLine = line.trim();
return trimmedLine.startsWith("```"); return trimmedLine.startsWith("```");
} }
@@ -608,7 +612,7 @@ class MultiLineCode extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new MultiLineCode(); const instance = new MultiLineCode();
const line = lineFeed.claim().trim(); const line = lineFeed.claim().trim();
const lineWithoutStartTag = line.slice(3, line.length); const lineWithoutStartTag = line.slice(3, line.length);
@@ -656,7 +660,7 @@ class MultiLineCode extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -669,15 +673,15 @@ class TextRow extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {Symbol[]} * @type {Symbol[]}
*/ */
children = []; children: Symbol[] = [];
/** /**
* @type {Symbol[]} * @type {Symbol[]}
*/ */
allowedChildren = [ allowedChildren: (typeof Symbol)[] = [
Bold, Bold,
Italic, Italic,
ImageLink, ImageLink,
@@ -691,7 +695,7 @@ class TextRow extends Symbol {
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return true; return true;
} }
@@ -700,7 +704,7 @@ class TextRow extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new TextRow(); const instance = new TextRow();
instance.text = lineFeed.claim(); instance.text = lineFeed.claim();
return instance; return instance;
@@ -711,7 +715,7 @@ class TextRow extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
for (let i = 0; i < this.text.length; i++) { for (let i = 0; i < this.text.length; i++) {
if (i === this.text.length - 1) { if (i === this.text.length - 1) {
const plainText = new PlainText(); const plainText = new PlainText();
@@ -743,17 +747,17 @@ class PlainText extends Symbol {
/** /**
* @type {string} * @type {string}
*/ */
text = ""; text: string = "";
/** /**
* @type {Symbol[]} * @type {Symbol[]}
*/ */
children = []; children: Symbol[] = [];
/** /**
* *
* @param {string} line * @param {string} line
* @returns {boolean} * @returns {boolean}
*/ */
static canParse(line) { static canParse(line: string): boolean {
return true; return true;
} }
@@ -762,7 +766,7 @@ class PlainText extends Symbol {
* @param {LineFeed} lineFeed * @param {LineFeed} lineFeed
* @returns {Symbol} * @returns {Symbol}
*/ */
static create(lineFeed) { static create(lineFeed: LineFeed): Symbol {
const instance = new PlainText(); const instance = new PlainText();
instance.text = lineFeed.claim(); instance.text = lineFeed.claim();
return instance; return instance;
@@ -773,7 +777,7 @@ class PlainText extends Symbol {
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {void} * @returns {void}
*/ */
process(assetDirectory) { process(assetDirectory: string): void {
return; return;
} }
@@ -782,16 +786,21 @@ class PlainText extends Symbol {
} }
} }
const isRegExp = (value: unknown): value is RegExp => value instanceof RegExp;
/** /**
* *
* @param {string|RexExp} token * @param {string|RexExp} token
* @param {string} string * @param {string} string
* @returns {string[]} * @returns {string[]}
*/ */
const splitStringAtToken = (token, string) => { const splitStringAtToken = (
token: string | RegExp,
string: string
): string[] => {
let index = 0; let index = 0;
let length = 0; let length = 0;
if (typeof token?.exec === "function") { if (isRegExp(token)) {
const exp = new RegExp(token); const exp = new RegExp(token);
const match = exp.exec(string); const match = exp.exec(string);
index = match?.index ?? -1; index = match?.index ?? -1;
@@ -808,14 +817,17 @@ const splitStringAtToken = (token, string) => {
/** /**
* *
* @param {string|RexExp} token * @param {string|RegExp} token
* @param {string} string * @param {string} string
* @returns {string[]} * @returns {string[]}
*/ */
const extractTokenAndRest = (token, string) => { const extractTokenAndRest = (
token: string | RegExp,
string: string
): string[] => {
let index = 0; let index = 0;
let length = 0; let length = 0;
if (typeof token?.exec === "function") { if (isRegExp(token)) {
const exp = new RegExp(token); const exp = new RegExp(token);
const match = exp.exec(string); const match = exp.exec(string);
index = match?.index ?? -1; index = match?.index ?? -1;
@@ -835,7 +847,7 @@ const extractTokenAndRest = (token, string) => {
* @param {string} token * @param {string} token
* @param {string} string * @param {string} string
*/ */
const getAmountOfTokenInBeginningOfFile = (token, string) => { const getAmountOfTokenInBeginningOfFile = (token: string, string: string) => {
let count = 0; let count = 0;
let searchIndex = 0; let searchIndex = 0;
while (true) { while (true) {
@@ -853,7 +865,7 @@ const getAmountOfTokenInBeginningOfFile = (token, string) => {
/** /**
* @type {Symbol[]} * @type {Symbol[]}
*/ */
const AllSymbols = [ const AllSymbols: (typeof Symbol)[] = [
JustALineBreak, JustALineBreak,
Heading, Heading,
UnorderedListItem, UnorderedListItem,
@@ -873,7 +885,7 @@ const AllSymbols = [
* @param {string} assetDirectory * @param {string} assetDirectory
* @returns {string} * @returns {string}
*/ */
export const toHtml = (markdown, assetDirectory) => { export const toHtml = (markdown: string, assetDirectory: string): string => {
// Stage one, markdown to symbols // Stage one, markdown to symbols
const symbols = toSymbols(markdown, assetDirectory); const symbols = toSymbols(markdown, assetDirectory);
@@ -895,7 +907,7 @@ export const toHtml = (markdown, assetDirectory) => {
* @param {Symbol[]} symbols * @param {Symbol[]} symbols
* @returns {Symbol[]} * @returns {Symbol[]}
*/ */
const stageThreeProcessing = (symbols) => { const stageThreeProcessing = (symbols: Symbol[]): Symbol[] => {
const localSymbols = [...symbols]; const localSymbols = [...symbols];
// Turn line break pairs into paragraphs // Turn line break pairs into paragraphs
while (localSymbols.filter((s) => s instanceof JustALineBreak).length !== 0) { while (localSymbols.filter((s) => s instanceof JustALineBreak).length !== 0) {
@@ -929,13 +941,22 @@ const stageThreeProcessing = (symbols) => {
* @param {Symbol[]} allowedSymbols * @param {Symbol[]} allowedSymbols
* @returns {Symbol[]} * @returns {Symbol[]}
*/ */
const toSymbols = (markdown, assetDirectory, allowedSymbols = AllSymbols) => { const toSymbols = (
markdown: string,
assetDirectory: string,
allowedSymbols: (typeof Symbol)[] = AllSymbols
): Symbol[] => {
const lineFeed = new LineFeed(markdown); const lineFeed = new LineFeed(markdown);
const symbols = []; const symbols: Symbol[] = [];
while (!lineFeed.isEmpty()) { while (!lineFeed.isEmpty()) {
const line = lineFeed.peek(); const line = lineFeed.peek();
const factory = allowedSymbols.find((factory) => factory.canParse(line)); const factory = allowedSymbols.find((factory) => factory.canParse(line));
if (factory === undefined) {
const textRow = TextRow.create(lineFeed);
symbols.push(textRow);
continue;
}
const symbol = factory.create(lineFeed, assetDirectory); const symbol = factory.create(lineFeed, assetDirectory);
symbols.push(symbol); symbols.push(symbol);
} }

View File

@@ -0,0 +1,102 @@
import * as fs from "fs";
import { toHtml } from "./markdown.js";
export type Paths = {
outputDirectory: string;
notes: {
rootPath: string;
assetsDirectoryName: string;
noteFileName: string;
};
templates: {
rootPath: string;
noteTemplateName: string;
indexTemplateName: string;
notFoundName: string;
};
};
export class ToolchainBuilder {
private paths: Paths;
constructor(paths: Paths) {
this.paths = paths;
}
build() {
if (fs.readdirSync(".").includes(this.paths.outputDirectory)) {
fs.rmSync(this.paths.outputDirectory, { recursive: true, force: true });
console.log("Starting rebuild");
} else {
console.log("Starting build");
}
fs.mkdirSync(this.paths.outputDirectory);
console.log("Indexing content..");
const directoryNames = fs.readdirSync(this.paths.notes.rootPath);
const manifest = directoryNames.map((noteDirectory) => ({
directoryName: noteDirectory,
name: noteDirectory,
markdown: fs.readFileSync(
`${this.paths.notes.rootPath}/${noteDirectory}/${this.paths.notes.noteFileName}`,
{ encoding: "utf-8" }
),
assetDirectoryPath: `${this.paths.notes.rootPath}/${noteDirectory}/${this.paths.notes.assetsDirectoryName}`,
publicAssetDirectoryPath: `/${noteDirectory}_`,
}));
console.log("Parsing content..");
manifest.forEach((m) => {
const notePath = `${this.paths.templates.rootPath}/${this.paths.templates.noteTemplateName}`;
let htmlTemplate = fs.readFileSync(notePath, {
encoding: "utf-8",
});
htmlTemplate = htmlTemplate.replace(
"{{markdown}}",
toHtml(m.markdown, m.publicAssetDirectoryPath)
);
fs.writeFileSync(
`${this.paths.outputDirectory}/${m.directoryName}.html`,
htmlTemplate,
{
encoding: "utf-8",
flag: "ax",
}
);
if (!fs.existsSync(m.assetDirectoryPath)) return;
const assetsList = fs.readdirSync(m.assetDirectoryPath);
assetsList.forEach((assetName) =>
fs.cpSync(
`${m.assetDirectoryPath}/${assetName}`,
`${this.paths.outputDirectory}/${m.name}_${assetName}`
)
);
});
console.log("Moving static templates..");
[this.paths.templates.notFoundName].forEach((filename) => {
fs.copyFileSync(
`${this.paths.templates.rootPath}/${filename}`,
`${this.paths.outputDirectory}/${filename}`
);
});
console.log("Building startpage..");
let htmlTemplate = fs.readFileSync(
`${this.paths.templates.rootPath}/${this.paths.templates.indexTemplateName}`,
{
encoding: "utf-8",
}
);
const links = manifest
.map((m) => `<a href='/${m.directoryName}.html'>${m.name}</a>`)
.reverse();
const unorderedListItems = links.map((l) => `<li>${l}</li>`).join("\r\n");
const html = `
<ul>
${unorderedListItems}
<ul>
`;
htmlTemplate = htmlTemplate.replace("{{content}}", html);
fs.writeFileSync(`${this.paths.outputDirectory}/index.html`, htmlTemplate, {
encoding: "utf-8",
flag: "ax",
});
console.log("Done");
}
}

View File

@@ -0,0 +1,9 @@
{
"include": ["src/zblog-toolchain.ts"],
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "bin",
"declaration": true,
"declarationMap": true
}
}

View File

10
readme.md Normal file
View File

@@ -0,0 +1,10 @@
# ZBlog
## Getting Started
1. Clone the repository.
1. Install node 20.
1. Install all npmjs packages `npm run install --workspaces`
1. Build all workspaces `npm run build --workspaces`
1. Generate site `npm run generate-blog`
1. Start dev server `npm run dev`

18
src/index.ts Normal file
View File

@@ -0,0 +1,18 @@
import { Paths, ToolchainBuilder } from "@zblog/toolchain";
const paths: Paths = {
outputDirectory: "dist",
notes: {
rootPath: "src/notes",
assetsDirectoryName: "assets",
noteFileName: "note.md",
},
templates: {
rootPath: "src/templates",
noteTemplateName: "note.html",
indexTemplateName: "index.html",
notFoundName: "404.html",
},
};
new ToolchainBuilder(paths).build();

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"include": ["src/index.ts"],
"compilerOptions": {
"moduleResolution": "NodeNext",
"target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"module": "NodeNext" /* Specify what module code is generated. */,
"outDir": "bin" /* Specify an output folder for all emitted files. */,
"noEmit": false /* Disable emitting files from a compilation. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}