chore(typescript): ship to TypeScript

This commit is contained in:
harttle
2019-02-14 22:33:45 +08:00
parent c8de8bcde2
commit c37b330751
64 changed files with 812 additions and 1621 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
import * as lexical from './lexical.js'
import { evalValue } from './syntax.js'
import assert from './util/assert.js'
import { assign, create } from './util/underscore.js'
import * as lexical from './lexical'
import { evalValue } from './syntax'
import assert from './util/assert'
import { assign, create } from './util/underscore'
const valueRE = new RegExp(`${lexical.value.source}`, 'g')
+4 -4
View File
@@ -1,6 +1,6 @@
import strftime from './util/strftime.js'
import * as _ from './util/underscore.js'
import { isTruthy } from './syntax.js'
import strftime from './util/strftime'
import * as _ from './util/underscore'
import { isTruthy } from './syntax'
const escapeMap = {
'&': '&',
@@ -132,7 +132,7 @@ function isValidDate (date) {
return date instanceof Date && !isNaN(date.getTime())
}
export default function registerAll (liquid) {
export default function registerAll (liquid, Liquid) {
return _.forOwn(filters, (func, name) => liquid.registerFilter(name, func))
}
-69
View File
@@ -1,69 +0,0 @@
export as namespace Liquid;
export function isTruthy(val: any): boolean;
export function isFalsy(val: any): boolean;
export function evalExp(exp: string, scope: any): any;
export function evalValue(str: string, scope: any): any;
export default class Liquid {
constructor(options?: Options);
private init(tag, filter, options): Liquid;
private respectCache(key, getter): Promise<any>
parse(html: string, filepath?: string): Liquid.Template
render(tpl: Template, ctx: any, opts?: Options): Promise<string>
parseAndRender(html: string, ctx: any, opts?: Options): Promise<string>
renderFile(file: string, ctx: any, opts?: Options): Promise<string>
getTemplate(file: string, root: string): Promise<Liquid.Template>
registerFilter(name: string, filter: Filter): void
registerTag(name: string, tag: Tag): void
express(opts: Options): any
}
export interface Options {
/** `root` is a directory or an array of directories to resolve layouts and includes, as well as the filename passed in when calling `.renderFile()`. If an array, the files are looked up in the order they occur in the array. Defaults to `["."]`*/
root?: string | string[]
/** `extname` is used to lookup the template file when filepath doesn't include an extension name. Eg: setting to `".html"` will allow including file by basename. Defaults to `""`. */
extname?: string
/** `cache` indicates whether or not to cache resolved templates. Defaults to `false`. */
cache?: boolean
/** `dynamicPartials`: if set, treat `<filepath>` parameter in `{%include filepath %}`, `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`. */
dynamicPartials?: boolean
/** `strict_filters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`. */
strict_filters?: boolean
/** `trim_tag_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`. */
trim_tag_right?: boolean
/** `trim_tag_left` is similar to `trim_tag_right`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */
trim_tag_left?: boolean
/** ``trim_value_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`. */
trim_value_right?: boolean
/** `trim_value_left` is similar to `trim_value_right`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */
trim_value_left?: boolean
/** `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`. */
greedy?: boolean
}
export interface Template { }
export interface Tag {
parse(this: any, tagToken: any, remainTokens: any): void
render(this: any, scope: any, hash: any): void
}
export type Filter = (...args: any) => string
declare namespace Types {
class LiquidError extends Error {
input: string
line: number
file: string
}
class ParseError extends LiquidError {
originalError: Error
}
class TokenizationError extends LiquidError {}
class RenderBreakError extends LiquidError {}
class AssertionError extends LiquidError {}
class AssignScope {}
class CaptureScope {}
class IncrementScope {}
class DecrementScope {}
}
+12 -13
View File
@@ -1,17 +1,16 @@
import 'regenerator-runtime/runtime'
import * as Scope from './scope'
import Scope from './scope'
import * as template from './template'
import * as _ from './util/underscore.js'
import assert from './util/assert.js'
import * as tokenizer from './tokenizer.js'
import Render from './render.js'
import Tag from './tag.js'
import Filter from './filter.js'
import * as _ from './util/underscore'
import assert from './util/assert'
import * as tokenizer from './tokenizer'
import Render from './render'
import Tag from './tag'
import Filter from './filter'
import Parser from './parser'
import { isTruthy, isFalsy, evalExp, evalValue } from './syntax.js'
import { ParseError, TokenizationError, RenderBreakError, AssertionError } from './util/error.js'
import tags from './tags/index.js'
import filters from './filters.js'
import { isTruthy, isFalsy, evalExp, evalValue } from './syntax'
import { ParseError, TokenizationError, RenderBreakError, AssertionError } from './util/error'
import tags from './tags/index'
import filters from './filters'
const _engine = {
init: function (tag, filter, options) {
@@ -35,7 +34,7 @@ const _engine = {
},
render: function (tpl, ctx, opts) {
opts = _.assign({}, this.options, opts)
const scope = Scope.factory(ctx, opts)
const scope = new Scope(ctx, opts)
return this.renderer.renderTemplates(tpl, scope)
},
parseAndRender: async function (html, ctx, opts) {
+17 -17
View File
@@ -1,27 +1,28 @@
import * as lexical from './lexical.js'
import { create } from './util/underscore.js'
import { ParseError } from './util/error.js'
import assert from './util/assert.js'
import * as lexical from './lexical'
import { ParseError } from './util/error'
import assert from './util/assert'
export default function (Tag, Filter) {
const stream = {
init: function (tokens) {
class ParseStream {
tokens: Array<any>
handlers: object
stopRequested: boolean
constructor (tokens) {
this.tokens = tokens
this.handlers = {}
return this
},
on: function (name, cb) {
}
on (name, cb) {
this.handlers[name] = cb
return this
},
trigger: function (event, arg) {
}
trigger (event: string, arg?: any) {
const h = this.handlers[event]
if (typeof h === 'function') {
h(arg)
return true
}
},
start: function () {
}
start () {
this.trigger('start')
let token
while (!this.stopRequested && (token = this.tokens.shift())) {
@@ -35,8 +36,8 @@ export default function (Tag, Filter) {
}
if (!this.stopRequested) this.trigger('end')
return this
},
stop: function () {
}
stop () {
this.stopRequested = true
return this
}
@@ -93,8 +94,7 @@ export default function (Tag, Filter) {
}
function parseStream (tokens) {
const s = create(stream)
return s.init(tokens)
return new ParseStream(tokens)
}
return {
+5 -5
View File
@@ -1,7 +1,7 @@
import { evalExp } from './syntax.js'
import { RenderBreakError, RenderError } from './util/error.js'
import { stringify, create } from './util/underscore.js'
import assert from './util/assert.js'
import { evalExp } from './syntax'
import { RenderBreakError, RenderError } from './util/error'
import { stringify, create } from './util/underscore'
import assert from './util/assert'
const render = {
renderTemplates: async function (templates, scope) {
@@ -16,7 +16,7 @@ const render = {
e.resolvedHTML = html
throw e
}
throw new RenderError(e, tpl)
throw e instanceof RenderError ? e : new RenderError(e, tpl)
}
}
return html
+42 -36
View File
@@ -1,17 +1,38 @@
import * as _ from './util/underscore.js'
import * as lexical from './lexical.js'
import assert from './util/assert.js'
import * as _ from './util/underscore'
import * as lexical from './lexical'
import assert from './util/assert'
const Scope = {
getAll: function () {
interface ScopeOptions {
dynamicPartials: boolean
strict_variables: boolean
strict_filters: boolean
blocks: object
root: Array<string>
}
export default class Scope {
opts: ScopeOptions
contexts: Array<object>
constructor (ctx = {}, opts?: any) {
const defaultOptions: ScopeOptions = {
dynamicPartials: true,
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}
this.opts = _.assign(defaultOptions, opts)
this.contexts = [ctx || {}]
}
getAll () {
return this.contexts.reduce((ctx, val) => _.assign(ctx, val), _.create(null))
},
get: function (path) {
}
get (path) {
const paths = this.propertyAccessSeq(path)
const scope = this.findContextFor(paths[0]) || _.last(this.contexts)
return paths.reduce((value, key) => this.readProperty(value, key), scope)
},
set: function (path, v) {
}
set (path, v) {
const paths = this.propertyAccessSeq(path)
let scope = this.findContextFor(paths[0]) || _.last(this.contexts)
paths.some((key, i) => {
@@ -27,14 +48,14 @@ const Scope = {
}
scope = scope[key]
})
},
unshift: function (ctx) {
}
unshift (ctx) {
return this.contexts.unshift(ctx)
},
push: function (ctx) {
}
push (ctx) {
return this.contexts.push(ctx)
},
pop: function (ctx) {
}
pop (ctx) {
if (!arguments.length) {
return this.contexts.pop()
}
@@ -43,9 +64,8 @@ const Scope = {
throw new TypeError('scope not found, cannot pop')
}
return this.contexts.splice(i, 1)[0]
},
findContextFor: function (key, filter) {
filter = filter || (() => true)
}
findContextFor (key, filter = (arg => true)) {
for (let i = this.contexts.length - 1; i >= 0; i--) {
const candidate = this.contexts[i]
if (!filter(candidate)) continue
@@ -54,8 +74,8 @@ const Scope = {
}
}
return null
},
readProperty: function (obj, key) {
}
readProperty (obj, key) {
let val
if (_.isNil(obj)) {
val = undefined
@@ -70,7 +90,7 @@ const Scope = {
throw new TypeError(`undefined variable: ${key}`)
}
return val
},
}
/*
* Parse property access sequence from access string
@@ -80,7 +100,7 @@ const Scope = {
* accessSeq("foo['b]r']") // ['foo', 'b]r']
* accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar'
*/
propertyAccessSeq: function (str) {
propertyAccessSeq (str) {
str = String(str)
const seq = []
let name = ''
@@ -163,17 +183,3 @@ function matchRightBracket (str, begin) {
}
return -1
}
export function factory (ctx, opts) {
const defaultOptions = {
dynamicPartials: true,
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}
const scope = _.create(Scope)
scope.opts = _.assign(defaultOptions, opts)
scope.contexts = [ctx || {}]
return scope
}
+3 -3
View File
@@ -1,6 +1,6 @@
import Operators from './operators.js'
import * as lexical from './lexical.js'
import assert from './util/assert.js'
import Operators from './operators'
import * as lexical from './lexical'
import assert from './util/assert'
const operators = Operators(isTruthy)
+4 -4
View File
@@ -1,7 +1,7 @@
import { hashCapture } from './lexical.js'
import { create } from './util/underscore.js'
import { evalValue } from './syntax.js'
import assert from './util/assert.js'
import { hashCapture } from './lexical'
import { create } from './util/underscore'
import { evalValue } from './syntax'
import assert from './util/assert'
function hash (markup, scope) {
const obj = {}
+3 -3
View File
@@ -1,6 +1,6 @@
import assert from '../util/assert.js'
import { identifier } from '../lexical.js'
import { create } from '../util/underscore.js'
import assert from '../util/assert'
import { identifier } from '../lexical'
import { create } from '../util/underscore'
export default function (liquid, Liquid) {
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
+3 -3
View File
@@ -1,6 +1,6 @@
import assert from '../util/assert.js'
import { create } from '../util/underscore.js'
import { identifier } from '../lexical.js'
import assert from '../util/assert'
import { create } from '../util/underscore'
import { identifier } from '../lexical'
export default function (liquid, Liquid) {
const re = new RegExp(`(${identifier.source})`)
+2 -2
View File
@@ -1,5 +1,5 @@
import assert from '../util/assert.js'
import { value as rValue } from '../lexical.js'
import assert from '../util/assert'
import { value as rValue } from '../lexical'
export default function (liquid, Liquid) {
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
+3 -3
View File
@@ -1,6 +1,6 @@
import { create } from '../util/underscore.js'
import assert from '../util/assert.js'
import { identifier } from '../lexical.js'
import { create } from '../util/underscore'
import assert from '../util/assert'
import { identifier } from '../lexical'
export default function (liquid, Liquid) {
const { CaptureScope, AssignScope, DecrementScope } = Liquid.Types
+4 -4
View File
@@ -1,7 +1,7 @@
import { mapSeries } from '../util/promise.js'
import { isString, isObject, isArray } from '../util/underscore.js'
import assert from '../util/assert.js'
import { identifier, value, hash } from '../lexical.js'
import { mapSeries } from '../util/promise'
import { isString, isObject, isArray } from '../util/underscore'
import assert from '../util/assert'
import { identifier, value, hash } from '../lexical'
export default function (liquid, Liquid) {
const RenderBreakError = Liquid.Types.RenderBreakError
+2 -2
View File
@@ -1,5 +1,5 @@
import assert from '../util/assert.js'
import { value, quotedLine } from '../lexical.js'
import assert from '../util/assert'
import { value, quotedLine } from '../lexical'
const staticFileRE = /[^\s,]+/
+3 -3
View File
@@ -1,6 +1,6 @@
import assert from '../util/assert.js'
import { create } from '../util/underscore.js'
import { identifier } from '../lexical.js'
import assert from '../util/assert'
import { create } from '../util/underscore'
import { identifier } from '../lexical'
export default function (liquid, Liquid) {
const { CaptureScope, AssignScope, IncrementScope } = Liquid.Types
+14 -14
View File
@@ -1,17 +1,17 @@
import For from './for.js'
import Assign from './assign.js'
import Capture from './capture.js'
import Case from './case.js'
import Comment from './comment.js'
import Include from './include.js'
import Decrement from './decrement.js'
import Cycle from './cycle.js'
import If from './if.js'
import Increment from './increment.js'
import Layout from './layout.js'
import Raw from './raw.js'
import Tablerow from './tablerow.js'
import Unless from './unless.js'
import For from './for'
import Assign from './assign'
import Capture from './capture'
import Case from './case'
import Comment from './comment'
import Include from './include'
import Decrement from './decrement'
import Cycle from './cycle'
import If from './if'
import Increment from './increment'
import Layout from './layout'
import Raw from './raw'
import Tablerow from './tablerow'
import Unless from './unless'
export default function (engine, Liquid) {
Assign(engine, Liquid)
+2 -2
View File
@@ -1,5 +1,5 @@
import assert from '../util/assert.js'
import { value as rValue } from '../lexical.js'
import assert from '../util/assert'
import { value as rValue } from '../lexical'
/*
* blockMode:
+3 -3
View File
@@ -1,6 +1,6 @@
import { mapSeries } from '../util/promise.js'
import assert from '../util/assert.js'
import { identifier, value, hash } from '../lexical.js'
import { mapSeries } from '../util/promise'
import assert from '../util/assert'
import { identifier, value, hash } from '../lexical'
export default function (liquid, Liquid) {
const re = new RegExp(`^(${identifier.source})\\s+in\\s+` +
+4 -4
View File
@@ -1,7 +1,7 @@
import * as _ from './util/underscore.js'
import path from 'path'
import { anySeries } from './util/promise.js'
import fs from 'fs'
import * as _ from './util/underscore'
import * as path from 'path'
import { anySeries } from './util/promise'
import * as fs from 'fs'
const statFileAsync = _.promisify(fs.stat)
const readFileAsync = _.promisify(fs.readFile)
+6 -6
View File
@@ -1,10 +1,10 @@
import * as lexical from './lexical.js'
import { TokenizationError } from './util/error.js'
import * as _ from './util/underscore.js'
import assert from './util/assert.js'
import whiteSpaceCtrl from './whitespace-ctrl.js'
import * as lexical from './lexical'
import { TokenizationError } from './util/error'
import * as _ from './util/underscore'
import assert from './util/assert'
import whiteSpaceCtrl from './whitespace-ctrl'
export { default as whiteSpaceCtrl } from './whitespace-ctrl.js'
export { default as whiteSpaceCtrl } from './whitespace-ctrl'
export function parse (input, file, options) {
assert(_.isString(input), 'illegal input')
+2 -2
View File
@@ -1,6 +1,6 @@
import { AssertionError } from './error.js'
import { AssertionError } from './error'
export default function (predicate, message) {
export default function (predicate: any, message: string) {
if (!predicate) {
message = message || `expect ${predicate} to be true`
throw new AssertionError(message)
-100
View File
@@ -1,100 +0,0 @@
import * as _ from './underscore.js'
function initError () {
this.name = this.constructor.name
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
}
function initLiquidError (err, token) {
initError.call(this)
this.input = token.input
this.line = token.line
this.file = token.file
const context = mkContext(token.input, token.line)
this.message = mkMessage(err.message, token)
this.stack = context +
'\n' + (this.stack || this.message) +
(err.stack ? '\nFrom ' + err.stack : '')
}
export function TokenizationError (message, token) {
initLiquidError.call(this, { message: message }, token)
}
TokenizationError.prototype = _.create(Error.prototype)
TokenizationError.prototype.constructor = TokenizationError
export function ParseError (e, token) {
_.assign(this, e)
this.originalError = e
initLiquidError.call(this, e, token)
}
ParseError.prototype = _.create(Error.prototype)
ParseError.prototype.constructor = ParseError
export function RenderError (e, tpl) {
// return the original render error
if (e instanceof RenderError) {
return e
}
_.assign(this, e)
this.originalError = e
initLiquidError.call(this, e, tpl.token)
}
RenderError.prototype = _.create(Error.prototype)
RenderError.prototype.constructor = RenderError
export function RenderBreakError (message) {
initError.call(this)
this.message = message + ''
}
RenderBreakError.prototype = _.create(Error.prototype)
RenderBreakError.prototype.constructor = RenderBreakError
export function AssertionError (message) {
initError.call(this)
this.message = message + ''
}
AssertionError.prototype = _.create(Error.prototype)
AssertionError.prototype.constructor = AssertionError
function mkContext (input, line) {
const lines = input.split('\n')
const begin = Math.max(line - 2, 1)
const end = Math.min(line + 3, lines.length)
const context = _
.range(begin, end + 1)
.map(l => [
(l === line) ? '>> ' : ' ',
align(l, end),
'| ',
lines[l - 1]
].join(''))
.join('\n')
return context
}
function align (n, max) {
const length = (max + '').length
const str = n + ''
const blank = Array(length - str.length).join(' ')
return blank + str
}
function mkMessage (msg, token) {
msg = msg || ''
if (token.file) {
msg += ', file:' + token.file
}
if (token.line) {
msg += ', line:' + token.line
}
return msg
}
+120
View File
@@ -0,0 +1,120 @@
import * as _ from './underscore'
function captureStack () {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
}
abstract class LiquidError {
input: string
line: string
file: string
message: string
name: string
stack: string
token: any
originalError: any
constructor(err, token) {
this.input = token.input
this.line = token.line
this.file = token.file
this.originalError = err
this.token = token
}
captureStackTrace(obj) {
this.name = obj.constructor.name
captureStack.call(obj)
const err = this.originalError
const context = mkContext(this.input, this.line)
this.message = mkMessage(err.message, this.token)
this.stack = context +
'\n' + (this.stack || this.message) +
(err.stack ? '\nFrom ' + err.stack : '')
}
}
export class TokenizationError extends LiquidError {
constructor(message, token) {
super({message}, token)
super.captureStackTrace(this)
}
}
TokenizationError.prototype = _.create(Error.prototype)
TokenizationError.prototype.constructor = TokenizationError
export class ParseError extends LiquidError {
constructor(err, token) {
super(err, token)
_.assign(this, err)
super.captureStackTrace(this)
}
}
ParseError.prototype = _.create(Error.prototype)
ParseError.prototype.constructor = ParseError
export class RenderError extends LiquidError {
constructor(err, tpl) {
super(err, tpl.token)
_.assign(this, err)
super.captureStackTrace(this)
}
}
RenderError.prototype = _.create(Error.prototype)
RenderError.prototype.constructor = RenderError
export class RenderBreakError {
message: string
constructor (message) {
captureStack.call(this)
this.message = message + ''
}
}
RenderBreakError.prototype = _.create(Error.prototype)
RenderBreakError.prototype.constructor = RenderBreakError
export class AssertionError {
message: string
constructor (message) {
captureStack.call(this)
this.message = message + ''
}
}
AssertionError.prototype = _.create(Error.prototype)
AssertionError.prototype.constructor = AssertionError
function mkContext (input, targetLine) {
const lines = input.split('\n')
const begin = Math.max(targetLine - 2, 1)
const end = Math.min(targetLine + 3, lines.length)
const context = _
.range(begin, end + 1)
.map(lineNumber => {
const indicator = (lineNumber === targetLine) ? '>> ' : ' '
const num = padStart(String(end).length, lineNumber)
const text = lines[lineNumber - 1]
return `${indicator}${num}| ${text}`
})
.join('\n')
return context
}
function mkMessage (msg, token) {
msg = msg || ''
if (token.file) {
msg += ', file:' + token.file
}
if (token.line) {
msg += ', line:' + token.line
}
return msg
}
function padStart (length, str) {
str = String(str)
const blank = Array(length - str.length).join(' ')
return blank + str
}
@@ -15,9 +15,9 @@ export function isFunction (value) {
}
export function promisify (fn) {
return function () {
return function (...args) {
return new Promise((resolve, reject) => {
fn(...arguments, (err, result) => {
fn(...args, (err, result) => {
err ? reject(err) : resolve(result)
})
})
@@ -96,11 +96,14 @@ export function forOwn (object, iteratee) {
* @param {...Object} sources The source objects.
* @return {Object} Returns object.
*/
export function assign (object) {
object = isObject(object) ? object : {}
const srcs = Array.prototype.slice.call(arguments, 1)
srcs.forEach((src) => Object.assign(object, src))
return object
export function assign (obj, ...srcs) {
obj = isObject(obj) ? obj : {}
srcs.forEach(src => binaryAssign(obj, src))
return obj
}
function binaryAssign(target, src) {
for(let key in src) if (src.hasOwnProperty(key)) target[key] = src[key]
}
export function last (arr) {
@@ -139,7 +142,7 @@ export function isObject (value) {
* Note that ranges that stop before they start are considered to be zero-length instead of
* negative if you'd like a negative range, use a negative step.
*/
export function range (start, stop, step) {
export function range (start: number, stop: number, step?: number) {
if (arguments.length === 1) {
stop = start
start = 0
+1 -1
View File
@@ -1,4 +1,4 @@
import { assign } from './util/underscore.js'
import { assign } from './util/underscore'
export default function whiteSpaceCtrl (tokens, options) {
options = assign({ greedy: true }, options)