mirror of
https://github.com/cupcakearmy/cometa.git
synced 2025-03-12 22:37:28 +00:00
V0.1 Recursive if statements
This commit is contained in:
parent
1bf9876f7e
commit
47c89612b9
113
actions.ts
Normal file
113
actions.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { compileBlock } from './reader'
|
||||||
|
import { ActionFunction, re, error, options, Part } from './options'
|
||||||
|
import { getFromObject, readFileSync } from './util'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
export const comment: ActionFunction = html => {
|
||||||
|
|
||||||
|
const tag = re.comment + re.ending
|
||||||
|
const end = html.indexOf(tag)
|
||||||
|
|
||||||
|
if (end === -1) throw new Error(error.parse.comment_not_closed)
|
||||||
|
|
||||||
|
return {
|
||||||
|
parts: [],
|
||||||
|
length: end + tag.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logic: ActionFunction = html => {
|
||||||
|
|
||||||
|
const rexp = {
|
||||||
|
start: new RegExp(`${re.begin}\\${re.if} *\\${re.if_else}?[A-z]\\w*? *${re.ending}`, 'g'),
|
||||||
|
else: new RegExp(`${re.begin} *\\${re.if_else} *${re.ending}`, 'g'),
|
||||||
|
end: RegExp(`${re.begin} *\\${re.closing_tag} *\\${re.if} *${re.ending}`, 'g'),
|
||||||
|
}
|
||||||
|
|
||||||
|
// First occurence of the if statement
|
||||||
|
const current = {
|
||||||
|
found: rexp.start.exec(html),
|
||||||
|
variable: '',
|
||||||
|
inverted: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no starting tag for an if statement return an error
|
||||||
|
if (current.found === null || current.found.index !== 0)
|
||||||
|
throw new Error(error.parse.default)
|
||||||
|
|
||||||
|
// Extract variable name from the if statemtent
|
||||||
|
current.variable = current.found[0].slice(re.begin.length + re.if.length, -re.ending.length).trim()
|
||||||
|
current.inverted = current.variable[0] === re.if_invert
|
||||||
|
if (current.inverted)
|
||||||
|
current.variable = current.variable.slice(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(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: any) => {
|
||||||
|
const ret: any = getFromObject(data, current.variable)
|
||||||
|
let isTrue: boolean = ret !== undefined && ret !== false && ret !== null && ret !== ''
|
||||||
|
if (current.inverted) isTrue = !isTrue
|
||||||
|
|
||||||
|
if (isTrue)
|
||||||
|
return compileBlock(body.if).parts
|
||||||
|
else
|
||||||
|
return compileBlock(body.else).parts
|
||||||
|
}],
|
||||||
|
length: next.end.index + next.end[0].length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const importer: ActionFunction = html => {
|
||||||
|
|
||||||
|
const end = html.indexOf(re.ending)
|
||||||
|
|
||||||
|
if (end === -1) throw new Error(error.parse.include_not_closed)
|
||||||
|
|
||||||
|
const template_name: string = html.substring(re.begin.length + re.incude.length, end).trim()
|
||||||
|
const file_name: string = join(options.template_dir, `${template_name}.${options.template_ext}`)
|
||||||
|
const file: Part = readFileSync(file_name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
parts: compileBlock(file).parts,
|
||||||
|
length: end + re.ending.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const variables: ActionFunction = html => {
|
||||||
|
|
||||||
|
const end = html.indexOf(re.ending)
|
||||||
|
|
||||||
|
if (end === -1) throw new Error(error.parse.variable_not_closed)
|
||||||
|
|
||||||
|
const variable_name = html.substring(re.begin.length, end).trim()
|
||||||
|
|
||||||
|
return {
|
||||||
|
parts: [(data: any) => String(getFromObject(data, variable_name))],
|
||||||
|
length: end + re.ending.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loop: ActionFunction = html => {
|
||||||
|
return {
|
||||||
|
parts: [],
|
||||||
|
length: html.length
|
||||||
|
}
|
||||||
|
}
|
57
app.ts
57
app.ts
@ -1,11 +1,10 @@
|
|||||||
import * as fs from 'fs'
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as util from './util'
|
import * as util from './util'
|
||||||
import * as parser from './parser'
|
import * as parser from './parser'
|
||||||
import { Render, LOG_TYPE, options } from './options'
|
import * as compiler from './compiler'
|
||||||
import { replaceVars, addParts } from './parser';
|
import { Compiled, LOG_TYPE, options } from './options'
|
||||||
|
|
||||||
const cache: Map<string, Render> = new Map()
|
const cache: Map<string, Compiled> = new Map()
|
||||||
|
|
||||||
function logger(type: LOG_TYPE, msg: object | string): void {
|
function logger(type: LOG_TYPE, msg: object | string): void {
|
||||||
if (typeof msg === 'object')
|
if (typeof msg === 'object')
|
||||||
@ -28,47 +27,41 @@ function logger(type: LOG_TYPE, msg: object | string): void {
|
|||||||
console.log(`${typeString}:`, msg)
|
console.log(`${typeString}:`, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compile(html: string): Promise<Render> {
|
async function compile(html: string): Promise<Compiled> {
|
||||||
html = await parser.insertImports(html)
|
|
||||||
html = parser.removeComments(html)
|
|
||||||
|
|
||||||
const compiled = replaceVars(html)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
do(data) {
|
template: compiler.process(html),
|
||||||
return addParts(data, compiled)
|
|
||||||
},
|
|
||||||
hash: await util.checksum(html, true),
|
hash: await util.checksum(html, true),
|
||||||
time: Date.now()
|
time: Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function render(template_name: string, data?: any): Promise<string | undefined> {
|
async function render(template_name: string, data?: any): Promise<string> {
|
||||||
const compiled_path = path.join(options.compiled_dir, `${template_name}.${options.compiled_ext}`)
|
// const compiled_path = path.join(options.compiled_dir, `${template_name}.${options.compiled_ext}`)
|
||||||
|
const template_path = path.join(options.template_dir, `${template_name}.${options.template_ext}`)
|
||||||
if (!options.caching || !await util.exists(compiled_path)) {
|
|
||||||
const template_path = path.join(options.template_dir, `${template_name}.${options.template_ext}`)
|
|
||||||
|
|
||||||
|
// Compile Template if is not in cache
|
||||||
|
if (options.caching && !cache.get(template_name)) {
|
||||||
const html = await util.readFile(template_path)
|
const html = await util.readFile(template_path)
|
||||||
if (html === undefined) {
|
if (html !== undefined)
|
||||||
logger(LOG_TYPE.Error, 'No file found')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cache.set(template_name, await compile(html))
|
cache.set(template_name, await compile(html))
|
||||||
|
else {
|
||||||
|
logger(LOG_TYPE.Error, 'No file found')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = cache.get(template_name)
|
const compiled = cache.get(template_name)
|
||||||
if (render) {
|
if (compiled)
|
||||||
return render.do(data)
|
return parser.computeParts(compiled.template, data)
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function go() {
|
async function go() {
|
||||||
console.log(await render('test', {
|
const ret = await render('new', {
|
||||||
title: 'title',
|
test: true,
|
||||||
|
testa: true,
|
||||||
|
title: 'test',
|
||||||
body: {
|
body: {
|
||||||
p: [
|
p: [
|
||||||
'omg',
|
'omg',
|
||||||
@ -77,7 +70,9 @@ async function go() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
ret.log()
|
||||||
}
|
}
|
||||||
|
|
||||||
go()
|
go()
|
75
compiler.ts
Normal file
75
compiler.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { Part, re, error, ActionFunction, ActionReturn } from './options'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
const rexp = Object.freeze({
|
||||||
|
begin: new RegExp(re.begin, 'g'),
|
||||||
|
end: new RegExp(re.ending, 'g'),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const compileBlock: ActionFunction = part => {
|
||||||
|
interface Next {
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
|
let next: Next
|
||||||
|
const getNext = (s: string): Next => Object.freeze({
|
||||||
|
start: s.search(rexp.begin),
|
||||||
|
end: s.search(rexp.end),
|
||||||
|
})
|
||||||
|
|
||||||
|
let ret: ActionReturn = {
|
||||||
|
parts: [],
|
||||||
|
length: NaN
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToRet(item: any) {
|
||||||
|
ret.parts = ret.parts.concat(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
next = getNext(part)
|
||||||
|
while (next.start !== -1) {
|
||||||
|
|
||||||
|
if (next.start === null || next.end === null)
|
||||||
|
throw new Error(error.parse.default)
|
||||||
|
|
||||||
|
addToRet(part.substr(0, next.start))
|
||||||
|
part = part.slice(next.start)
|
||||||
|
|
||||||
|
let func: ActionFunction
|
||||||
|
|
||||||
|
switch (part[re.begin.length]) {
|
||||||
|
case re.comment:
|
||||||
|
func = actions.comment
|
||||||
|
break
|
||||||
|
case re.if:
|
||||||
|
func = actions.logic
|
||||||
|
break
|
||||||
|
// case re.for:
|
||||||
|
// func = actions.loop
|
||||||
|
// break
|
||||||
|
case 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
|
||||||
|
}
|
||||||
|
|
||||||
|
export function process(html: string, options = {}): Part[] {
|
||||||
|
const parts: Part[] = compileBlock(html).parts
|
||||||
|
return parts
|
||||||
|
|
||||||
|
}
|
41
options.ts
41
options.ts
@ -4,18 +4,37 @@ export const enum LOG_TYPE {
|
|||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Render {
|
export function isRender(obj: any): obj is Render {
|
||||||
do: ((data: any) => string)
|
return typeof obj === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Render = string
|
||||||
|
export type Part = (PartFunction | Render)
|
||||||
|
export type PartFunction = (data: any) => (Part[] | Render)
|
||||||
|
|
||||||
|
export interface ActionReturn {
|
||||||
|
parts: Part[]
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActionFunction = (part: Render) => ActionReturn
|
||||||
|
|
||||||
|
export interface Compiled {
|
||||||
|
template: Part[]
|
||||||
hash: string
|
hash: string
|
||||||
time: number
|
time: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Error {
|
export const error = {
|
||||||
parse: string
|
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',
|
||||||
|
},
|
||||||
|
|
||||||
export const error: Error = {
|
|
||||||
parse: 'Parse Error.'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
@ -33,8 +52,8 @@ export const options: Options = {
|
|||||||
caching: true,
|
caching: true,
|
||||||
template_dir: './views',
|
template_dir: './views',
|
||||||
template_ext: 'html',
|
template_ext: 'html',
|
||||||
compiled_dir: './views',
|
compiled_dir: './cache',
|
||||||
compiled_ext: 'htmlbin',
|
compiled_ext: 'bjs',
|
||||||
max_recursion: 100,
|
max_recursion: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +64,10 @@ interface Expressions {
|
|||||||
incude: string
|
incude: string
|
||||||
if: string
|
if: string
|
||||||
if_else: string
|
if_else: string
|
||||||
|
if_invert: string
|
||||||
for: string
|
for: string
|
||||||
for_in: string
|
for_in: string
|
||||||
|
closing_tag: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const re: Expressions = {
|
export const re: Expressions = {
|
||||||
@ -56,6 +77,8 @@ export const re: Expressions = {
|
|||||||
incude: '>',
|
incude: '>',
|
||||||
if: '?',
|
if: '?',
|
||||||
if_else: '!',
|
if_else: '!',
|
||||||
|
if_invert: '!',
|
||||||
for: '*',
|
for: '*',
|
||||||
for_in: 'in',
|
for_in: 'in',
|
||||||
|
closing_tag: '/',
|
||||||
}
|
}
|
106
parser.ts
106
parser.ts
@ -1,99 +1,27 @@
|
|||||||
import { re, error } from "./options";
|
import { Part, Render, isRender, PartFunction } from "./options";
|
||||||
import { readFile } from "./util";
|
|
||||||
import * as path from 'path'
|
|
||||||
import { options } from './options'
|
|
||||||
|
|
||||||
function getFromData(data: any, name: string): string {
|
|
||||||
|
|
||||||
name = name.trim()
|
export function computeParts(parts: Part[], data = {}): Render {
|
||||||
|
if (parts.length === 0)
|
||||||
// If not matches the valid pattern of a getter, return empty string
|
|
||||||
const valid: boolean = /^[A-z]\w*(\.[A-z]\w*|\[\d+\]|\[('|")\w+\2\]|\[[A-z]\w*\])*$/.test(name)
|
|
||||||
if (!valid)
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
name = name.replace(/('|")/g, '')
|
return computePart(parts[0], data) + computeParts(parts.slice(1), data)
|
||||||
name = name.replace(/\[(\w+)\]/g, '.$1')
|
|
||||||
|
|
||||||
for (const i of name.split('.'))
|
|
||||||
data = data[i]
|
|
||||||
|
|
||||||
return String(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceBetween(start: number, end: number, str: string, replace: string): string {
|
function computePart(part: Part, data = {}): Render {
|
||||||
return str.substring(0, start) + replace + str.substring(end)
|
if (isRender(part))
|
||||||
|
return part
|
||||||
|
else
|
||||||
|
return computePartFunction(part, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertImports(html: string, depth = 0): Promise<string> {
|
function computePartFunction(func: PartFunction, data = {}): Render {
|
||||||
if (depth > options.max_recursion)
|
if (isRender(func))
|
||||||
throw new Error('Maximal recursion in include statement')
|
return func;
|
||||||
|
else {
|
||||||
const begin = re.begin + re.incude
|
const ret = func(data)
|
||||||
const ending = re.ending
|
if (isRender(ret))
|
||||||
|
return ret
|
||||||
const exp = new RegExp(`${begin}.*?${ending}`, 'g')
|
else return computeParts(ret, data)
|
||||||
|
|
||||||
const includes = html.match(exp)
|
|
||||||
if (includes !== null)
|
|
||||||
for (const i of includes) {
|
|
||||||
const template_name = i.slice(begin.length, -ending.length).trim()
|
|
||||||
const file = await readFile(path.join(options.template_dir, `${template_name}.${options.template_ext}`))
|
|
||||||
const render = await insertImports(file, ++depth)
|
|
||||||
html = html.replace(i, render)
|
|
||||||
}
|
|
||||||
|
|
||||||
return html
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 begin = new RegExp(re.begin)
|
|
||||||
const ending = new RegExp(re.ending)
|
|
||||||
|
|
||||||
const ret: (((data: any) => string) | string)[] = []
|
|
||||||
|
|
||||||
let i = html.match(begin) // 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(ending)
|
|
||||||
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(begin) // Starting char
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.push(html)
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
}
|
76
reader.ts
Normal file
76
reader.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { Part, re, error, ActionFunction, ActionReturn } from './options'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
const rexp = Object.freeze({
|
||||||
|
begin: new RegExp(re.begin, 'g'),
|
||||||
|
end: new RegExp(re.ending, 'g'),
|
||||||
|
})
|
||||||
|
|
||||||
|
interface Next {
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compileBlock: ActionFunction = part => {
|
||||||
|
|
||||||
|
let next: Next
|
||||||
|
const getNext = (s: string): Next => Object.freeze({
|
||||||
|
start: s.search(rexp.begin),
|
||||||
|
end: s.search(rexp.end),
|
||||||
|
})
|
||||||
|
|
||||||
|
let ret: ActionReturn = {
|
||||||
|
parts: [],
|
||||||
|
length: NaN
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToRet(item: any) {
|
||||||
|
ret.parts = ret.parts.concat(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
next = getNext(part)
|
||||||
|
while (next.start !== -1) {
|
||||||
|
|
||||||
|
if (next.start === null || next.end === null)
|
||||||
|
throw new Error(error.parse.default)
|
||||||
|
|
||||||
|
addToRet(part.substr(0, next.start))
|
||||||
|
part = part.slice(next.start)
|
||||||
|
|
||||||
|
let func: ActionFunction
|
||||||
|
|
||||||
|
switch (part[re.begin.length]) {
|
||||||
|
case re.comment:
|
||||||
|
func = actions.comment
|
||||||
|
break
|
||||||
|
case re.if:
|
||||||
|
func = actions.logic
|
||||||
|
break
|
||||||
|
// case re.for:
|
||||||
|
// func = actions.loop
|
||||||
|
// break
|
||||||
|
case 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
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compile(html: string, options = {}): Part[] {
|
||||||
|
const parts: Part[] = compileBlock(html).parts
|
||||||
|
return parts
|
||||||
|
|
||||||
|
}
|
53
util.ts
53
util.ts
@ -1,6 +1,16 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface String {
|
||||||
|
log: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.log = function (): void {
|
||||||
|
console.log(this)
|
||||||
|
}
|
||||||
|
|
||||||
export function readFile(url: string): Promise<string> {
|
export function readFile(url: string): Promise<string> {
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
fs.readFile(url, (err, data) => {
|
fs.readFile(url, (err, data) => {
|
||||||
@ -12,7 +22,26 @@ export function readFile(url: string): Promise<string> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exists(url: string): Promise<boolean> {
|
export function readFileSync(url: string): string {
|
||||||
|
return fs.readFileSync(url).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeFile(url: string, data: any): Promise<boolean> {
|
||||||
|
return new Promise(res => {
|
||||||
|
fs.writeFile(url, data, err => {
|
||||||
|
if (err)
|
||||||
|
res(false)
|
||||||
|
res(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeFileSync(url: string, data: any): void {
|
||||||
|
fs.writeFileSync(url, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fileExists(url: string): Promise<boolean> {
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
fs.exists(url, _ => {
|
fs.exists(url, _ => {
|
||||||
res(_)
|
res(_)
|
||||||
@ -34,3 +63,25 @@ export function checksum(url: string, plain = false, alg = 'sha1'): Promise<stri
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function replaceBetween(start: number, end: number, str: string, replace: string): string {
|
||||||
|
return str.substring(0, start) + replace + str.substring(end)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFromObject(data: any, name: string): any {
|
||||||
|
|
||||||
|
name = name.trim()
|
||||||
|
|
||||||
|
// If not matches the valid pattern of a getter, return empty string
|
||||||
|
const valid: boolean = /^[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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user