import { Context } from "../struct/context.js"; import { Fragment, fragmentFormats } from "../struct/fragment.js"; import { BasePaths } from "./base-paths.js"; import { FileHelper } from "./file-helper.js"; import { FragmentManager } from "./fragment-manager.js"; import { SettingsReader } from "./settings-reader.js"; import { TemplateManager } from "./template-manager.js"; import { tokenTypes } from "./token.js"; import { Tokenizer } from "./tokenizer.js"; import fs, { write } from 'fs'; export class Renderer { path; context; constructor(path, context) { this.path = path; this.context = context; } readFolder() { return `${BasePaths.contentRoot()}/${this.path}`; } writeFolder() { return `${BasePaths.targetRoot()}/${this.path}`; } renderAll() { // Create output folder fs.mkdirSync(this.writeFolder()); // Get all files const entries = fs.readdirSync(this.readFolder(), { encoding: 'utf-8', withFileTypes: true }); const files = entries.filter( (e) => e.isFile() ); for(let i = 0; i < files.length; i++) { // Render content files, copy non-content files. if(FileHelper.isContent(files[i])) { this.renderPage(files[i]); } else if(FileHelper.isSettingsFile(files[i])) { // pass } else { this.copyFile(files[i]); } } // Get all subdirectories. const subdirs = entries.filter( (e) => e.isDirectory() ); for(let i = 0; i < subdirs.length; i++) { this.renderSubdirectory(subdirs[i]); } } copyFile(fileEnt) { const readPath = `${this.readFolder()}/${fileEnt.name}`; const writePath = `${this.writeFolder()}/${fileEnt.name}`; fs.copyFileSync(readPath, writePath); } renderPage(fileEnt) { const type = FileHelper.getFragmentType(fileEnt); const readPath = `${this.readFolder()}/${fileEnt.name}`; const content = fs.readFileSync(readPath, { encoding: 'utf-8' }); const vars = SettingsReader.readSettingsFromContent(content); const strippedContent = SettingsReader.trimSettingsFromContent(content); const fileContext = new Context(vars); const fullContext = this.context.mergeFrom(fileContext); const contentFragment = new Fragment(type, strippedContent); let root = contentFragment; const templateKey = fullContext.get(`template`); if(templateKey) { const template = new TemplateManager().get(templateKey); if(template) { root = template; } } const pageOutput = this.renderFragment(root, fullContext, new FragmentManager(contentFragment)); const writePath = `${this.writeFolder()}/${FileHelper.getOutputFileName(fileEnt)}`; fs.writeFileSync(writePath, pageOutput); } renderFragment(fragment, localContext, fragmentManager) { if(!fragment) { return ''; } const tokenizer = new Tokenizer(); const fragmentAsHtml = fragment.toHtml().sourceContent; const tokensByVar = tokenizer.tokensByVariable(fragmentAsHtml); const replacedVarTokens = tokensByVar.map( (t) => { if(t.type === tokenTypes.TEXT) { return t.content; } else { const key = t.content.trim(); const value = localContext.get(key); return value; } } ); const fragmentContentAfterVariableReplace = replacedVarTokens.reduce( (a, b) => `${a}${b}` ); const tokensByFragment = tokenizer.tokensByFragment(fragmentContentAfterVariableReplace); const self = this; const replacedFragmentTokens = tokensByFragment.map( (t) => { if(t.type === tokenTypes.TEXT) { return t.content; } else { const key = t.content.trim(); const value = fragmentManager.get(key); return self.renderFragment(value, localContext, fragmentManager); } } ); const final = replacedFragmentTokens.reduce( (a, b) => `${a}${b}` ); return final; } renderSubdirectory(subDirEnt) { const subPath = `${this.readFolder()}/${subDirEnt.name}`; const subdirContext = SettingsReader.readDirectorySettings(subPath); const nextContext = this.context.copy().mergeFrom(subdirContext); const subRenderer = new Renderer(`${this.path}/${subDirEnt.name}`, nextContext); subRenderer.renderAll(); } }