mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 04:40:39 -07:00
perf: use polymophism instead duck test
This commit is contained in:
@@ -2,17 +2,26 @@ import Token from './token'
|
||||
import { last } from '../util/underscore'
|
||||
|
||||
export default class DelimitedToken extends Token {
|
||||
trimLeft: boolean
|
||||
trimRight: boolean
|
||||
constructor (raw: string, value: string, input: string, line: number, pos: number, file?: string) {
|
||||
constructor (
|
||||
raw: string,
|
||||
value: string,
|
||||
input: string,
|
||||
line: number,
|
||||
pos: number,
|
||||
trimLeft: boolean,
|
||||
trimRight: boolean,
|
||||
file?: string
|
||||
) {
|
||||
super(raw, input, line, pos, file)
|
||||
this.trimLeft = value[0] === '-'
|
||||
this.trimRight = last(value) === '-'
|
||||
const tl = value[0] === '-'
|
||||
const tr = last(value) === '-'
|
||||
this.value = value
|
||||
.slice(
|
||||
this.trimLeft ? 1 : 0,
|
||||
this.trimRight ? -1 : value.length
|
||||
tl ? 1 : 0,
|
||||
tr ? -1 : value.length
|
||||
)
|
||||
.trim()
|
||||
this.trimLeft = tl || trimLeft
|
||||
this.trimRight = tr || trimRight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ export default class HTMLToken extends Token {
|
||||
this.type = 'html'
|
||||
this.value = str
|
||||
}
|
||||
static is (token: Token): token is HTMLToken {
|
||||
return token.type === 'html'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
import DelimitedToken from './delimited-token'
|
||||
import Token from './token'
|
||||
import { NormalizedFullOptions } from '../liquid-options'
|
||||
|
||||
export default class OutputToken extends DelimitedToken {
|
||||
constructor (raw: string, value: string, input: string, line: number, pos: number, file?: string) {
|
||||
super(raw, value, input, line, pos, file)
|
||||
constructor (
|
||||
raw: string,
|
||||
value: string,
|
||||
input: string,
|
||||
line: number,
|
||||
pos: number,
|
||||
options: NormalizedFullOptions,
|
||||
file?: string
|
||||
) {
|
||||
super(raw, value, input, line, pos, options.trimOutputLeft, options.trimOutputRight, file)
|
||||
this.type = 'output'
|
||||
}
|
||||
static is (token: Token): token is OutputToken {
|
||||
return token.type === 'output'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,16 @@ export default class ParseStream {
|
||||
this.handlers[name] = cb
|
||||
return this
|
||||
}
|
||||
trigger <T extends Token | ITemplate> (event: string, arg?: T) {
|
||||
private trigger <T extends Token | ITemplate> (event: string, arg?: T) {
|
||||
const h = this.handlers[event]
|
||||
if (typeof h === 'function') {
|
||||
h(arg)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return h ? (h(arg), true) : false
|
||||
}
|
||||
start () {
|
||||
this.trigger('start')
|
||||
let token: Token | undefined
|
||||
while (!this.stopRequested && (token = this.tokens.shift())) {
|
||||
if (this.trigger('token', token)) continue
|
||||
if (token.type === 'tag' && this.trigger(`tag:${(<TagToken>token).name}`, token)) {
|
||||
if (TagToken.is(token) && this.trigger(`tag:${token.name}`, token)) {
|
||||
continue
|
||||
}
|
||||
const template = this.parseToken(token, this.tokens)
|
||||
|
||||
@@ -25,10 +25,10 @@ export default class Parser {
|
||||
}
|
||||
parseToken (token: Token, remainTokens: Array<Token>) {
|
||||
try {
|
||||
if (token.type === 'tag') {
|
||||
return new Tag(token as TagToken, remainTokens, this.liquid)
|
||||
if (TagToken.is(token)) {
|
||||
return new Tag(token, remainTokens, this.liquid)
|
||||
}
|
||||
if (token.type === 'output') {
|
||||
if (OutputToken.is(token)) {
|
||||
return new Output(token as OutputToken, this.liquid.options.strictFilters)
|
||||
}
|
||||
return new HTML(token)
|
||||
|
||||
+15
-2
@@ -1,12 +1,22 @@
|
||||
import DelimitedToken from './delimited-token'
|
||||
import Token from './token'
|
||||
import { TokenizationError } from '../util/error'
|
||||
import * as lexical from './lexical'
|
||||
import { NormalizedFullOptions } from '../liquid-options'
|
||||
|
||||
export default class TagToken extends DelimitedToken {
|
||||
name: string
|
||||
args: string
|
||||
constructor (raw: string, value: string, input: string, line: number, pos: number, file?: string) {
|
||||
super(raw, value, input, line, pos, file)
|
||||
constructor (
|
||||
raw: string,
|
||||
value: string,
|
||||
input: string,
|
||||
line: number,
|
||||
pos: number,
|
||||
options: NormalizedFullOptions,
|
||||
file?: string
|
||||
) {
|
||||
super(raw, value, input, line, pos, options.trimTagLeft, options.trimTagRight, file)
|
||||
this.type = 'tag'
|
||||
const match = this.value.match(lexical.tagLine)
|
||||
if (!match) {
|
||||
@@ -15,4 +25,7 @@ export default class TagToken extends DelimitedToken {
|
||||
this.name = match[1]
|
||||
this.args = match[2]
|
||||
}
|
||||
static is (token: Token): token is TagToken {
|
||||
return token.type === 'tag'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export default class Token {
|
||||
trimLeft: boolean = false
|
||||
trimRight: boolean = false
|
||||
type: string = 'notset'
|
||||
line: number
|
||||
col: number
|
||||
|
||||
+24
-19
@@ -9,16 +9,18 @@ import { NormalizedFullOptions, applyDefault } from '../liquid-options'
|
||||
enum ParseState { HTML, OUTPUT, TAG }
|
||||
|
||||
export default class Tokenizer {
|
||||
options: NormalizedFullOptions
|
||||
private options: NormalizedFullOptions
|
||||
constructor (options?: NormalizedFullOptions) {
|
||||
this.options = applyDefault(options)
|
||||
}
|
||||
tokenize (input: string, file?: string) {
|
||||
const tokens: Token[] = []
|
||||
const tagL = this.options.tagDelimiterLeft
|
||||
const tagR = this.options.tagDelimiterRight
|
||||
const outputL = this.options.outputDelimiterLeft
|
||||
const outputR = this.options.outputDelimiterRight
|
||||
const {
|
||||
tagDelimiterLeft,
|
||||
tagDelimiterRight,
|
||||
outputDelimiterLeft,
|
||||
outputDelimiterRight
|
||||
} = this.options
|
||||
let p = 0
|
||||
let curLine = 1
|
||||
let state = ParseState.HTML
|
||||
@@ -33,36 +35,39 @@ export default class Tokenizer {
|
||||
lineBegin = p + 1
|
||||
}
|
||||
if (state === ParseState.HTML) {
|
||||
if (input.substr(p, outputL.length) === outputL) {
|
||||
if (input.substr(p, outputDelimiterLeft.length) === outputDelimiterLeft) {
|
||||
if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file))
|
||||
buffer = outputL
|
||||
buffer = outputDelimiterLeft
|
||||
line = curLine
|
||||
col = p - lineBegin + 1
|
||||
p += outputL.length
|
||||
p += outputDelimiterLeft.length
|
||||
state = ParseState.OUTPUT
|
||||
continue
|
||||
} else if (input.substr(p, tagL.length) === tagL) {
|
||||
} else if (input.substr(p, tagDelimiterLeft.length) === tagDelimiterLeft) {
|
||||
if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file))
|
||||
buffer = tagL
|
||||
buffer = tagDelimiterLeft
|
||||
line = curLine
|
||||
col = p - lineBegin + 1
|
||||
p += tagL.length
|
||||
p += tagDelimiterLeft.length
|
||||
state = ParseState.TAG
|
||||
continue
|
||||
}
|
||||
} else if (state === ParseState.OUTPUT && input.substr(p, outputR.length) === outputR) {
|
||||
buffer += outputR
|
||||
tokens.push(new OutputToken(buffer, buffer.slice(outputL.length, -outputR.length), input, line, col, file))
|
||||
p += outputR.length
|
||||
} else if (
|
||||
state === ParseState.OUTPUT &&
|
||||
input.substr(p, outputDelimiterRight.length) === outputDelimiterRight
|
||||
) {
|
||||
buffer += outputDelimiterRight
|
||||
tokens.push(new OutputToken(buffer, buffer.slice(outputDelimiterLeft.length, -outputDelimiterRight.length), input, line, col, this.options, file))
|
||||
p += outputDelimiterRight.length
|
||||
buffer = ''
|
||||
line = curLine
|
||||
col = p - lineBegin + 1
|
||||
state = ParseState.HTML
|
||||
continue
|
||||
} else if (input.substr(p, tagR.length) === tagR) {
|
||||
buffer += tagR
|
||||
tokens.push(new TagToken(buffer, buffer.slice(tagL.length, -tagR.length), input, line, col, file))
|
||||
p += tagR.length
|
||||
} else if (input.substr(p, tagDelimiterRight.length) === tagDelimiterRight) {
|
||||
buffer += tagDelimiterRight
|
||||
tokens.push(new TagToken(buffer, buffer.slice(tagDelimiterLeft.length, -tagDelimiterRight.length), input, line, col, this.options, file))
|
||||
p += tagDelimiterRight.length
|
||||
buffer = ''
|
||||
line = curLine
|
||||
col = p - lineBegin + 1
|
||||
|
||||
@@ -1,47 +1,38 @@
|
||||
import DelimitedToken from '../parser/delimited-token'
|
||||
import Token from '../parser/token'
|
||||
import TagToken from '../parser/tag-token'
|
||||
import HTMLToken from '../parser/html-token'
|
||||
import { NormalizedFullOptions } from '../liquid-options'
|
||||
|
||||
export default function whiteSpaceCtrl (tokens: Token[], options: NormalizedFullOptions) {
|
||||
options = { greedy: true, ...options }
|
||||
let inRaw = false
|
||||
|
||||
tokens.forEach((token: Token, i: number) => {
|
||||
if (shouldTrimLeft(token as DelimitedToken, inRaw, options)) {
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (!inRaw && token.trimLeft) {
|
||||
trimLeft(tokens[i - 1], options.greedy)
|
||||
}
|
||||
|
||||
if (token.type === 'tag' && (token as TagToken).name === 'raw') inRaw = true
|
||||
if (token.type === 'tag' && (token as TagToken).name === 'endraw') inRaw = false
|
||||
if (TagToken.is(token)) {
|
||||
if (token.name === 'raw') inRaw = true
|
||||
else if (token.name === 'endraw') inRaw = false
|
||||
}
|
||||
|
||||
if (shouldTrimRight(token as DelimitedToken, inRaw, options)) {
|
||||
if (!inRaw && token.trimRight) {
|
||||
trimRight(tokens[i + 1], options.greedy)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function shouldTrimLeft (token: DelimitedToken, inRaw: boolean, options: NormalizedFullOptions) {
|
||||
if (inRaw) return false
|
||||
if (token.type === 'tag') return token.trimLeft || options.trimTagLeft
|
||||
if (token.type === 'output') return token.trimLeft || options.trimOutputLeft
|
||||
}
|
||||
|
||||
function shouldTrimRight (token: DelimitedToken, inRaw: boolean, options: NormalizedFullOptions) {
|
||||
if (inRaw) return false
|
||||
if (token.type === 'tag') return token.trimRight || options.trimTagRight
|
||||
if (token.type === 'output') return token.trimRight || options.trimOutputRight
|
||||
}
|
||||
}
|
||||
|
||||
function trimLeft (token: Token, greedy: boolean) {
|
||||
if (!token || token.type !== 'html') return
|
||||
if (!token || !HTMLToken.is(token)) return
|
||||
|
||||
const rLeft = greedy ? /\s+$/g : /[\t\r ]*$/g
|
||||
token.value = token.value.replace(rLeft, '')
|
||||
}
|
||||
|
||||
function trimRight (token: Token, greedy: boolean) {
|
||||
if (!token || token.type !== 'html') return
|
||||
if (!token || !HTMLToken.is(token)) return
|
||||
|
||||
const rRight = greedy ? /^\s+/g : /^[\t\r ]*\n?/g
|
||||
token.value = token.value.replace(rRight, '')
|
||||
|
||||
Reference in New Issue
Block a user