Rewrite in-progress #1
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