Continuation on breaking out functionality

This commit is contained in:
2024-01-28 22:09:21 +01:00
parent 502946376f
commit 31c253c99b
5 changed files with 181 additions and 50 deletions

View File

@@ -0,0 +1,112 @@
import fs from "fs";
import {
copyFile,
readFileAsText,
writeTextAsFile,
} from "./toolchain-helpers.js";
import { toHtml } from "./markdown-parser.js";
import { MenuManifest } from "./zblog-toolchain.js";
type MarkdownLoaderOptions = {
notesRootPath: string;
noteFileName: string;
assetsDirectoryName: string;
};
type MarkdownLoaderManifest = {
directoryName: string;
name: string;
markdown: string;
assetDirectoryPath: string;
publicAssetDirectoryPath: string;
assetFiles: { path: string; name: string }[];
}[];
const createMarkdownLoaderManifest = ({
notesRootPath,
assetsDirectoryName,
noteFileName,
}: MarkdownLoaderOptions): MarkdownLoaderManifest => {
const directoryNames = fs.readdirSync(notesRootPath);
return directoryNames.map((noteDirectory) => {
const assetDirectoryPath = `${notesRootPath}/${noteDirectory}/${assetsDirectoryName}`;
return {
directoryName: noteDirectory,
name: noteDirectory,
markdown: fs.readFileSync(
`${notesRootPath}/${noteDirectory}/${noteFileName}`,
{ encoding: "utf-8" }
),
assetDirectoryPath,
publicAssetDirectoryPath: `/${noteDirectory}_`,
assetFiles: fs.existsSync(assetDirectoryPath)
? fs.readdirSync(assetDirectoryPath).map((name) => ({
path: `${assetDirectoryPath}/${name}`,
name,
}))
: [],
};
});
};
const copyAssetsToOutputDirectory = (
manifest: MarkdownLoaderManifest,
outputDirectory: string
) => {
manifest.forEach((m) =>
m.assetFiles.forEach((asset) =>
copyFile(asset.path, `${outputDirectory}/${m.name}_${asset.name}`)
)
);
};
const writeMarkdownAsHtmlToOutputDirectory = (
markdownManifest: MarkdownLoaderManifest,
noteHtmlTemplate: string,
markdownHtmlReplacementTag: string,
outputDirectory: string
) => {
markdownManifest.forEach((m) => {
const markdownAsHtml = toHtml(m.markdown, m.publicAssetDirectoryPath);
const noteHtmlDocument = noteHtmlTemplate.replace(
markdownHtmlReplacementTag,
markdownAsHtml
);
writeTextAsFile(
`${outputDirectory}/${m.directoryName}.html`,
noteHtmlDocument
);
});
};
export const loadMarkdown = (
options: {
rootPath: string;
assetsDirectoryName: string;
noteFileName: string;
noteHtmlTemplatePath: string;
outputDirectory: string;
markdownHtmlReplacementTag: string;
},
menuManifest: MenuManifest
) => {
const markdownManifest = createMarkdownLoaderManifest({
notesRootPath: options.rootPath,
assetsDirectoryName: options.assetsDirectoryName,
noteFileName: options.noteFileName,
});
menuManifest.push(
...markdownManifest.map((m) => ({
name: m.name,
link: `/${m.directoryName}.html`,
}))
);
const noteHtmlTemplate = readFileAsText(options.noteHtmlTemplatePath);
writeMarkdownAsHtmlToOutputDirectory(
markdownManifest,
noteHtmlTemplate,
options.markdownHtmlReplacementTag,
options.outputDirectory
);
copyAssetsToOutputDirectory(markdownManifest, options.outputDirectory);
};

View File

@@ -0,0 +1,24 @@
import fs from "fs";
export const destructivelyRecreateDirectory = (directoryPath: string) => {
if (fs.existsSync(directoryPath)) {
fs.rmdirSync(directoryPath, { recursive: true });
}
fs.mkdirSync(directoryPath, { recursive: true });
};
export const readFileAsText = (path: string) => {
return fs.readFileSync(path, {
encoding: "utf-8",
});
};
export const writeTextAsFile = (path: string, text: string) => {
fs.writeFileSync(path, text, {
encoding: "utf-8",
flag: "ax",
});
};
export const copyFile = (from: string, to: string) => {
fs.copyFileSync(from, to);
};

View File

@@ -1,5 +1,6 @@
import * as fs from "fs";
import { toHtml } from "./markdown.js";
import { destructivelyRecreateDirectory } from "./toolchain-helpers.js";
import { loadMarkdown } from "./markdown-loader.js";
export type Paths = {
outputDirectory: string;
@@ -7,6 +8,7 @@ export type Paths = {
rootPath: string;
assetsDirectoryName: string;
noteFileName: string;
markdownHtmlVariableReplacementTag: string;
};
templates: {
rootPath: string;
@@ -16,60 +18,52 @@ export type Paths = {
};
};
export type MenuManifest = { name: string; link: string }[];
export type PluginBuilder = () => Plugin;
export type Plugin = (builderContext: {
menuManifest: MenuManifest;
}) => void | Promise<void>;
export class ToolchainBuilder {
private paths: Paths;
private menuManifest: MenuManifest = [];
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");
async build() {
destructivelyRecreateDirectory(this.paths.outputDirectory);
loadMarkdown(
{
rootPath: this.paths.notes.rootPath,
assetsDirectoryName: this.paths.notes.assetsDirectoryName,
noteFileName: this.paths.notes.noteFileName,
markdownHtmlReplacementTag:
this.paths.notes.markdownHtmlVariableReplacementTag,
noteHtmlTemplatePath: `${this.paths.templates.rootPath}/${this.paths.templates.noteTemplateName}`,
outputDirectory: this.paths.outputDirectory,
},
this.menuManifest
);
const plugins: Plugin[] = [
() => {},
async () => {
return new Promise<void>((resolve) => {
resolve();
});
},
];
for (const plugin of plugins) {
await plugin({ menuManifest: this.menuManifest });
}
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..");
// static html loader
[this.paths.templates.notFoundName].forEach((filename) => {
fs.copyFileSync(
`${this.paths.templates.rootPath}/${filename}`,
@@ -83,8 +77,8 @@ export class ToolchainBuilder {
encoding: "utf-8",
}
);
const links = manifest
.map((m) => `<a href='/${m.directoryName}.html'>${m.name}</a>`)
const links = this.menuManifest
.map((m) => `<a href='${m.link}'>${m.name}</a>`)
.reverse();
const unorderedListItems = links.map((l) => `<li>${l}</li>`).join("\r\n");
const html = `

View File

@@ -6,6 +6,7 @@ const paths: Paths = {
rootPath: "src/notes",
assetsDirectoryName: "assets",
noteFileName: "note.md",
markdownHtmlVariableReplacementTag: "{{markdown}}",
},
templates: {
rootPath: "src/templates",