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",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"zblog-toolchain": "bin/zblog-toolchain.js"
|
"zblog-toolchain": "bin/index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "bin/zblog-toolchain.js",
|
"module": "bin/index.js",
|
||||||
"main": "bin/zblog-toolchain.js",
|
"main": "bin/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"zblog-toolchain": "./bin/zblog-toolchain.js"
|
"zblog-toolchain": "./bin/index.js"
|
||||||
},
|
},
|
||||||
"types": "./bin/zblog-toolchain.d.ts",
|
"types": "./bin/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.json"
|
"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,
|
writeTextAsFile,
|
||||||
} from "./toolchain-helpers.js";
|
} from "./toolchain-helpers.js";
|
||||||
import { toHtml } from "./markdown-parser.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;
|
notesRootPath: string;
|
||||||
noteFileName: string;
|
noteFileName: string;
|
||||||
assetsDirectoryName: string;
|
assetsDirectoryName: string;
|
||||||
@@ -26,7 +26,7 @@ const createMarkdownLoaderManifest = ({
|
|||||||
notesRootPath,
|
notesRootPath,
|
||||||
assetsDirectoryName,
|
assetsDirectoryName,
|
||||||
noteFileName,
|
noteFileName,
|
||||||
}: MarkdownLoaderOptions): MarkdownLoaderManifest => {
|
}: MarkdownLoaderManifestOptions): MarkdownLoaderManifest => {
|
||||||
const directoryNames = fs.readdirSync(notesRootPath);
|
const directoryNames = fs.readdirSync(notesRootPath);
|
||||||
return directoryNames.map((noteDirectory) => {
|
return directoryNames.map((noteDirectory) => {
|
||||||
const assetDirectoryPath = `${notesRootPath}/${noteDirectory}/${assetsDirectoryName}`;
|
const assetDirectoryPath = `${notesRootPath}/${noteDirectory}/${assetsDirectoryName}`;
|
||||||
@@ -79,34 +79,44 @@ const writeMarkdownAsHtmlToOutputDirectory = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadMarkdown = (
|
type MarkdownLoaderOptions = {
|
||||||
options: {
|
rootPath: string;
|
||||||
rootPath: string;
|
assetsDirectoryName: string;
|
||||||
assetsDirectoryName: string;
|
noteFileName: string;
|
||||||
noteFileName: string;
|
noteHtmlTemplatePath: string;
|
||||||
noteHtmlTemplatePath: string;
|
markdownHtmlReplacementTag: 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);
|
|
||||||
};
|
};
|
||||||
|
export class MarkdownLoader implements IPluginBuilder {
|
||||||
|
options: MarkdownLoaderOptions;
|
||||||
|
constructor(options: MarkdownLoaderOptions) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
build(): Plugin {
|
||||||
|
return (builderContext) => {
|
||||||
|
const markdownManifest = createMarkdownLoaderManifest({
|
||||||
|
notesRootPath: this.options.rootPath,
|
||||||
|
assetsDirectoryName: this.options.assetsDirectoryName,
|
||||||
|
noteFileName: this.options.noteFileName,
|
||||||
|
});
|
||||||
|
builderContext.menuManifest.push(
|
||||||
|
...markdownManifest.map((m) => ({
|
||||||
|
name: m.name,
|
||||||
|
link: `/${m.directoryName}.html`,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const noteHtmlTemplate = readFileAsText(
|
||||||
|
this.options.noteHtmlTemplatePath
|
||||||
|
);
|
||||||
|
writeMarkdownAsHtmlToOutputDirectory(
|
||||||
|
markdownManifest,
|
||||||
|
noteHtmlTemplate,
|
||||||
|
this.options.markdownHtmlReplacementTag,
|
||||||
|
builderContext.outputDirectory
|
||||||
|
);
|
||||||
|
copyAssetsToOutputDirectory(
|
||||||
|
markdownManifest,
|
||||||
|
builderContext.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 * as fs from "fs";
|
||||||
import { destructivelyRecreateDirectory } from "./toolchain-helpers.js";
|
import { destructivelyRecreateDirectory } from "./toolchain-helpers.js";
|
||||||
import { loadMarkdown } from "./markdown-loader.js";
|
import { MenuManifest, Plugin, IPluginBuilder } from "./typings.js";
|
||||||
|
|
||||||
export type Paths = {
|
export type Paths = {
|
||||||
outputDirectory: string;
|
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 {
|
export class ToolchainBuilder {
|
||||||
private paths: Paths;
|
private paths: Paths;
|
||||||
|
private plugins: Plugin[] = [];
|
||||||
private menuManifest: MenuManifest = [];
|
private menuManifest: MenuManifest = [];
|
||||||
constructor(paths: Paths) {
|
constructor(paths: Paths) {
|
||||||
this.paths = paths;
|
this.paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPlugins(plugins: IPluginBuilder[]): ToolchainBuilder {
|
||||||
|
this.plugins = plugins.map((p) => p.build());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
async build() {
|
async build() {
|
||||||
destructivelyRecreateDirectory(this.paths.outputDirectory);
|
destructivelyRecreateDirectory(this.paths.outputDirectory);
|
||||||
|
|
||||||
loadMarkdown(
|
for (const plugin of this.plugins) {
|
||||||
{
|
await plugin({
|
||||||
rootPath: this.paths.notes.rootPath,
|
menuManifest: this.menuManifest,
|
||||||
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,
|
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",
|
"extends": "../../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "bin",
|
"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 = {
|
const paths: Paths = {
|
||||||
outputDirectory: "dist",
|
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