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 fs from 'fs';
|
||||||
import Showdown from 'showdown';
|
import { BasePaths } from './lib/render/base-paths.js';
|
||||||
import 'dotenv/config';
|
import { Renderer } from './lib/render/renderer.js';
|
||||||
import { ENV } from './lib/env.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();
|
// Delete prior output, if exists.
|
||||||
|
if(fs.existsSync(BasePaths.targetRoot())) {
|
||||||
let inputDir = 'content/';
|
fs.rmSync(BasePaths.targetRoot(), { recursive: true, force: true });
|
||||||
if(ENV.getIsSet('VS_INPUT_DIR')) {
|
|
||||||
inputDir = ENV.getString('VS_INPUT_DIR');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outputDir = 'build/';
|
// fs.mkdirSync(BasePaths.targetRoot());
|
||||||
if(ENV.getIsSet('VS_OUTPUT_DIR')) {
|
|
||||||
outputDir = ENV.getString('VS_OUTPUT_DIR');
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileEntities = fs.readdirSync(inputDir, {recursive: true, withFileTypes: true, encoding: 'utf8'});
|
const startPath = '';
|
||||||
|
const rootContext = SettingsReader.readDirectorySettings(`${BasePaths.contentRoot()}/${startPath}`);
|
||||||
|
|
||||||
fileEntities.forEach((fileEnt) => {
|
const renderer = new Renderer(startPath, rootContext);
|
||||||
const filenameTokens = fileEnt.name.split('.');
|
|
||||||
const extension = filenameTokens[filenameTokens.length - 1];
|
|
||||||
|
|
||||||
if(extension === 'md') {
|
renderer.renderAll();
|
||||||
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}`);
|
|
||||||
}
|
|
||||||
});
|
|
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 http from 'http';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { ENV } from './lib/env.js';
|
import { ENV } from './lib/env.js';
|
||||||
|
import { BasePaths } from './lib/render/base-paths.js';
|
||||||
|
|
||||||
const isDebugEnabled = ENV.getBoolean('VS_DEBUG');
|
const isDebugEnabled = ENV.getBoolean('VS_DEBUG');
|
||||||
const hasOverrideDirectory = ENV.getIsSet('VS_OUTPUT_DIR');
|
|
||||||
const overrideDirectory = ENV.getString('VS_OUTPUT_DIR');
|
const directory = BasePaths.targetRoot();
|
||||||
const defaultDirectory = 'build';
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
public: hasOverrideDirectory ? overrideDirectory : defaultDirectory,
|
public: directory,
|
||||||
directoryListing: isDebugEnabled,
|
directoryListing: isDebugEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user