shubform
Version:
Shubform is a static website generator
112 lines (111 loc) • 4.16 kB
JavaScript
import { load } from "js-yaml";
import { readFileSync, existsSync } from "fs";
import { marked } from "./marked.js";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
// Get the current file's directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export const compParse = (body, attrs, config) => {
const re = new RegExp("\\={3}\\n(.*?)\\n\\={3}", "gms").exec(body);
if (!re || re?.length === 0) {
return [new SegmentResult(marked.parse(body, { async: false }))];
}
// Separate body into segments
let segments = [];
// console.log("re:", re);
const index = re.index;
const firstPart = body.slice(0, index);
const firstSegment = new SegmentResult(marked.parse(firstPart, { async: false }));
segments.push(firstSegment);
const lastPart = body.slice(index + re[0].length);
body = lastPart;
const match = re[1];
// console.log("Building component:", match);
const component = new SegmentResult(parseComponent(match, attrs, config), true);
// console.log("component:", component);
segments.push(component);
if (body) {
// If there is more to the body, parse it
const otherSegments = compParse(body, attrs, config);
segments.push(...otherSegments);
}
return segments;
};
const parseComponent = (component, attrs, config) => {
const yamlObj = load(component);
const crit = ComponentCriteria.create(yamlObj);
return buildComponent(crit, attrs, config);
};
const buildComponent = (comp, attrs, config) => {
let isError = false;
// loaded component html
const dir = config.dev.componentsDir;
// Check if native components exist
let componentPath = "";
let jsPath = "";
const nativePath = join(__dirname, `./nativeComponents/${comp.component}.html`);
if (existsSync(nativePath)) {
componentPath = nativePath;
jsPath = join(__dirname, `./nativeComponents/${comp.component}.js`);
}
else {
// mkdirSync("nativeComponents", { recursive: true });
componentPath = `${dir}/${comp.component}.html`;
jsPath = `${dir}/${comp.component}.js`;
}
const componentHTML = readFileSync(componentPath, "utf8");
// loaded component js
const js = readFileSync(jsPath, "utf8");
// loaded component copy
const copyHTML = [];
if (comp.copy) {
comp.copy.forEach((copy) => {
const copyPath = `${config.dev.componentCopyDir}/${attrs.componentCopyDir}/${copy}.md`;
if (existsSync(copyPath) === false) {
console.error("\x1b[31m%s\x1b[0m", "Copy file does not exist: " + copyPath);
isError = true;
return;
}
const copyText = readFileSync(copyPath, "utf8");
// console.log("copyText:", copyText);
copyHTML.push(compParse(copyText, attrs, config).reduce((acc, x) => acc + x.body, ""));
});
}
if (comp.comps) {
comp.comps.forEach((comp) => {
const subComp = buildComponent(comp, attrs, config);
// console.log(subComp);
copyHTML.push(subComp);
});
}
if (isError) {
throw Error("Check above for error");
}
// execute js loaded from file
const jsBuildComp = eval("(" + js + ")");
// execute js function to build component
const jsResult = jsBuildComp(copyHTML, componentHTML, comp.style);
return jsResult;
};
class ComponentCriteria {
constructor(component, copy, comps, style = {}) {
this.component = component;
this.copy = copy;
this.comps = comps;
this.style = style;
}
static create(obj) {
const component = obj["component"];
const copy = obj["copy"];
const comps = obj["comps"]?.map((comp) => ComponentCriteria.create(comp)) ?? [];
const style = obj["style"] ?? {};
return new ComponentCriteria(component, copy, comps, style);
}
}
class SegmentResult {
constructor(body, isComponent = false) {
this.body = body;
this.isComponent = isComponent;
}
}