diff --git a/.gitignore b/.gitignore
index bfc5219..9bb1fdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,5 +65,6 @@ node_modules/
# dotenv environment variables file
.env
-.dist
-temp
\ No newline at end of file
+dist
+temp
+bin
\ No newline at end of file
diff --git a/build.js b/build.js
deleted file mode 100644
index 7425795..0000000
--- a/build.js
+++ /dev/null
@@ -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) => `${m.name}`)
- .reverse();
-
-const unorderedListItems = links.map((l) => `
${l}`).join("\r\n");
-const html = `
-
- ${unorderedListItems}
-
-`;
-htmlTemplate = htmlTemplate.replace("{{content}}", html);
-fs.writeFileSync(`${paths.output}/index.html`, htmlTemplate, {
- encoding: "utf-8",
- flag: "ax",
-});
-
-console.log("Done");
diff --git a/firebase.json b/firebase.json
index e18c0e8..5a89d68 100644
--- a/firebase.json
+++ b/firebase.json
@@ -1,6 +1,6 @@
{
"hosting": {
- "public": ".dist",
+ "public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
}
}
diff --git a/package-lock.json b/package-lock.json
index aef21ba..d5e1fd9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,9 @@
"name": "blog",
"version": "1.0.0",
"license": "ISC",
+ "workspaces": [
+ "packages/@zblog/toolchain"
+ ],
"devDependencies": {
"@types/node": "^20.11.5",
"concurrently": "^8.2.1",
@@ -37,6 +40,10 @@
"undici-types": "~5.26.4"
}
},
+ "node_modules/@zblog/toolchain": {
+ "resolved": "packages/@zblog/toolchain",
+ "link": true
+ },
"node_modules/@zeit/schemas": {
"version": "2.29.0",
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz",
@@ -1721,6 +1728,13 @@
"engines": {
"node": ">=8"
}
+ },
+ "packages/@zblog/toolchain": {
+ "version": "1.0.0",
+ "license": "ISC",
+ "bin": {
+ "zblog-toolchain": "bin/zblog-toolchain.js"
+ }
}
}
}
diff --git a/package.json b/package.json
index b70a44e..ddc3a89 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,21 @@
{
"name": "blog",
"version": "1.0.0",
- "description": "",
+ "description": "A static blog generator",
"type": "module",
"main": "index.js",
"scripts": {
"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",
- "build": "node build.js",
- "init": "npm install -g firebase-tools && firebase login",
- "serve": "serve .dist"
+ "watch-build": "nodemon --watch notes --watch src --watch markdown.ts --exec \"npm run build && npm run generate-blog\"",
+ "build": "tsc -p tsconfig.json",
+ "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",
"devDependencies": {
"@types/node": "^20.11.5",
diff --git a/packages/@zblog/toolchain/.gitignore b/packages/@zblog/toolchain/.gitignore
new file mode 100644
index 0000000..92d264c
--- /dev/null
+++ b/packages/@zblog/toolchain/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+bin
+dist
diff --git a/packages/@zblog/toolchain/package.json b/packages/@zblog/toolchain/package.json
new file mode 100644
index 0000000..02541bc
--- /dev/null
+++ b/packages/@zblog/toolchain/package.json
@@ -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"
+}
diff --git a/markdown.js b/packages/@zblog/toolchain/src/markdown.ts
similarity index 80%
rename from markdown.js
rename to packages/@zblog/toolchain/src/markdown.ts
index 0ab1f06..4bb875b 100644
--- a/markdown.js
+++ b/packages/@zblog/toolchain/src/markdown.ts
@@ -1,14 +1,14 @@
class LineFeed {
- originalText = undefined;
+ originalText: string | undefined = undefined;
/**
* @type {string[]}
*/
- feed = undefined;
+ feed: string[] | undefined = undefined;
/**
*
* @param {string} markdown
*/
- constructor(markdown) {
+ constructor(markdown: string) {
this.originalText = markdown;
// TODO: Need to identify line endings
this.feed = markdown.split("\n");
@@ -17,16 +17,16 @@ class LineFeed {
/**
* @returns {string}
*/
- peek() {
- return this.feed[0] ?? undefined;
+ peek(): string {
+ return this.feed?.[0] ?? "";
}
/**
*
* @returns {string}
*/
- claim() {
- const line = this.feed.shift();
+ claim(): string {
+ const line = this.feed?.shift();
if (line === undefined) throw new Error("Feed is empty");
return line;
}
@@ -35,25 +35,25 @@ class LineFeed {
*
* @param {string} line
*/
- push(line) {
+ push(line: string | null | undefined) {
if (line === undefined || line === null) return;
- this.feed = [line, ...this.feed];
+ this.feed = [line, ...(this.feed ?? [])];
}
/**
*
* @returns {boolean}
*/
- isEmpty() {
- return this.feed.length === 0;
+ isEmpty(): boolean {
+ return this.feed?.length === 0;
}
}
class Symbol {
- static canParse(line) {
+ static canParse(line: string) {
throw new Error("Not implemented");
}
- static create(lineFeed, assetDirectory) {
+ static create(lineFeed: LineFeed, assetDirectory: string): Symbol {
throw new Error("Not implemented");
}
/**
@@ -61,7 +61,7 @@ class Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
throw new Error("Not Implemented");
}
render() {
@@ -73,17 +73,17 @@ class Heading extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {number}
*/
- level = 1;
+ level: number = 1;
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line.trim().startsWith("#");
}
@@ -92,7 +92,7 @@ class Heading extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new Heading();
const line = lineFeed.claim();
instance.text = line.replaceAll("#", "").trim();
@@ -107,7 +107,7 @@ class Heading extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -122,7 +122,7 @@ class JustALineBreak extends Symbol {
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line === "";
}
@@ -131,7 +131,7 @@ class JustALineBreak extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new JustALineBreak();
lineFeed.claim();
return instance;
@@ -142,7 +142,7 @@ class JustALineBreak extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -155,17 +155,17 @@ class Paragraph extends Symbol {
/**
* @type {string[]}
*/
- lines = [];
+ lines: string[] = [];
/**
* @type {Symbol[]}
*/
- children = [];
+ children: Symbol[] = [];
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line === "";
}
@@ -174,7 +174,7 @@ class Paragraph extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new Paragraph();
lineFeed.claim();
@@ -198,7 +198,7 @@ class Paragraph extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -211,17 +211,17 @@ class UnorderedListItem extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {number}
*/
- level = 0;
+ level: number = 0;
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line.trim().startsWith("- ");
}
@@ -230,7 +230,7 @@ class UnorderedListItem extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new UnorderedListItem();
const line = lineFeed.claim();
instance.text = line.replaceAll("-", "").trim();
@@ -243,7 +243,7 @@ class UnorderedListItem extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -256,17 +256,17 @@ class OrderedListItem extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {number}
*/
- level = 0;
+ level: number = 0;
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return new RegExp(/^\d\. /).test(line.trim());
}
@@ -275,7 +275,7 @@ class OrderedListItem extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new UnorderedListItem();
const line = lineFeed.claim();
instance.text = line.trim().replace(/^\d\. /, "");
@@ -288,7 +288,7 @@ class OrderedListItem extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -300,25 +300,27 @@ class Link extends Symbol {
/**
* @type {RegExp}
*/
- static canParseRegExp = new RegExp(/^\[.*\]\(.*\)/);
+ static canParseRegExp: RegExp = new RegExp(/^\[.*\]\(.*\)/);
/**
* @type {RegExp}
*/
- static textAndLinkRegExp = new RegExp(/\[(?.*)\]\((?.*)\)/);
+ static textAndLinkRegExp: RegExp = new RegExp(
+ /\[(?.*)\]\((?.*)\)/
+ );
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {string}
*/
- link = "";
+ link: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return Link.canParseRegExp.test(line.trim());
}
@@ -327,7 +329,7 @@ class Link extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new Link();
const line = lineFeed.claim().trim();
const [linkLine, rest] = extractTokenAndRest(Link.textAndLinkRegExp, line);
@@ -343,7 +345,7 @@ class Link extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -356,29 +358,31 @@ class ImageLink extends Symbol {
/**
* @type {RegExp}
*/
- static canParseRegExp = new RegExp(/^!\[.*\]\(.*\)/);
+ static canParseRegExp: RegExp = new RegExp(/^!\[.*\]\(.*\)/);
/**
* @type {RegExp}
*/
- static textAndLinkRegExp = new RegExp(/!\[(?.*)\]\((?.*)\)/);
+ static textAndLinkRegExp: RegExp = new RegExp(
+ /!\[(?.*)\]\((?.*)\)/
+ );
/**
* @type {string}
*/
- static assetDirectoryToken = "@asset/";
+ static assetDirectoryToken: string = "@asset/";
/**
* @type {string}
*/
- alt = "";
+ alt: string = "";
/**
* @type {string}
*/
- link = "";
+ link: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return ImageLink.canParseRegExp.test(line.trim());
}
@@ -387,7 +391,7 @@ class ImageLink extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed, assetDirectory) {
+ static create(lineFeed: LineFeed, assetDirectory: string): Symbol {
const instance = new ImageLink();
const line = lineFeed.claim().trim();
const [linkLine, rest] = extractTokenAndRest(
@@ -408,7 +412,7 @@ class ImageLink extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -421,13 +425,13 @@ class Italic extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line.startsWith("_");
}
@@ -436,7 +440,7 @@ class Italic extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new Italic();
const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(1, line.length);
@@ -465,7 +469,7 @@ class Italic extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -478,13 +482,13 @@ class Bold extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line.startsWith("**");
}
@@ -493,7 +497,7 @@ class Bold extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new Bold();
const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(2, line.length);
@@ -522,7 +526,7 @@ class Bold extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -535,13 +539,13 @@ class SingleLineCode extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return line.startsWith("`");
}
@@ -550,7 +554,7 @@ class SingleLineCode extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new SingleLineCode();
const line = lineFeed.claim().trim();
const lineWithoutStartingUnderscore = line.slice(1, line.length);
@@ -579,7 +583,7 @@ class SingleLineCode extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -592,13 +596,13 @@ class MultiLineCode extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
const trimmedLine = line.trim();
return trimmedLine.startsWith("```");
}
@@ -608,7 +612,7 @@ class MultiLineCode extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new MultiLineCode();
const line = lineFeed.claim().trim();
const lineWithoutStartTag = line.slice(3, line.length);
@@ -656,7 +660,7 @@ class MultiLineCode extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
return;
}
@@ -669,15 +673,15 @@ class TextRow extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {Symbol[]}
*/
- children = [];
+ children: Symbol[] = [];
/**
* @type {Symbol[]}
*/
- allowedChildren = [
+ allowedChildren: (typeof Symbol)[] = [
Bold,
Italic,
ImageLink,
@@ -691,7 +695,7 @@ class TextRow extends Symbol {
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return true;
}
@@ -700,7 +704,7 @@ class TextRow extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new TextRow();
instance.text = lineFeed.claim();
return instance;
@@ -711,7 +715,7 @@ class TextRow extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
for (let i = 0; i < this.text.length; i++) {
if (i === this.text.length - 1) {
const plainText = new PlainText();
@@ -743,17 +747,17 @@ class PlainText extends Symbol {
/**
* @type {string}
*/
- text = "";
+ text: string = "";
/**
* @type {Symbol[]}
*/
- children = [];
+ children: Symbol[] = [];
/**
*
* @param {string} line
* @returns {boolean}
*/
- static canParse(line) {
+ static canParse(line: string): boolean {
return true;
}
@@ -762,7 +766,7 @@ class PlainText extends Symbol {
* @param {LineFeed} lineFeed
* @returns {Symbol}
*/
- static create(lineFeed) {
+ static create(lineFeed: LineFeed): Symbol {
const instance = new PlainText();
instance.text = lineFeed.claim();
return instance;
@@ -773,7 +777,7 @@ class PlainText extends Symbol {
* @param {string} assetDirectory
* @returns {void}
*/
- process(assetDirectory) {
+ process(assetDirectory: string): void {
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} string
* @returns {string[]}
*/
-const splitStringAtToken = (token, string) => {
+const splitStringAtToken = (
+ token: string | RegExp,
+ string: string
+): string[] => {
let index = 0;
let length = 0;
- if (typeof token?.exec === "function") {
+ if (isRegExp(token)) {
const exp = new RegExp(token);
const match = exp.exec(string);
index = match?.index ?? -1;
@@ -808,14 +817,17 @@ const splitStringAtToken = (token, string) => {
/**
*
- * @param {string|RexExp} token
+ * @param {string|RegExp} token
* @param {string} string
* @returns {string[]}
*/
-const extractTokenAndRest = (token, string) => {
+const extractTokenAndRest = (
+ token: string | RegExp,
+ string: string
+): string[] => {
let index = 0;
let length = 0;
- if (typeof token?.exec === "function") {
+ if (isRegExp(token)) {
const exp = new RegExp(token);
const match = exp.exec(string);
index = match?.index ?? -1;
@@ -835,7 +847,7 @@ const extractTokenAndRest = (token, string) => {
* @param {string} token
* @param {string} string
*/
-const getAmountOfTokenInBeginningOfFile = (token, string) => {
+const getAmountOfTokenInBeginningOfFile = (token: string, string: string) => {
let count = 0;
let searchIndex = 0;
while (true) {
@@ -853,7 +865,7 @@ const getAmountOfTokenInBeginningOfFile = (token, string) => {
/**
* @type {Symbol[]}
*/
-const AllSymbols = [
+const AllSymbols: (typeof Symbol)[] = [
JustALineBreak,
Heading,
UnorderedListItem,
@@ -873,7 +885,7 @@ const AllSymbols = [
* @param {string} assetDirectory
* @returns {string}
*/
-export const toHtml = (markdown, assetDirectory) => {
+export const toHtml = (markdown: string, assetDirectory: string): string => {
// Stage one, markdown to symbols
const symbols = toSymbols(markdown, assetDirectory);
@@ -895,7 +907,7 @@ export const toHtml = (markdown, assetDirectory) => {
* @param {Symbol[]} symbols
* @returns {Symbol[]}
*/
-const stageThreeProcessing = (symbols) => {
+const stageThreeProcessing = (symbols: Symbol[]): Symbol[] => {
const localSymbols = [...symbols];
// Turn line break pairs into paragraphs
while (localSymbols.filter((s) => s instanceof JustALineBreak).length !== 0) {
@@ -929,13 +941,22 @@ const stageThreeProcessing = (symbols) => {
* @param {Symbol[]} allowedSymbols
* @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 symbols = [];
+ const symbols: Symbol[] = [];
while (!lineFeed.isEmpty()) {
const line = lineFeed.peek();
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);
symbols.push(symbol);
}
diff --git a/packages/@zblog/toolchain/src/zblog-toolchain.ts b/packages/@zblog/toolchain/src/zblog-toolchain.ts
new file mode 100644
index 0000000..ae48d90
--- /dev/null
+++ b/packages/@zblog/toolchain/src/zblog-toolchain.ts
@@ -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) => `${m.name}`)
+ .reverse();
+ const unorderedListItems = links.map((l) => `- ${l}
`).join("\r\n");
+ const html = `
+
+ ${unorderedListItems}
+
+ `;
+ htmlTemplate = htmlTemplate.replace("{{content}}", html);
+ fs.writeFileSync(`${this.paths.outputDirectory}/index.html`, htmlTemplate, {
+ encoding: "utf-8",
+ flag: "ax",
+ });
+ console.log("Done");
+ }
+}
diff --git a/packages/@zblog/toolchain/tsconfig.json b/packages/@zblog/toolchain/tsconfig.json
new file mode 100644
index 0000000..226ac12
--- /dev/null
+++ b/packages/@zblog/toolchain/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "include": ["src/zblog-toolchain.ts"],
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "bin",
+ "declaration": true,
+ "declarationMap": true
+ }
+}
diff --git a/plugin.js b/plugin.js
deleted file mode 100644
index e69de29..0000000
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..d56059f
--- /dev/null
+++ b/readme.md
@@ -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`
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..a2f8421
--- /dev/null
+++ b/src/index.ts
@@ -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();
diff --git a/notes/1-initial-commit/assets/git.png b/src/notes/1-initial-commit/assets/git.png
similarity index 100%
rename from notes/1-initial-commit/assets/git.png
rename to src/notes/1-initial-commit/assets/git.png
diff --git a/notes/1-initial-commit/note.md b/src/notes/1-initial-commit/note.md
similarity index 100%
rename from notes/1-initial-commit/note.md
rename to src/notes/1-initial-commit/note.md
diff --git a/notes/2-ideas/note.md b/src/notes/2-ideas/note.md
similarity index 100%
rename from notes/2-ideas/note.md
rename to src/notes/2-ideas/note.md
diff --git a/notes/3-markdown-parser/note.md b/src/notes/3-markdown-parser/note.md
similarity index 100%
rename from notes/3-markdown-parser/note.md
rename to src/notes/3-markdown-parser/note.md
diff --git a/notes/4-markdown-parser-part-2/note.md b/src/notes/4-markdown-parser-part-2/note.md
similarity index 100%
rename from notes/4-markdown-parser-part-2/note.md
rename to src/notes/4-markdown-parser-part-2/note.md
diff --git a/notes/5-pomodoro-timer/note.md b/src/notes/5-pomodoro-timer/note.md
similarity index 100%
rename from notes/5-pomodoro-timer/note.md
rename to src/notes/5-pomodoro-timer/note.md
diff --git a/templates/404.html b/src/templates/404.html
similarity index 100%
rename from templates/404.html
rename to src/templates/404.html
diff --git a/templates/index.html b/src/templates/index.html
similarity index 100%
rename from templates/index.html
rename to src/templates/index.html
diff --git a/templates/note.html b/src/templates/note.html
similarity index 100%
rename from templates/note.html
rename to src/templates/note.html
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9d80219
--- /dev/null
+++ b/tsconfig.json
@@ -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. */
+ }
+}