Compare commits
No commits in common. "325b747bd841b1e80a12791e52e57aade02d50ca" and "c0dc360579715b6c08389e5caa8693897318d6d1" have entirely different histories.
325b747bd8
...
c0dc360579
52
build.js
52
build.js
@ -1,20 +1,46 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { BasePaths } from './lib/render/base-paths.js';
|
import Showdown from 'showdown';
|
||||||
import { Renderer } from './lib/render/renderer.js';
|
import 'dotenv/config';
|
||||||
import { FileHelper } from './lib/render/file-helper.js';
|
import { ENV } from './lib/env.js';
|
||||||
import { SettingsReader } from './lib/render/settings-reader.js';
|
|
||||||
import { Context } from './lib/struct/context.js';
|
|
||||||
|
|
||||||
// Delete prior output, if exists.
|
const converter = new Showdown.Converter();
|
||||||
if(fs.existsSync(BasePaths.targetRoot())) {
|
|
||||||
fs.rmSync(BasePaths.targetRoot(), { recursive: true, force: true });
|
let inputDir = 'content/';
|
||||||
|
if(ENV.getIsSet('VS_INPUT_DIR')) {
|
||||||
|
inputDir = ENV.getString('VS_INPUT_DIR');
|
||||||
}
|
}
|
||||||
|
|
||||||
// fs.mkdirSync(BasePaths.targetRoot());
|
let outputDir = 'build/';
|
||||||
|
if(ENV.getIsSet('VS_OUTPUT_DIR')) {
|
||||||
|
outputDir = ENV.getString('VS_OUTPUT_DIR');
|
||||||
|
}
|
||||||
|
|
||||||
const startPath = '';
|
const fileEntities = fs.readdirSync(inputDir, {recursive: true, withFileTypes: true, encoding: 'utf8'});
|
||||||
const rootContext = SettingsReader.readDirectorySettings(`${BasePaths.contentRoot()}/${startPath}`);
|
|
||||||
|
|
||||||
const renderer = new Renderer(startPath, rootContext);
|
fileEntities.forEach((fileEnt) => {
|
||||||
|
const filenameTokens = fileEnt.name.split('.');
|
||||||
|
const extension = filenameTokens[filenameTokens.length - 1];
|
||||||
|
|
||||||
renderer.renderAll();
|
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}`);
|
||||||
|
}
|
||||||
|
});
|
@ -1 +0,0 @@
|
|||||||
test variable = <* test_var *>
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"test_var": "Outer settings",
|
|
||||||
"template": "base-template"
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"test_var": "Inner setting",
|
|
||||||
"template": ""
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# Hello world from another directory!
|
|
||||||
|
|
||||||
<{ var-test }>
|
|
@ -1,3 +0,0 @@
|
|||||||
# Hello world!
|
|
||||||
|
|
||||||
<{ var-test }>
|
|
@ -1,6 +0,0 @@
|
|||||||
<div>
|
|
||||||
<h3>
|
|
||||||
Outer wrapper!
|
|
||||||
</h3>
|
|
||||||
<{ content }>
|
|
||||||
</div>
|
|
1
content/test.md
Normal file
1
content/test.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Here's a test!
|
@ -1,2 +0,0 @@
|
|||||||
export const DEFAULT_SOURCE_BASE = './content';
|
|
||||||
export const DEFUALT_TARGET_BASE = './build';
|
|
@ -1,24 +0,0 @@
|
|||||||
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`;
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,64 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
export const tokenTypes = {
|
|
||||||
TEXT: 'text',
|
|
||||||
VARIABLE: 'variable',
|
|
||||||
FRAGMENT: 'fragment',
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Token {
|
|
||||||
type;
|
|
||||||
content;
|
|
||||||
|
|
||||||
constructor(type, token) {
|
|
||||||
this.type = type;
|
|
||||||
this.content = token;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
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 directory = BasePaths.targetRoot();
|
const overrideDirectory = ENV.getString('VS_OUTPUT_DIR');
|
||||||
|
const defaultDirectory = 'build';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
public: directory,
|
public: hasOverrideDirectory ? overrideDirectory : defaultDirectory,
|
||||||
directoryListing: isDebugEnabled,
|
directoryListing: isDebugEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user