mirror of
https://github.com/cupcakearmy/cometa.git
synced 2025-03-12 14:27:28 +00:00
First Commit
This commit is contained in:
parent
53574b13a3
commit
f234cd8c18
99
app.js
Normal file
99
app.js
Normal file
@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [0, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
exports.__esModule = true;
|
||||
var fs = require("fs");
|
||||
var S = {
|
||||
re: {
|
||||
begin: /{{/,
|
||||
end: /}}/
|
||||
},
|
||||
opt: {
|
||||
encoding: 'utf-8'
|
||||
}
|
||||
};
|
||||
var LOG_TYPE;
|
||||
(function (LOG_TYPE) {
|
||||
LOG_TYPE[LOG_TYPE["Info"] = 0] = "Info";
|
||||
LOG_TYPE[LOG_TYPE["Warning"] = 1] = "Warning";
|
||||
LOG_TYPE[LOG_TYPE["Error"] = 2] = "Error";
|
||||
})(LOG_TYPE || (LOG_TYPE = {}));
|
||||
function logger(type, msg) {
|
||||
if (typeof msg === 'object')
|
||||
msg = JSON.stringify(msg);
|
||||
var typeString = '';
|
||||
switch (type) {
|
||||
case LOG_TYPE.Info:
|
||||
typeString = 'Info';
|
||||
break;
|
||||
case LOG_TYPE.Warning:
|
||||
typeString = 'Warning';
|
||||
break;
|
||||
case LOG_TYPE.Error:
|
||||
typeString = 'Error';
|
||||
break;
|
||||
}
|
||||
console.log(typeString + ":", msg);
|
||||
}
|
||||
function readFile(url) {
|
||||
return new Promise(function (res, rej) {
|
||||
fs.readFile(url, function (err, data) {
|
||||
if (err)
|
||||
res();
|
||||
else
|
||||
res(data.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
function test() {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var html;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, readFile('./test.html')];
|
||||
case 1:
|
||||
html = _a.sent();
|
||||
if (html === undefined) {
|
||||
logger(LOG_TYPE.Error, 'No file found');
|
||||
return [2 /*return*/];
|
||||
}
|
||||
logger(LOG_TYPE.Info, html);
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
test();
|
1
app.js.map
Normal file
1
app.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";;AAAA,yBAAwB;AAGxB,MAAM,CAAC,GAAW;IACjB,EAAE,EAAE;QACH,KAAK,EAAE,IAAI;QACX,GAAG,EAAE,IAAI;KACT;IACD,GAAG,EAAE;QACJ,QAAQ,EAAE,OAAO;KACjB;CACD,CAAA;AAED,kBAAkB,GAAW;IAC5B,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC/B,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YAC9B,EAAE,CAAC,CAAC,GAAG,CAAC;gBACP,GAAG,CAAC,GAAG,CAAC,CAAA;YACT,IAAI;gBACH,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACtB,CAAC,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;AACH,CAAC;AAED,KAAK;IACJ,OAAO,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAA;AAC3C,CAAC;AACD,IAAI,EAAE,CAAA"}
|
83
app.ts
Normal file
83
app.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as util from './util'
|
||||
import * as parser from './parser'
|
||||
import { Render, LOG_TYPE, options } from './options'
|
||||
import { replaceVars, addParts } from './parser';
|
||||
|
||||
|
||||
const cache: Map<string, Render> = new Map()
|
||||
|
||||
function logger(type: LOG_TYPE, msg: object | string): void {
|
||||
if (typeof msg === 'object')
|
||||
msg = JSON.stringify(msg)
|
||||
|
||||
let typeString: string = ''
|
||||
|
||||
switch (type) {
|
||||
case LOG_TYPE.Info:
|
||||
typeString = 'Info'
|
||||
break
|
||||
case LOG_TYPE.Warning:
|
||||
typeString = 'Warning'
|
||||
break
|
||||
case LOG_TYPE.Error:
|
||||
typeString = 'Error'
|
||||
break
|
||||
}
|
||||
|
||||
console.log(`${typeString}:`, msg)
|
||||
}
|
||||
|
||||
async function compile(html: string): Promise<Render> {
|
||||
html = parser.removeComments(html)
|
||||
|
||||
const compiled = replaceVars(html)
|
||||
|
||||
return {
|
||||
do(data) {
|
||||
return addParts(data, compiled)
|
||||
},
|
||||
hash: await util.checksum(html, true),
|
||||
time: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
async function render(template_name: string, data?: any): Promise<string | undefined> {
|
||||
const compiled_path = path.join(options.compiled_dir, template_name) + `.${options.compiled_dir}`
|
||||
|
||||
if (!options.caching || !await util.exists(compiled_path)) {
|
||||
const template_path = path.join(options.template_dir, template_name) + `.${options.template_ext}`
|
||||
|
||||
const html = await util.readFile(template_path)
|
||||
if (html === undefined) {
|
||||
logger(LOG_TYPE.Error, 'No file found')
|
||||
return
|
||||
}
|
||||
else
|
||||
cache.set(template_name, await compile(html))
|
||||
}
|
||||
|
||||
const render = cache.get(template_name)
|
||||
if (render) {
|
||||
return render.do(data)
|
||||
}
|
||||
else
|
||||
return
|
||||
}
|
||||
|
||||
async function go() {
|
||||
console.log(await render('test', {
|
||||
title: 'title',
|
||||
body: {
|
||||
p: [
|
||||
'omg',
|
||||
{
|
||||
check: 'let go'
|
||||
}
|
||||
]
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
go()
|
59
options.ts
Normal file
59
options.ts
Normal file
@ -0,0 +1,59 @@
|
||||
export const enum LOG_TYPE {
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
export interface Render {
|
||||
do: ((data: any) => string)
|
||||
hash: string
|
||||
time: number
|
||||
}
|
||||
|
||||
export interface Error {
|
||||
parse: string
|
||||
}
|
||||
|
||||
export const error: Error = {
|
||||
parse: 'Parse Error.'
|
||||
}
|
||||
|
||||
interface Options {
|
||||
encoding: string
|
||||
caching: boolean
|
||||
template_dir: string
|
||||
template_ext: string
|
||||
compiled_dir: string
|
||||
compiled_ext: string
|
||||
}
|
||||
|
||||
export const options: Options = {
|
||||
encoding: 'utf-8',
|
||||
caching: true,
|
||||
template_dir: './views',
|
||||
template_ext: 'html',
|
||||
compiled_dir: './views',
|
||||
compiled_ext: 'htmlbin',
|
||||
}
|
||||
|
||||
interface Expressions {
|
||||
begin: string
|
||||
ending: string
|
||||
comment: string
|
||||
incude: string
|
||||
if: string
|
||||
if_else: string
|
||||
for: string
|
||||
for_in: string
|
||||
}
|
||||
|
||||
export const re: Expressions = {
|
||||
begin: '{{',
|
||||
ending: '}}',
|
||||
comment: '#',
|
||||
incude: '>',
|
||||
if: '?',
|
||||
if_else: '!',
|
||||
for: '*',
|
||||
for_in: 'in',
|
||||
}
|
14
package-lock.json
generated
Normal file
14
package-lock.json
generated
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "template",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.3.0.tgz",
|
||||
"integrity": "sha512-wNBfvNjzsJl4tswIZKXCFQY0lss9nKUyJnG6T94X/eqjRgI2jHZ4evdjhQYBSan/vGtF6XVXPApOmNH2rf0KKw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
18
package.json
Normal file
18
package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "template",
|
||||
"version": "1.0.0",
|
||||
"description": "Templating Engine",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"Templating",
|
||||
"Engine"
|
||||
],
|
||||
"author": "Niccolo Borgioli",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^9.3.0"
|
||||
}
|
||||
}
|
65
parser.ts
Normal file
65
parser.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { re, error } from "./options";
|
||||
|
||||
function getFromData(data: any, name: string): string {
|
||||
let ret: any = data
|
||||
for (let i of name.trim().split('.')) {
|
||||
const array = i.match(/\[\w+\]/)
|
||||
if (array === null)
|
||||
ret = ret[i]
|
||||
else {
|
||||
const index: string = array[0].slice(1, -1)
|
||||
const num_index: number = parseInt(index[0])
|
||||
ret = ret[i.substr(0, array.index)]
|
||||
ret = num_index === NaN ? ret[index] : ret[num_index]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function addParts(data: any, parts: (((data: any) => string) | string)[]): string {
|
||||
if (parts.length === 0) return ''
|
||||
|
||||
const part: string | ((data: any) => string) = parts[0]
|
||||
|
||||
return (typeof part === 'string' ? part : part(data)) + addParts(data, parts.slice(1))
|
||||
}
|
||||
|
||||
export function removeComments(html: string): string {
|
||||
return html.replace(
|
||||
new RegExp(`${re.begin}${re.comment}(.|\n)*?${re.comment}${re.ending}`, 'g'), '')
|
||||
}
|
||||
|
||||
export function replaceVars(html: string): (((data: any) => string) | string)[] {
|
||||
|
||||
const ret: (((data: any) => string) | string)[] = []
|
||||
|
||||
let i = html.match(/{{/) // Starting char
|
||||
while (i !== null) {
|
||||
if (i.index === undefined)
|
||||
throw new Error(error.parse)
|
||||
|
||||
// Push text before
|
||||
ret.push(html.substr(0, i.index))
|
||||
|
||||
html = html.slice(i.index + i[0].length)
|
||||
|
||||
// Get closing tag
|
||||
const j = html.match(/}}/)
|
||||
if (j === null || j.index === undefined)
|
||||
throw new Error(error.parse)
|
||||
|
||||
const sub: string = html.substring(0, j.index)
|
||||
|
||||
ret.push((data) => {
|
||||
return getFromData(data, sub)
|
||||
})
|
||||
|
||||
html = html.slice(j.index + j[0].length)
|
||||
|
||||
i = html.match(/{{/) // Starting char
|
||||
}
|
||||
|
||||
ret.push(html)
|
||||
|
||||
return ret
|
||||
}
|
53
tsconfig.json
Normal file
53
tsconfig.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation: */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
"removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": false, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
},
|
||||
"exclude": [
|
||||
"./node_modules"
|
||||
]
|
||||
}
|
36
util.ts
Normal file
36
util.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import * as fs from 'fs'
|
||||
import * as crypto from 'crypto'
|
||||
|
||||
export function readFile(url: string): Promise<string> {
|
||||
return new Promise(res => {
|
||||
fs.readFile(url, (err, data) => {
|
||||
if (err)
|
||||
throw new Error(`No such file: ${url}`)
|
||||
else
|
||||
res(data.toString())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function exists(url: string): Promise<boolean> {
|
||||
return new Promise(res => {
|
||||
fs.exists(url, _ => {
|
||||
res(_)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function checksum(url: string, plain = false, alg = 'sha1'): Promise<string> {
|
||||
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')) })
|
||||
}
|
||||
})
|
||||
}
|
17
views/test.html
Normal file
17
views/test.html
Normal file
@ -0,0 +1,17 @@
|
||||
<doctype html>
|
||||
|
||||
<head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
{{# asdfkjashdlkfjashdlkj #}}
|
||||
<section>
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ body.p[1].check }}</p>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</doctype>
|
Loading…
x
Reference in New Issue
Block a user