Compare commits
	
		
			2 Commits
		
	
	
		
			c0dc360579
			...
			325b747bd8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 325b747bd8 | |||
|   | 850721934a | 
							
								
								
									
										52
									
								
								build.js
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								build.js
									
									
									
									
									
								
							| @ -1,46 +1,20 @@ | ||||
| import fs from 'fs'; | ||||
| import Showdown from 'showdown'; | ||||
| import 'dotenv/config'; | ||||
| import { ENV } from './lib/env.js'; | ||||
| import { BasePaths } from './lib/render/base-paths.js'; | ||||
| import { Renderer } from './lib/render/renderer.js'; | ||||
| import { FileHelper } from './lib/render/file-helper.js'; | ||||
| import { SettingsReader } from './lib/render/settings-reader.js'; | ||||
| import { Context } from './lib/struct/context.js'; | ||||
| 
 | ||||
| const converter = new Showdown.Converter(); | ||||
| 
 | ||||
| let inputDir = 'content/'; | ||||
| if(ENV.getIsSet('VS_INPUT_DIR')) { | ||||
|     inputDir = ENV.getString('VS_INPUT_DIR'); | ||||
| // Delete prior output, if exists.
 | ||||
| if(fs.existsSync(BasePaths.targetRoot())) { | ||||
|     fs.rmSync(BasePaths.targetRoot(), { recursive: true, force: true }); | ||||
| } | ||||
| 
 | ||||
| let outputDir = 'build/'; | ||||
| if(ENV.getIsSet('VS_OUTPUT_DIR')) { | ||||
|     outputDir = ENV.getString('VS_OUTPUT_DIR'); | ||||
| } | ||||
| // fs.mkdirSync(BasePaths.targetRoot());
 | ||||
| 
 | ||||
| const fileEntities = fs.readdirSync(inputDir, {recursive: true, withFileTypes: true, encoding: 'utf8'}); | ||||
| const startPath = ''; | ||||
| const rootContext = SettingsReader.readDirectorySettings(`${BasePaths.contentRoot()}/${startPath}`); | ||||
| 
 | ||||
| fileEntities.forEach((fileEnt) => { | ||||
|     const filenameTokens = fileEnt.name.split('.'); | ||||
|     const extension = filenameTokens[filenameTokens.length - 1]; | ||||
| const renderer = new Renderer(startPath, rootContext); | ||||
| 
 | ||||
|     if(extension === 'md') { | ||||
|         const fullPath = fileEnt.parentPath + fileEnt.name; | ||||
| 
 | ||||
|         const content = fs.readFileSync(fullPath, {encoding: 'utf8'}); | ||||
| 
 | ||||
|         const converted = converter.makeHtml(content); | ||||
| 
 | ||||
|         let outputFileName = ""; | ||||
|         for(let i = 0; i < filenameTokens.length - 1; i++) { | ||||
|             outputFileName += filenameTokens[i]; | ||||
|         } | ||||
| 
 | ||||
|         outputFileName += '.html'; | ||||
| 
 | ||||
|         const trimmedPath = fileEnt.parentPath.replace(inputDir, ""); | ||||
| 
 | ||||
|         const outputPath = outputDir + trimmedPath + outputFileName; | ||||
| 
 | ||||
|         fs.writeFileSync(outputPath, converted); | ||||
| 
 | ||||
|         console.log(`Converted ${fullPath} -> ${outputPath}`); | ||||
|     } | ||||
| }); | ||||
| renderer.renderAll(); | ||||
							
								
								
									
										1
									
								
								content/fragments/var-test.vs.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								content/fragments/var-test.vs.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| test variable = <* test_var *> | ||||
							
								
								
									
										4
									
								
								content/site/_settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								content/site/_settings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| { | ||||
|     "test_var": "Outer settings", | ||||
|     "template": "base-template" | ||||
| } | ||||
							
								
								
									
										4
									
								
								content/site/sub/_settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								content/site/sub/_settings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| { | ||||
|     "test_var": "Inner setting", | ||||
|     "template": "" | ||||
| } | ||||
							
								
								
									
										3
									
								
								content/site/sub/test.vs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								content/site/sub/test.vs.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| # Hello world from another directory! | ||||
| 
 | ||||
| <{ var-test }> | ||||
							
								
								
									
										3
									
								
								content/site/test_root.vs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								content/site/test_root.vs.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| # Hello world! | ||||
| 
 | ||||
| <{ var-test }> | ||||
							
								
								
									
										6
									
								
								content/templates/base-template.vs.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								content/templates/base-template.vs.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| <div> | ||||
|     <h3> | ||||
|         Outer wrapper! | ||||
|     </h3> | ||||
|     <{ content }> | ||||
| </div> | ||||
| @ -1 +0,0 @@ | ||||
| # Here's a test! | ||||
							
								
								
									
										2
									
								
								lib/constants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								lib/constants.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| export const DEFAULT_SOURCE_BASE = './content'; | ||||
| export const DEFUALT_TARGET_BASE = './build'; | ||||
							
								
								
									
										24
									
								
								lib/render/base-paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/render/base-paths.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| import { DEFAULT_SOURCE_BASE, DEFUALT_TARGET_BASE } from "../constants.js"; | ||||
| import { ENV } from "../env.js" | ||||
| 
 | ||||
| export const BasePaths = { | ||||
|     sourceRoot() { | ||||
|         const overridePath = ENV.getString(`VS_INPUT_DIR`); | ||||
| 
 | ||||
|         return overridePath ?? DEFAULT_SOURCE_BASE; | ||||
|     }, | ||||
|     targetRoot() { | ||||
|         const overridePath = ENV.getString(`VS_OUTPUT_DIR`); | ||||
| 
 | ||||
|         return overridePath ?? DEFUALT_TARGET_BASE; | ||||
|     }, | ||||
|     templates() { | ||||
|         return `${this.sourceRoot()}/templates`; | ||||
|     }, | ||||
|     fragments() { | ||||
|         return `${this.sourceRoot()}/fragments`; | ||||
|     }, | ||||
|     contentRoot() { | ||||
|         return `${this.sourceRoot()}/site`; | ||||
|     }, | ||||
| } | ||||
							
								
								
									
										66
									
								
								lib/render/file-helper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								lib/render/file-helper.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| import { fragmentFormats } from "../struct/fragment.js"; | ||||
| 
 | ||||
| export const FileHelper = { | ||||
|     isContent(fileEnt) { | ||||
|         return this.isFragment(fileEnt); | ||||
|     }, | ||||
| 
 | ||||
|     isFragment(fileEnt) { | ||||
|         if(!fileEnt.isFile()) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         const contentExtensions = [ | ||||
|             '.vs.md', | ||||
|             '.vs.html', | ||||
|         ]; | ||||
| 
 | ||||
|         const matchesAnyContentExtensions = contentExtensions.some( | ||||
|             (ext) => fileEnt.name.endsWith(ext) | ||||
|         ); | ||||
| 
 | ||||
|         return matchesAnyContentExtensions; | ||||
|     }, | ||||
| 
 | ||||
|     isSettingsFile(fileEnt) { | ||||
|         if(!fileEnt.isFile()) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return fileEnt.name === '_settings.json'; | ||||
|     }, | ||||
| 
 | ||||
|     getFragmentType(fileEnt) { | ||||
|         const filePath = fileEnt.name; | ||||
|          | ||||
|         if(filePath.endsWith('.vs.md')) { | ||||
|             return fragmentFormats.V_MARKDOWN; | ||||
|         } | ||||
| 
 | ||||
|         if(filePath.endsWith('.vs.html')) { | ||||
|             return fragmentFormats.V_HTML; | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     }, | ||||
| 
 | ||||
|     getOutputFileName(fileEnt) { | ||||
|         if(this.isContent(fileEnt)) { | ||||
|             const stripped = this.getBaseName(fileEnt); | ||||
| 
 | ||||
|             return `${stripped}.html`; | ||||
|         } else { | ||||
|             return fileEnt.name; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     getBaseName(fileEnt) { | ||||
|         const isMd = (this.getFragmentType(fileEnt) === fragmentFormats.V_MARKDOWN); | ||||
|         const ext = isMd ? '.vs.md' : '.vs.html'; | ||||
| 
 | ||||
|         const idx = fileEnt.name.lastIndexOf(ext); | ||||
|         const stripped = fileEnt.name.substring(0, idx); | ||||
| 
 | ||||
|         return stripped; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								lib/render/fragment-manager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								lib/render/fragment-manager.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| import fs from 'fs'; | ||||
| import { BasePaths } from "./base-paths.js"; | ||||
| import { FileHelper } from './file-helper.js'; | ||||
| import { Fragment } from '../struct/fragment.js'; | ||||
| 
 | ||||
| export const SPECIAL_CONTENT_SYMBOL = `content`; | ||||
| 
 | ||||
| const FragmentSuperManager = { | ||||
|     fragments: null, | ||||
|     initialized: false, | ||||
| 
 | ||||
|     initialize() { | ||||
|         const fragmentDir = BasePaths.fragments(); | ||||
|         const entries = fs.readdirSync(fragmentDir, { encoding: 'utf-8', withFileTypes: true }); | ||||
|         const fragmentEntries = entries.filter( | ||||
|             (e) => FileHelper.isFragment(e) | ||||
|         ); | ||||
| 
 | ||||
|         let fragments = []; | ||||
| 
 | ||||
|         for(let i = 0; i < fragmentEntries.length; i++) { | ||||
|             const type = FileHelper.getFragmentType(fragmentEntries[i]); | ||||
|             const path = fragmentEntries[i].parentPath + '/' + fragmentEntries[i].name; | ||||
|             const contents = fs.readFileSync(path, { encoding: 'utf-8' }); | ||||
| 
 | ||||
|             const fragment = new Fragment(type, contents); | ||||
| 
 | ||||
|             const name = FileHelper.getBaseName(fragmentEntries[i]); | ||||
| 
 | ||||
|             fragments.push({ | ||||
|                 name: name, | ||||
|                 fragment: fragment | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         this.fragments = fragments; | ||||
|         this.initialized = true; | ||||
|     }, | ||||
| 
 | ||||
|     get(key)  { | ||||
|         const match = this.fragments?.find( | ||||
|             (e) => e.name === key | ||||
|         ); | ||||
| 
 | ||||
|         return match?.fragment; | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| export class FragmentManager { | ||||
|     contentFragment; | ||||
|     _fragmentSuperManager; | ||||
| 
 | ||||
|     constructor(contentFragment) { | ||||
|         this.contentFragment = contentFragment; | ||||
|         this._fragmentSuperManager = FragmentSuperManager; | ||||
| 
 | ||||
|         if(!FragmentSuperManager.initialized) { | ||||
|             FragmentSuperManager.initialize(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     get(key) { | ||||
|         if(key === SPECIAL_CONTENT_SYMBOL) { | ||||
|             return this.contentFragment; | ||||
|         } | ||||
| 
 | ||||
|         return this._fragmentSuperManager.get(key); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										150
									
								
								lib/render/renderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								lib/render/renderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | ||||
| 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(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										35
									
								
								lib/render/settings-reader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/render/settings-reader.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| import fs from 'fs'; | ||||
| import { Variable } from '../struct/variable.js'; | ||||
| import { Context } from '../struct/context.js'; | ||||
| 
 | ||||
| export const SettingsReader = { | ||||
|     trimSettingsFromContent(rawContent) { | ||||
|         return rawContent; | ||||
|     }, | ||||
|     readSettingsFromContent(rawContent) { | ||||
|         return []; | ||||
|     }, | ||||
|     readDirectorySettings(directoryPath) { | ||||
|         if(!fs.existsSync(directoryPath)) { | ||||
|             return new Context(); | ||||
|         } | ||||
| 
 | ||||
|         if(!directoryPath.endsWith('/')) { | ||||
|             directoryPath += '/'; | ||||
|         } | ||||
| 
 | ||||
|         const settingsPath = directoryPath + '_settings.json'; | ||||
|         if(!fs.existsSync(settingsPath)) { | ||||
|             return new Context(); | ||||
|         } | ||||
| 
 | ||||
|         const settingsFileContent = fs.readFileSync(settingsPath, { encoding: 'utf-8' }); | ||||
|         const dict = JSON.parse(settingsFileContent); | ||||
| 
 | ||||
|         const vars = Object.keys(dict).map( | ||||
|             (k) => new Variable(k, dict[k]) | ||||
|         ); | ||||
| 
 | ||||
|         return new Context(vars); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										64
									
								
								lib/render/template-manager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								lib/render/template-manager.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| import fs from 'fs'; | ||||
| import { Fragment, fragmentFormats } from "../struct/fragment.js"; | ||||
| import { BasePaths } from "./base-paths.js"; | ||||
| import { FileHelper } from './file-helper.js'; | ||||
| 
 | ||||
| const TemplateSuperManager = { | ||||
|     templates: null, | ||||
|     initalized: false, | ||||
| 
 | ||||
|     initialize() { | ||||
|         const templateDir = BasePaths.templates(); | ||||
|         const entries = fs.readdirSync(templateDir, { encoding: 'utf-8', withFileTypes: true }); | ||||
|         const templateEntries = entries.filter( | ||||
|             (e) => FileHelper.isFragment(e) | ||||
|         ); | ||||
| 
 | ||||
|         let templates = []; | ||||
| 
 | ||||
|         for(let i = 0; i < templateEntries.length; i++) { | ||||
|             const type = FileHelper.getFragmentType(templateEntries[i]); | ||||
|             const path = templateEntries[i].parentPath + '/' + templateEntries[i].name; | ||||
|             const contents = fs.readFileSync(path, { encoding: 'utf-8' }); | ||||
| 
 | ||||
|             const template = new Fragment(type, contents); | ||||
| 
 | ||||
|             const name = FileHelper.getBaseName(templateEntries[i]); | ||||
| 
 | ||||
|             templates.push({ | ||||
|                 name: name, | ||||
|                 template: template | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         this.templates = templates; | ||||
|         this.initialized = true; | ||||
|     }, | ||||
| 
 | ||||
|     get(key) { | ||||
|         if(!this.initalized) { | ||||
|             this.initialize(); | ||||
|         } | ||||
| 
 | ||||
|         const match = this.templates.find( | ||||
|             (t) => t.name === key | ||||
|         ); | ||||
| 
 | ||||
|         return match?.template; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class TemplateManager { | ||||
|     _templateSuperManager; | ||||
| 
 | ||||
|     constructor() { | ||||
|         this._templateSuperManager = TemplateSuperManager; | ||||
|         if(!TemplateSuperManager.initalized) { | ||||
|             TemplateSuperManager.initialize(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     get(key) { | ||||
|         return this._templateSuperManager.get(key); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								lib/render/token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/render/token.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| export const tokenTypes = { | ||||
|     TEXT: 'text', | ||||
|     VARIABLE: 'variable', | ||||
|     FRAGMENT: 'fragment', | ||||
| }; | ||||
| 
 | ||||
| export class Token { | ||||
|     type; | ||||
|     content; | ||||
| 
 | ||||
|     constructor(type, token) { | ||||
|         this.type = type; | ||||
|         this.content = token; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										61
									
								
								lib/render/tokenizer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								lib/render/tokenizer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| import { Token, tokenTypes } from "./token.js"; | ||||
| 
 | ||||
| const VARIABLE_TOKEN_DEF = { | ||||
|     start: '<*', | ||||
|     end: '*>', | ||||
| }; | ||||
| 
 | ||||
| const FRAGMENT_TOKEN_DEF = { | ||||
|     start: '<{', | ||||
|     end: '}>', | ||||
| }; | ||||
| 
 | ||||
| export class Tokenizer { | ||||
|     constructor() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     tokensByVariable(fragmentText) { | ||||
|         const forward_split_tokens = fragmentText.split(VARIABLE_TOKEN_DEF.start); | ||||
| 
 | ||||
|         let tokens = [ | ||||
|             new Token(tokenTypes.TEXT, forward_split_tokens[0]) | ||||
|         ]; | ||||
| 
 | ||||
|         for(let i = 1; i < forward_split_tokens.length; i++) { | ||||
|             const back_split = forward_split_tokens[i].split(VARIABLE_TOKEN_DEF.end); | ||||
| 
 | ||||
|             if(back_split.length !== 2) { | ||||
|                 console.error(`Difficulty parsing token: ${forward_split_tokens[i]}. Keeping as plain-text`); | ||||
|                 tokens.push(new Token(tokenTypes.TEXT, forward_split_tokens[i])); | ||||
|             } else { | ||||
|                 tokens.push(new Token(tokenTypes.VARIABLE, back_split[0])); | ||||
|                 tokens.push(new Token(tokenTypes.TEXT, back_split[1])); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return tokens; | ||||
|     } | ||||
| 
 | ||||
|     tokensByFragment(fragmentText) { | ||||
|         const forward_split_tokens = fragmentText.split(FRAGMENT_TOKEN_DEF.start); | ||||
| 
 | ||||
|         let tokens = [ | ||||
|             new Token(tokenTypes.TEXT, forward_split_tokens[0]) | ||||
|         ]; | ||||
| 
 | ||||
|         for(let i = 1; i < forward_split_tokens.length; i++) { | ||||
|             const back_split = forward_split_tokens[i].split(FRAGMENT_TOKEN_DEF.end); | ||||
| 
 | ||||
|             if(back_split.length !== 2) { | ||||
|                 console.error(`Difficulty parsing token: ${forward_split_tokens[i]}. Keeping as plain-text`); | ||||
|                 tokens.push(new Token(tokenTypes.TEXT, forward_split_tokens[i])); | ||||
|             } else { | ||||
|                 tokens.push(new Token(tokenTypes.FRAGMENT, back_split[0])); | ||||
|                 tokens.push(new Token(tokenTypes.TEXT, back_split[1])); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return tokens; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										50
									
								
								lib/struct/context.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								lib/struct/context.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| import { Variable } from "./variable.js"; | ||||
| 
 | ||||
| export class Context { | ||||
|     variables; | ||||
| 
 | ||||
|     constructor(vars) { | ||||
|         if(vars) { | ||||
|             this.variables = vars.map(v => new Variable(v.key, v.value)); | ||||
|         } else { | ||||
|             this.variables = []; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     copy() { | ||||
|         return new Context(this.variables); | ||||
|     } | ||||
| 
 | ||||
|     hasVariable(key) { | ||||
|         return this.variables.some( | ||||
|             (v) => v.key === key | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     get(key) { | ||||
|         const match = this.variables.find( | ||||
|             (v) => v.key === key | ||||
|         ); | ||||
| 
 | ||||
|         return match?.value ?? ""; | ||||
|     } | ||||
| 
 | ||||
|     set(key, value) { | ||||
|         const withoutOld = this.variables.filter( | ||||
|             (v) => v.key !== key | ||||
|         ); | ||||
| 
 | ||||
|         this.variables = [ | ||||
|             ...withoutOld, | ||||
|             new Variable(key, value) | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     mergeFrom(other) { | ||||
|         for(let i = 0; i < other.variables.length; i++) { | ||||
|             this.set(other.variables[i].key, other.variables[i].value); | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								lib/struct/fragment.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/struct/fragment.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| import Showdown from 'showdown'; | ||||
| 
 | ||||
| export const fragmentFormats = { | ||||
|     V_HTML: ".vs.html", | ||||
|     V_MARKDOWN: ".vs.md", | ||||
| }; | ||||
| 
 | ||||
| export class Fragment { | ||||
|     format; | ||||
|     sourceContent; | ||||
| 
 | ||||
|     constructor(format, sourceContent) { | ||||
|         this.format = format; | ||||
|         this.sourceContent = sourceContent; | ||||
|     } | ||||
| 
 | ||||
|     toHtml() { | ||||
|         if(this.format === fragmentFormats.V_MARKDOWN) { | ||||
|             const converter = new Showdown.Converter(); | ||||
| 
 | ||||
|             const htmlContent = converter.makeHtml(this.sourceContent); | ||||
| 
 | ||||
|             return new Fragment( | ||||
|                 fragmentFormats.V_HTML, | ||||
|                 htmlContent | ||||
|             ); | ||||
|         } else { | ||||
|             return new Fragment( | ||||
|                 this.format, | ||||
|                 this.sourceContent | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								lib/struct/variable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/struct/variable.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| export class Variable { | ||||
|     key; | ||||
|     value; | ||||
| 
 | ||||
|     constructor(k, v) { | ||||
|         this.key = k; | ||||
|         this.value = v; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								serve.js
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								serve.js
									
									
									
									
									
								
							| @ -3,14 +3,14 @@ import handler from 'serve-handler'; | ||||
| import http from 'http'; | ||||
| import 'dotenv/config'; | ||||
| import { ENV } from './lib/env.js'; | ||||
| import { BasePaths } from './lib/render/base-paths.js'; | ||||
| 
 | ||||
| const isDebugEnabled = ENV.getBoolean('VS_DEBUG'); | ||||
| const hasOverrideDirectory = ENV.getIsSet('VS_OUTPUT_DIR'); | ||||
| const overrideDirectory = ENV.getString('VS_OUTPUT_DIR'); | ||||
| const defaultDirectory = 'build'; | ||||
| 
 | ||||
| const directory = BasePaths.targetRoot(); | ||||
| 
 | ||||
| const options = { | ||||
|     public: hasOverrideDirectory ? overrideDirectory : defaultDirectory, | ||||
|     public: directory, | ||||
|     directoryListing: isDebugEnabled, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user