This commit is contained in:
nicco 2018-02-07 11:46:19 +01:00
parent d5850aca68
commit ec9590ba12
6 changed files with 372 additions and 0 deletions

129
dist/actions.js vendored Normal file
View File

@ -0,0 +1,129 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const compiler_1 = require("./compiler");
const options_1 = require("./options");
const util_1 = require("./util");
const path_1 = require("path");
const parser_1 = require("./parser");
exports.comment = html => {
const tag = options_1.re.comment + options_1.re.ending;
const end = html.indexOf(tag);
if (end === -1)
throw new Error(options_1.error.parse.comment_not_closed);
return {
parts: [],
length: end + tag.length
};
};
exports.logic = html => {
const rexp = {
start: new RegExp(`${options_1.re.begin}\\${options_1.re.if} *\\${options_1.re.if_else}?${options_1.re.valid_variable} *${options_1.re.ending}`, 'g'),
else: new RegExp(`${options_1.re.begin} *\\${options_1.re.if_else} *${options_1.re.ending}`, 'g'),
end: RegExp(`${options_1.re.begin} *\\${options_1.re.closing_tag} *\\${options_1.re.if} *${options_1.re.ending}`, 'g'),
};
const current = {
found: rexp.start.exec(html),
variable: '',
inverted: false,
};
if (current.found === null || current.found.index !== 0)
throw new Error(options_1.error.parse.default);
current.variable = current.found[0].slice(options_1.re.begin.length + options_1.re.if.length, -options_1.re.ending.length).trim();
current.inverted = current.variable[0] === options_1.re.if_invert;
if (current.inverted)
current.variable = current.variable.slice(options_1.re.if_invert.length);
let next;
do {
next = {
start: rexp.start.exec(html),
else: rexp.else.exec(html),
end: rexp.end.exec(html),
};
if (next.end === null)
throw new Error(options_1.error.parse.default);
} while (next.start !== null && next.start.index < next.end.index);
const body = {
if: html.substring(current.found[0].length, next.end.index),
else: ''
};
return {
parts: [(data) => {
const ret = util_1.getFromObject(data, current.variable);
let isTrue = ret !== undefined && ret !== false && ret !== null && ret !== '';
if (current.inverted)
isTrue = !isTrue;
if (isTrue)
return compiler_1.compileBlock(body.if).parts;
else
return compiler_1.compileBlock(body.else).parts;
}],
length: next.end.index + next.end[0].length
};
};
exports.importer = html => {
const end = html.indexOf(options_1.re.ending);
if (end === -1)
throw new Error(options_1.error.parse.include_not_closed);
const template_name = html.substring(options_1.re.begin.length + options_1.re.incude.length, end).trim();
const file_name = path_1.join(options_1.options.template_dir, `${template_name}.${options_1.options.template_ext}`);
const file = util_1.readFileSync(file_name);
return {
parts: compiler_1.compileBlock(file).parts,
length: end + options_1.re.ending.length
};
};
exports.variables = html => {
const end = html.indexOf(options_1.re.ending);
if (end === -1)
throw new Error(options_1.error.parse.variable_not_closed);
const variable_name = html.substring(options_1.re.begin.length, end).trim();
return {
parts: [(data) => {
const output = util_1.getFromObject(data, variable_name);
switch (typeof output) {
case 'object':
return JSON.stringify(output);
default:
return output;
}
}],
length: end + options_1.re.ending.length
};
};
exports.loop = html => {
const rexp = {
start: new RegExp(`${options_1.re.begin}\\${options_1.re.for} *${options_1.re.valid_variable} *${options_1.re.for_in} *${options_1.re.valid_variable} *${options_1.re.ending}`, 'g'),
end: RegExp(`${options_1.re.begin} *\\${options_1.re.closing_tag} *\\${options_1.re.for} *${options_1.re.ending}`, 'g'),
};
const current = {
found: rexp.start.exec(html),
variable: '',
arr: '',
};
if (current.found === null || current.found.index !== 0)
throw new Error(options_1.error.parse.default);
const statement = current.found[0].slice(options_1.re.begin.length + options_1.re.if.length, -options_1.re.ending.length).trim().split(options_1.re.for_in);
current.variable = statement[0].trim();
current.arr = statement[1].trim();
let next;
do {
next = {
start: rexp.start.exec(html),
end: rexp.end.exec(html),
};
if (next.end === null)
throw new Error(options_1.error.parse.default);
} while (next.start !== null && next.start.index < next.end.index);
html = html.substring(current.found[0].length, next.end.index);
return {
parts: [(data) => {
let ret = '';
for (const variable of util_1.getFromObject(data, current.arr)) {
const newData = Object.assign({ [current.variable]: variable }, data);
ret += parser_1.computeParts(compiler_1.compileBlock(html).parts, newData);
}
return ret;
}],
length: next.end.index + next.end[0].length
};
};

47
dist/blitz.js vendored Normal file
View File

