feat: move filters/tags to instances, fixes #188

This commit is contained in:
harttle
2020-03-03 00:02:29 +08:00
parent f6762078e0
commit df8a919f71
47 changed files with 240 additions and 221 deletions
+23
View File
@@ -0,0 +1,23 @@
import { FilterImplOptions } from './filter-impl-options'
import { Filter, FilterArgs } from './filter'
import { assert } from '../../util/assert'
export class FilterMap {
private impls: {[key: string]: FilterImplOptions} = {}
constructor (private readonly strictFilters: boolean) {}
get (name: string) {
const impl = this.impls[name]
assert(impl || !this.strictFilters, `undefined filter: ${name}`)
return impl
}
set (name: string, impl: FilterImplOptions) {
this.impls[name] = impl
}
create (name: string, args: FilterArgs) {
return new Filter(name, this.get(name), args)
}
}
+1 -11
View File
@@ -11,12 +11,8 @@ export class Filter {
public name: string
public args: FilterArgs
private impl: FilterImplOptions
private static impls: {[key: string]: FilterImplOptions} = {}
public constructor (name: string, args: FilterArgs, strictFilters: boolean) {
const impl = Filter.impls[name]
if (!impl && strictFilters) throw new TypeError(`undefined filter: ${name}`)
public constructor (name: string, impl: FilterImplOptions, args: FilterArgs) {
this.name = name
this.impl = impl || identify
this.args = args
@@ -29,12 +25,6 @@ export class Filter {
}
return this.impl.apply({ context }, [value, ...argv])
}
public static register (name: string, filter: FilterImplOptions) {
Filter.impls[name] = filter
}
public static clear () {
Filter.impls = {}
}
}
function isKeyValuePair (arr: FilterArg): arr is KeyValuePair {
+2 -2
View File
@@ -1,10 +1,10 @@
import { TemplateImpl } from '../template/template-impl'
import { Template } from '../template/template'
import { ITemplate } from '../template/itemplate'
import { HTMLToken } from '../parser/html-token'
import { Context } from '../context/context'
import { Emitter } from '../render/emitter'
export class HTML extends Template<HTMLToken> implements ITemplate {
export class HTML extends TemplateImpl<HTMLToken> implements Template {
private str: string
public constructor (token: HTMLToken) {
super(token)
-8
View File
@@ -1,8 +0,0 @@
import { Context } from '../context/context'
import { Token } from '../parser/token'
import { Emitter } from '../render/emitter'
export interface ITemplate {
token: Token;
render(ctx: Context, emitter: Emitter): any;
}
+5 -4
View File
@@ -1,16 +1,17 @@
import { Value } from './value'
import { FilterMap } from './filter/filter-map'
import { stringify, toValue } from '../util/underscore'
import { TemplateImpl } from '../template/template-impl'
import { Template } from '../template/template'
import { ITemplate } from '../template/itemplate'
import { Context } from '../context/context'
import { Emitter } from '../render/emitter'
import { OutputToken } from '../parser/output-token'
export class Output extends Template<OutputToken> implements ITemplate {
export class Output extends TemplateImpl<OutputToken> implements Template {
private value: Value
public constructor (token: OutputToken, strictFilters: boolean) {
public constructor (token: OutputToken, filters: FilterMap) {
super(token)
this.value = new Value(token.value, strictFilters)
this.value = new Value(token.value, filters)
}
public * render (ctx: Context, emitter: Emitter) {
const val = yield this.value.value(ctx)
-7
View File
@@ -1,7 +0,0 @@
import { Liquid } from '../../liquid'
import { ITagImplOptions } from './itag-impl-options'
export interface ITagImpl extends ITagImplOptions {
liquid: Liquid;
[key: string]: any;
}
@@ -1,11 +1,11 @@
import { Context } from '../../context/context'
import { TagToken } from '../../parser/tag-token'
import { Token } from '../../parser/token'
import { ITagImpl } from './itag-impl'
import { TagImpl } from './tag-impl'
import { Hash } from '../../template/tag/hash'
import { Emitter } from '../../render/emitter'
export interface ITagImplOptions {
parse?: (this: ITagImpl, token: TagToken, remainingTokens: Token[]) => void;
render: (this: ITagImpl, ctx: Context, hash: Hash, emitter: Emitter) => any;
export interface TagImplOptions {
parse?: (this: TagImpl, token: TagToken, remainingTokens: Token[]) => void;
render: (this: TagImpl, ctx: Context, hash: Hash, emitter: Emitter) => any;
}
+7
View File
@@ -0,0 +1,7 @@
import { Liquid } from '../../liquid'
import { TagImplOptions } from './tag-impl-options'
export interface TagImpl extends TagImplOptions {
liquid: Liquid;
[key: string]: any;
}
+16
View File
@@ -0,0 +1,16 @@
import { TagImplOptions } from './tag-impl-options'
import { assert } from '../../util/assert'
export class TagMap {
private impls: {[key: string]: TagImplOptions} = {}
get (name: string) {
const impl = this.impls[name]
assert(impl, `tag "${name}" not found`)
return impl
}
set (name: string, impl: TagImplOptions) {
this.impls[name] = impl
}
}
+7 -15
View File
@@ -1,21 +1,19 @@
import { isFunction } from '../../util/underscore'
import { assert } from '../../util/assert'
import { Liquid } from '../../liquid'
import { Template } from '../../template/template'
import { Emitter, Hash, Context, ITagImplOptions, TagToken, ITemplate, Token } from '../../types'
import { ITagImpl } from './itag-impl'
import { TemplateImpl } from '../../template/template-impl'
import { Emitter, Hash, Context, TagImplOptions, TagToken, Template, Token } from '../../types'
import { TagImpl } from './tag-impl'
export class Tag extends Template<TagToken> implements ITemplate {
export class Tag extends TemplateImpl<TagToken> implements Template {
public name: string
private impl: ITagImpl
private static impls: { [key: string]: ITagImplOptions } = {}
private impl: TagImpl
private static impls: { [key: string]: TagImplOptions } = {}
public constructor (token: TagToken, tokens: Token[], liquid: Liquid) {
super(token)
this.name = token.name
const impl = Tag.impls[token.name]
assert(impl, `tag ${token.name} not found`)
const impl = liquid.tags.get(token.name)
this.impl = Object.create(impl)
this.impl.liquid = liquid
@@ -28,10 +26,4 @@ export class Tag extends Template<TagToken> implements ITemplate {
const impl = this.impl
if (isFunction(impl.render)) return yield impl.render(ctx, hash, emitter)
}
public static register (name: string, tag: ITagImplOptions) {
Tag.impls[name] = tag
}
public static clear () {
Tag.impls = {}
}
}
+6
View File
@@ -0,0 +1,6 @@
export abstract class TemplateImpl<T> {
public token: T;
public constructor (token: T) {
this.token = token
}
}
+7 -5
View File
@@ -1,6 +1,8 @@
export abstract class Template<T> {
public token: T;
public constructor (token: T) {
this.token = token
}
import { Context } from '../context/context'
import { Token } from '../parser/token'
import { Emitter } from '../render/emitter'
export interface Template {
token: Token;
render(ctx: Context, emitter: Emitter): any;
}
+3 -4
View File
@@ -1,18 +1,17 @@
import { Expression } from '../render/expression'
import { FilterMap } from '../template/filter/filter-map'
import { FilterArgs, Filter } from './filter/filter'
import { Context } from '../context/context'
export class Value {
public readonly filters: Filter[] = []
public readonly initial: string
private strictFilters: boolean
/**
* @param str value string, like: "i have a dream | truncate: 3
*/
public constructor (str: string, strictFilters: boolean) {
public constructor (str: string, private readonly filterMap: FilterMap) {
const tokens = Value.tokenize(str)
this.strictFilters = strictFilters
this.initial = tokens[0]
this.parseFilters(tokens, 1)
}
@@ -45,7 +44,7 @@ export class Value {
argValue = tokens[i]
}
}
this.filters.push(new Filter(name, args, this.strictFilters))
this.filters.push(new Filter(name, this.filterMap.get(name), args))
}
public * value (ctx: Context) {
let val = yield new Expression(this.initial).evaluate(ctx)