From ec9590ba124762946e6933c0574e72162d88289e Mon Sep 17 00:00:00 2001 From: nicco Date: Wed, 7 Feb 2018 11:46:19 +0100 Subject: [PATCH] Dist --- dist/actions.js | 129 +++++++++++++++++++++++++++++++++++++++++++++++ dist/blitz.js | 47 +++++++++++++++++ dist/compiler.js | 58 +++++++++++++++++++++ dist/options.js | 38 ++++++++++++++ dist/parser.js | 26 ++++++++++ dist/util.js | 74 +++++++++++++++++++++++++++ 6 files changed, 372 insertions(+) create mode 100644 dist/actions.js create mode 100644 dist/blitz.js create mode 100644 dist/compiler.js create mode 100644 dist/options.js create mode 100644 dist/parser.js create mode 100644 dist/util.js diff --git a/dist/actions.js b/dist/actions.js new file mode 100644 index 0000000..1b1b0de --- /dev/null +++ b/dist/actions.js @@ -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 + }; +}; diff --git a/dist/blitz.js b/dist/blitz.js new file mode 100644 index 0000000..cea5738 --- /dev/null +++ b/dist/blitz.js @@ -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; diff --git a/dist/compiler.js b/dist/compiler.js new file mode 100644 index 0000000..dbe6265 --- /dev/null +++ b/dist/compiler.js @@ -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; diff --git a/dist/options.js b/dist/options.js new file mode 100644 index 0000000..2ce0c76 --- /dev/null +++ b/dist/options.js @@ -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|\\.)*?', +}; diff --git a/dist/parser.js b/dist/parser.js new file mode 100644 index 0000000..6987885 --- /dev/null +++ b/dist/parser.js @@ -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); + } +} diff --git a/dist/util.js b/dist/util.js new file mode 100644 index 0000000..b408b09 --- /dev/null +++ b/dist/util.js @@ -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;