@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const util = require("./util");
const parser = require("./parser");
const compiler = require("./compiler");
const options_1 = require("./options");
const cache = new Map();
function compile(html) {
return {
template: compiler.process(html),
time: Date.now()
};
}
function renderFile(file, data, callback) {
util.readFile(file).then(html => {
util.checksum(html, true).then(hash => {
if (options_1.options.caching && !cache.get(html))
cache.set(html, compile(html));
const compiled = cache.get(html);
if (compiled)
callback(null, parser.computeParts(compiled.template, data));
else
callback('Error: Chache not found', '');
});
});
}
exports.renderFile = renderFile;
async function render(template_name, data) {
const template_path = path.join(options_1.options.template_dir, `${template_name}.${options_1.options.template_ext}`);
if (options_1.options.caching && !cache.get(template_name)) {
const html = await util.readFile(template_path);
if (html !== undefined)
cache.set(template_name, compile(html));
else {
'No file found'.log();
return '';
}
}
const compiled = cache.get(template_name);
if (compiled)
return parser.computeParts(compiled.template, data);
else
return '';
}
exports.render = render;
exports._express = renderFile;

58
dist/compiler.js vendored Normal file
View File

@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const options_1 = require("./options");
const actions = require("./actions");
const rexp = Object.freeze({
begin: new RegExp(options_1.re.begin, 'g'),
end: new RegExp(options_1.re.ending, 'g'),
});
exports.compileBlock = part => {
let next;
const getNext = (s) => Object.freeze({
start: s.search(rexp.begin),
end: s.search(rexp.end),
});
let ret = {
parts: [],
length: NaN
};
function addToRet(item) {
ret.parts = ret.parts.concat(item);
}
next = getNext(part);
while (next.start !== -1) {
if (next.start === null || next.end === null)
throw new Error(options_1.error.parse.default);
addToRet(part.substr(0, next.start));
part = part.slice(next.start);
let func;
switch (part[options_1.re.begin.length]) {
case options_1.re.comment:
func = actions.comment;
break;
case options_1.re.if:
func = actions.logic;
break;
case options_1.re.for:
func = actions.loop;
break;
case options_1.re.incude:
func = actions.importer;
break;
default:
func = actions.variables;
break;
}
const result = func(part);
addToRet(result.parts);
part = part.slice(result.length);
next = getNext(part);
}
addToRet(part);
return ret;
};
function process(html, options = {}) {
const parts = exports.compileBlock(html).parts;
return parts;
}
exports.process = process;

38
dist/options.js vendored Normal file
View File

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isRender(obj) {
return typeof obj === 'string';
}
exports.isRender = isRender;
exports.error = {
parse: {
default: 'Parse Error.',
import_recursion: 'Maximal recusion achieved in import module',
not_supported: 'Not supported yet',
comment_not_closed: 'Comment was not closed properly',
variable_not_closed: 'Variable was not closed properly',
include_not_closed: 'Include not closed',
},
};
exports.options = {
encoding: 'utf-8',
caching: true,
template_dir: './views',
template_ext: 'html',
compiled_dir: './cache',
compiled_ext: 'bjs',
max_recursion: 100,
};
exports.re = {
begin: '{{',
ending: '}}',
comment: '#',
incude: '>',
if: '?',
if_else: '!',
if_invert: '!',
for: '*',
for_in: 'in',
closing_tag: '/',
valid_variable: '[A-z](\\w|\\.)*?',
};

26
dist/parser.js vendored Normal file
View File

@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const options_1 = require("./options");
function computeParts(parts, data = {}) {
if (parts.length === 0)
return '';
return computePart(parts[0], data) + computeParts(parts.slice(1), data);
}
exports.computeParts = computeParts;
function computePart(part, data = {}) {
if (options_1.isRender(part))
return part;
else
return computePartFunction(part, data);
}
function computePartFunction(func, data = {}) {
if (options_1.isRender(func))
return func;
else {
const ret = func(data);
if (options_1.isRender(ret))
return ret;
else
return computeParts(ret, data);
}
}

74
dist/util.js vendored Normal file
View File

@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const crypto = require("crypto");
String.prototype.log = function () {
console.log(this);
};
function readFile(url) {
return new Promise(res => {
fs.readFile(url, (err, data) => {
if (err)
throw new Error(`No such file: ${url}`);
else
res(data.toString());
});
});
}
exports.readFile = readFile;
function readFileSync(url) {
return fs.readFileSync(url).toString();
}
exports.readFileSync = readFileSync;
function writeFile(url, data) {
return new Promise(res => {
fs.writeFile(url, data, err => {
if (err)
res(false);
res(true);
});
});
}
exports.writeFile = writeFile;
function writeFileSync(url, data) {
fs.writeFileSync(url, data);
}
exports.writeFileSync = writeFileSync;
function fileExists(url) {
return new Promise(res => {
fs.exists(url, _ => {
res(_);
});
});
}
exports.fileExists = fileExists;
function checksum(url, plain = false, alg = 'sha1') {
return new Promise(res => {
const hash = crypto.createHash(alg);
if (plain) {
res(hash.update(url).digest('hex'));
}
else {
const stream = fs.createReadStream(url);
stream.on('data', data => hash.update(data, 'utf8'));
stream.on('end', _ => { res(hash.digest('hex')); });
}
});
}
exports.checksum = checksum;
function replaceBetween(start, end, str, replace) {
return str.substring(0, start) + replace + str.substring(end);
}
exports.replaceBetween = replaceBetween;
function getFromObject(data, name) {
name = name.trim();
const valid = /^[A-z]\w*(\.[A-z]\w*|\[\d+\]|\[('|")\w+\2\]|\[[A-z]\w*\])*$/.test(name);
if (!valid)
return '';
name = name.replace(/('|")/g, '');
name = name.replace(/\[(\w+)\]/g, '.$1');
for (const i of name.split('.'))
data = data[i];
return data;
}
exports.getFromObject = getFromObject;