Broke out all functionality into plugins
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1733,7 +1733,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"zblog-toolchain": "bin/zblog-toolchain.js"
|
||||
"zblog-toolchain": "bin/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"module": "bin/zblog-toolchain.js",
|
||||
"main": "bin/zblog-toolchain.js",
|
||||
"module": "bin/index.js",
|
||||
"main": "bin/index.js",
|
||||
"bin": {
|
||||
"zblog-toolchain": "./bin/zblog-toolchain.js"
|
||||
"zblog-toolchain": "./bin/index.js"
|
||||
},
|
||||
"types": "./bin/zblog-toolchain.d.ts",
|
||||
"types": "./bin/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json"
|
||||
},
|
||||
|
||||
30
packages/@zblog/toolchain/src/file-loader.ts
Normal file
30
packages/@zblog/toolchain/src/file-loader.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { IPluginBuilder, Plugin } from "./typings.js";
|
||||
import { copyFile } from "./toolchain-helpers.js";
|
||||
|
||||
type Options = {
|
||||
files: { path: string; menuEntry?: boolean; nameOverride?: string }[];
|
||||
};
|
||||
|
||||
export class FileLoader implements IPluginBuilder {
|
||||
options: Options;
|
||||
constructor(options: Options) {
|
||||
this.options = options;
|
||||
}
|
||||
build(): Plugin {
|
||||
return async (builderContext) => {
|
||||
const { menuManifest } = builderContext;
|
||||
const { files } = this.options;
|
||||
files.forEach((file) => {
|
||||
const fileName =
|
||||
file.nameOverride ?? file.path.split("/").pop() ?? file.path;
|
||||
copyFile(file.path, `${builderContext.outputDirectory}/${fileName}`);
|
||||
if (file.menuEntry) {
|
||||
menuManifest.push({
|
||||
name: fileName,
|
||||
link: file.path,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
9
packages/@zblog/toolchain/src/index.ts
Normal file
9
packages/@zblog/toolchain/src/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ToolchainBuilder as TSB, Paths as P } from "./zblog-toolchain.js";
|
||||
export const ToolchainBuilder = TSB;
|
||||
export type Paths = P;
|
||||
import { FileLoader as FL } from "./file-loader.js";
|
||||
export const FileLoader = FL;
|
||||
import { MarkdownLoader as ML } from "./markdown-loader.js";
|
||||
export const MarkdownLoader = ML;
|
||||
import { StartPagePlugin as SPP } from "./start-page-plugin.js";
|
||||
export const StartPagePlugin = SPP;
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
writeTextAsFile,
|
||||
} from "./toolchain-helpers.js";
|
||||
import { toHtml } from "./markdown-parser.js";
|
||||
import { MenuManifest } from "./zblog-toolchain.js";
|
||||
import { IPluginBuilder, MenuManifest, Plugin } from "./typings.js";
|
||||
|
||||
type MarkdownLoaderOptions = {
|
||||
type MarkdownLoaderManifestOptions = {
|
||||
notesRootPath: string;
|
||||
noteFileName: string;
|
||||
assetsDirectoryName: string;
|
||||
@@ -26,7 +26,7 @@ const createMarkdownLoaderManifest = ({
|
||||
notesRootPath,
|
||||
assetsDirectoryName,
|
||||
noteFileName,
|
||||
}: MarkdownLoaderOptions): MarkdownLoaderManifest => {
|
||||
}: MarkdownLoaderManifestOptions): MarkdownLoaderManifest => {
|
||||
const directoryNames = fs.readdirSync(notesRootPath);
|
||||
return directoryNames.map((noteDirectory) => {
|
||||
const assetDirectoryPath = `${notesRootPath}/${noteDirectory}/${assetsDirectoryName}`;
|
||||
@@ -79,34 +79,44 @@ const writeMarkdownAsHtmlToOutputDirectory = (
|
||||
});
|
||||
};
|
||||
|
||||
export const loadMarkdown = (
|
||||
options: {
|
||||
type MarkdownLoaderOptions = {
|
||||
rootPath: string;
|
||||
assetsDirectoryName: string;
|
||||
noteFileName: string;
|
||||
noteHtmlTemplatePath: string;
|
||||
outputDirectory: string;
|
||||
markdownHtmlReplacementTag: string;
|
||||
},
|
||||
menuManifest: MenuManifest
|
||||
) => {
|
||||
};
|
||||
export class MarkdownLoader implements IPluginBuilder {
|
||||
options: MarkdownLoaderOptions;
|
||||
constructor(options: MarkdownLoaderOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
build(): Plugin {
|
||||
return (builderContext) => {
|
||||
const markdownManifest = createMarkdownLoaderManifest({
|
||||
notesRootPath: options.rootPath,
|
||||
assetsDirectoryName: options.assetsDirectoryName,
|
||||
noteFileName: options.noteFileName,
|
||||
notesRootPath: this.options.rootPath,
|
||||
assetsDirectoryName: this.options.assetsDirectoryName,
|
||||
noteFileName: this.options.noteFileName,
|
||||
});
|
||||
menuManifest.push(
|
||||
builderContext.menuManifest.push(
|
||||
...markdownManifest.map((m) => ({
|
||||
name: m.name,
|
||||
link: `/${m.directoryName}.html`,
|
||||
}))
|
||||
);
|
||||
const noteHtmlTemplate = readFileAsText(options.noteHtmlTemplatePath);
|
||||
const noteHtmlTemplate = readFileAsText(
|
||||
this.options.noteHtmlTemplatePath
|
||||
);
|
||||
writeMarkdownAsHtmlToOutputDirectory(
|
||||
markdownManifest,
|
||||
noteHtmlTemplate,
|
||||
options.markdownHtmlReplacementTag,
|
||||
options.outputDirectory
|
||||
this.options.markdownHtmlReplacementTag,
|
||||
builderContext.outputDirectory
|
||||
);
|
||||
copyAssetsToOutputDirectory(
|
||||
markdownManifest,
|
||||
builderContext.outputDirectory
|
||||
);
|
||||
copyAssetsToOutputDirectory(markdownManifest, options.outputDirectory);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
32
packages/@zblog/toolchain/src/start-page-plugin.ts
Normal file
32
packages/@zblog/toolchain/src/start-page-plugin.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { IPluginBuilder, Plugin } from "./typings.js";
|
||||
import { readFileAsText, writeTextAsFile } from "./toolchain-helpers.js";
|
||||
|
||||
type StartPagePluginOptions = {
|
||||
indexTemplatePath: string;
|
||||
contentTemplateTag: string;
|
||||
};
|
||||
export class StartPagePlugin implements IPluginBuilder {
|
||||
options: StartPagePluginOptions;
|
||||
constructor(options: StartPagePluginOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
build(): Plugin {
|
||||
return (builderContext) => {
|
||||
let htmlTemplate = readFileAsText(this.options.indexTemplatePath);
|
||||
const links = builderContext.menuManifest
|
||||
.map((m) => `<a href='${m.link}'>${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);
|
||||
writeTextAsFile(
|
||||
`${builderContext.outputDirectory}/index.html`,
|
||||
htmlTemplate
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
9
packages/@zblog/toolchain/src/typings.ts
Normal file
9
packages/@zblog/toolchain/src/typings.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export type MenuManifest = { name: string; link: string }[];
|
||||
export interface IPluginBuilder {
|
||||
build(): Plugin;
|
||||
}
|
||||
|
||||
export type Plugin = (builderContext: {
|
||||
outputDirectory: string;
|
||||
menuManifest: MenuManifest;
|
||||
}) => void | Promise<void>;
|
||||
@@ -1,96 +1,32 @@
|
||||
import * as fs from "fs";
|
||||
import { destructivelyRecreateDirectory } from "./toolchain-helpers.js";
|
||||
import { loadMarkdown } from "./markdown-loader.js";
|
||||
import { MenuManifest, Plugin, IPluginBuilder } from "./typings.js";
|
||||
|
||||
export type Paths = {
|
||||
outputDirectory: string;
|
||||
notes: {
|
||||
rootPath: string;
|
||||
assetsDirectoryName: string;
|
||||
noteFileName: string;
|
||||
markdownHtmlVariableReplacementTag: string;
|
||||
};
|
||||
templates: {
|
||||
rootPath: string;
|
||||
noteTemplateName: string;
|
||||
indexTemplateName: string;
|
||||
notFoundName: string;
|
||||
};
|
||||
};
|
||||
|
||||
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 plugins: Plugin[] = [];
|
||||
private menuManifest: MenuManifest = [];
|
||||
constructor(paths: Paths) {
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
addPlugins(plugins: IPluginBuilder[]): ToolchainBuilder {
|
||||
this.plugins = plugins.map((p) => p.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
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}`,
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin({
|
||||
menuManifest: this.menuManifest,
|
||||
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 });
|
||||
}
|
||||
|
||||
// static html loader
|
||||
|
||||
[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 = 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 = `
|
||||
<ul>
|
||||
${unorderedListItems}
|
||||
<ul>
|
||||
`;
|
||||
htmlTemplate = htmlTemplate.replace("{{content}}", html);
|
||||
fs.writeFileSync(`${this.paths.outputDirectory}/index.html`, htmlTemplate, {
|
||||
encoding: "utf-8",
|
||||
flag: "ax",
|
||||
});
|
||||
console.log("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"include": ["src/zblog-toolchain.ts"],
|
||||
"include": ["src/index.ts"],
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
|
||||
45
src/index.ts
45
src/index.ts
@@ -1,19 +1,36 @@
|
||||
import { Paths, ToolchainBuilder } from "@zblog/toolchain";
|
||||
import {
|
||||
ToolchainBuilder,
|
||||
Paths,
|
||||
FileLoader,
|
||||
MarkdownLoader,
|
||||
StartPagePlugin,
|
||||
} from "@zblog/toolchain";
|
||||
|
||||
const paths: Paths = {
|
||||
outputDirectory: "dist",
|
||||
notes: {
|
||||
rootPath: "src/notes",
|
||||
assetsDirectoryName: "assets",
|
||||
noteFileName: "note.md",
|
||||
markdownHtmlVariableReplacementTag: "{{markdown}}",
|
||||
},
|
||||
templates: {
|
||||
rootPath: "src/templates",
|
||||
noteTemplateName: "note.html",
|
||||
indexTemplateName: "index.html",
|
||||
notFoundName: "404.html",
|
||||
},
|
||||
};
|
||||
|
||||
new ToolchainBuilder(paths).build();
|
||||
new ToolchainBuilder(paths)
|
||||
.addPlugins([
|
||||
new MarkdownLoader({
|
||||
rootPath: "src/notes",
|
||||
assetsDirectoryName: "assets",
|
||||
markdownHtmlReplacementTag: "{{markdown}}",
|
||||
noteFileName: "note.md",
|
||||
noteHtmlTemplatePath: `src/templates/note.html`,
|
||||
}),
|
||||
new FileLoader({
|
||||
files: [
|
||||
{
|
||||
path: "src/templates/404.html",
|
||||
nameOverride: "Not Found Page",
|
||||
menuEntry: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
new StartPagePlugin({
|
||||
indexTemplatePath: "src/templates/index.html",
|
||||
contentTemplateTag: "{{content}}",
|
||||
}),
|
||||
])
|
||||
.build();
|
||||
|
||||
Reference in New Issue
Block a user