mirror of
https://github.com/harttle/liquidjs.git
synced 2026-10-03 09:05:14 -07:00
fix: resolve TS errors without deprecated tsconfig options
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+2
-1
@@ -23,7 +23,8 @@ const tsconfig = (target) => ({
|
|||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
target,
|
target,
|
||||||
module: 'ES2015',
|
module: 'ES2015',
|
||||||
rootDir: 'src'
|
rootDir: 'src',
|
||||||
|
downlevelIteration: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class Context {
|
|||||||
* for tags like `{% capture %}` `{% assign %}` to operate
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
||||||
*/
|
*/
|
||||||
private scopes: Scope[] = [createScope()]
|
private scopes: Scope[] = [createScope()]
|
||||||
private registers = {}
|
private registers: Record<string, any> = {}
|
||||||
/**
|
/**
|
||||||
* user passed in scope
|
* user passed in scope
|
||||||
* `{% increment %}`, `{% decrement %}` changes this scope,
|
* `{% increment %}`, `{% decrement %}` changes this scope,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Context } from '../context'
|
import { Context } from '../context'
|
||||||
|
|
||||||
export abstract class Drop {
|
export abstract class Drop {
|
||||||
|
[key: string]: any
|
||||||
public liquidMethodMissing (key: string | number, context: Context): Promise<any> | any {
|
public liquidMethodMissing (key: string | number, context: Context): Promise<any> | any {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,14 +1,14 @@
|
|||||||
import { FilterImpl } from '../template'
|
import { FilterImpl } from '../template'
|
||||||
import { stringify } from '../util/underscore'
|
import { stringify } from '../util/underscore'
|
||||||
|
|
||||||
const escapeMap = {
|
const escapeMap: Record<string, string> = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
'>': '>',
|
'>': '>',
|
||||||
'"': '"',
|
'"': '"',
|
||||||
"'": '''
|
"'": '''
|
||||||
}
|
}
|
||||||
const unescapeMap = {
|
const unescapeMap: Record<string, string> = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
'>': '>',
|
'>': '>',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FilteredValueToken, TagToken, HTMLToken, HashToken, QuotedToken, LiquidTagToken, OutputToken, ValueToken, Token, RangeToken, FilterToken, TopLevelToken, PropertyAccessToken, OperatorToken, LiteralToken, IdentifierToken, NumberToken } from '../tokens'
|
import { FilteredValueToken, TagToken, HTMLToken, HashToken, QuotedToken, LiquidTagToken, OutputToken, ValueToken, Token, RangeToken, FilterToken, TopLevelToken, PropertyAccessToken, OperatorToken, LiteralToken, IdentifierToken, NumberToken } from '../tokens'
|
||||||
import { OperatorHandler } from '../render/operator'
|
import { OperatorHandler } from '../render/operator'
|
||||||
import { TrieNode, LiteralValue, Trie, createTrie, ellipsis, literalValues, TokenizationError, TYPES, QUOTE, BLANK, NUMBER, SIGN, isWord, isString } from '../util'
|
import { LiteralValue, Trie, createTrie, ellipsis, literalValues, TokenizationError, TYPES, QUOTE, BLANK, NUMBER, SIGN, isWord, isString } from '../util'
|
||||||
import { Operators, Expression } from '../render'
|
import { Operators, Expression } from '../render'
|
||||||
import { NormalizedFullOptions, defaultOptions } from '../liquid-options'
|
import { NormalizedFullOptions, defaultOptions } from '../liquid-options'
|
||||||
import { FilterArg } from './filter-arg'
|
import { FilterArg } from './filter-arg'
|
||||||
@@ -51,11 +51,11 @@ export class Tokenizer {
|
|||||||
return new OperatorToken(this.input, this.p, (this.p = end), this.file)
|
return new OperatorToken(this.input, this.p, (this.p = end), this.file)
|
||||||
}
|
}
|
||||||
matchTrie<T> (trie: Trie<T>) {
|
matchTrie<T> (trie: Trie<T>) {
|
||||||
let node: TrieNode<T> = trie
|
let node: Trie<T> = trie
|
||||||
let i = this.p
|
let i = this.p
|
||||||
let info
|
let info: Trie<T> | undefined
|
||||||
while (node[this.input[i]] && i < this.N) {
|
while ((node as Trie<T>)[this.input[i]] && i < this.N) {
|
||||||
node = node[this.input[i++]]
|
node = (node as Trie<T>)[this.input[i++]] as Trie<T>
|
||||||
if (node['end']) info = node
|
if (node['end']) info = node
|
||||||
}
|
}
|
||||||
if (!info) return -1
|
if (!info) return -1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const rHex = /[\da-fA-F]/
|
const rHex = /[\da-fA-F]/
|
||||||
const rOct = /[0-7]/
|
const rOct = /[0-7]/
|
||||||
const escapeChar = {
|
const escapeChar: Record<string, string> = {
|
||||||
b: '\b',
|
b: '\b',
|
||||||
f: '\f',
|
f: '\f',
|
||||||
n: '\n',
|
n: '\n',
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ export default class extends Tag {
|
|||||||
|
|
||||||
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText()
|
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText()
|
||||||
ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }))
|
ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }))
|
||||||
const hash = yield this.hash.render(ctx)
|
const hash = (yield this.hash.render(ctx)) as Record<string, any>
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
|
|
||||||
const modifiers = this.liquid.options.orderedFilterParameters
|
const modifiers = this.liquid.options.orderedFilterParameters
|
||||||
|
|||||||
+14
-12
@@ -3,16 +3,18 @@ import { BlockMode, createScope, Scope } from '../context'
|
|||||||
import { Parser } from '../parser'
|
import { Parser } from '../parser'
|
||||||
import { Argument, Arguments, PartialScope } from '../template'
|
import { Argument, Arguments, PartialScope } from '../template'
|
||||||
import { isString, isValueToken } from '../util'
|
import { isString, isValueToken } from '../util'
|
||||||
import { parseFilePath, renderFilePath } from './render'
|
import { parseFilePath, renderFilePath, ParsedFileName } from './render'
|
||||||
|
|
||||||
export default class extends Tag {
|
export default class extends Tag {
|
||||||
|
private file: ParsedFileName
|
||||||
|
private currentFile?: string
|
||||||
private withVar?: ValueToken
|
private withVar?: ValueToken
|
||||||
private hash: Hash
|
private hash: Hash
|
||||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
||||||
super(token, remainTokens, liquid)
|
super(token, remainTokens, liquid)
|
||||||
const { tokenizer } = token
|
const { tokenizer } = token
|
||||||
this['file'] = parseFilePath(tokenizer, this.liquid, parser)
|
this.file = parseFilePath(tokenizer, this.liquid, parser)
|
||||||
this['currentFile'] = token.file
|
this.currentFile = token.file
|
||||||
|
|
||||||
const begin = tokenizer.p
|
const begin = tokenizer.p
|
||||||
const withStr = tokenizer.readIdentifier()
|
const withStr = tokenizer.readIdentifier()
|
||||||
@@ -28,7 +30,7 @@ export default class extends Tag {
|
|||||||
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
|
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
|
||||||
const { liquid, hash, withVar } = this
|
const { liquid, hash, withVar } = this
|
||||||
const { renderer } = liquid
|
const { renderer } = liquid
|
||||||
const filepath = (yield renderFilePath(this['file'], ctx, liquid)) as string
|
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
|
||||||
assert(filepath, () => `illegal file path "${filepath}"`)
|
assert(filepath, () => `illegal file path "${filepath}"`)
|
||||||
|
|
||||||
const saved = ctx.saveRegister('blocks', 'blockMode')
|
const saved = ctx.saveRegister('blocks', 'blockMode')
|
||||||
@@ -36,7 +38,7 @@ export default class extends Tag {
|
|||||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||||
const scope = createScope((yield hash.render(ctx)) as Scope)
|
const scope = createScope((yield hash.render(ctx)) as Scope)
|
||||||
if (withVar) scope[filepath] = yield evalToken(withVar, ctx)
|
if (withVar) scope[filepath] = yield evalToken(withVar, ctx)
|
||||||
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this['currentFile'])) as Template[]
|
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this.currentFile)) as Template[]
|
||||||
ctx.push(ctx.opts.jekyllInclude ? createScope({ include: scope }) : scope)
|
ctx.push(ctx.opts.jekyllInclude ? createScope({ include: scope }) : scope)
|
||||||
yield renderer.renderTemplates(templates, ctx, emitter)
|
yield renderer.renderTemplates(templates, ctx, emitter)
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
@@ -44,14 +46,14 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public * children (partials: boolean, sync: boolean): Generator<unknown, Template[]> {
|
public * children (partials: boolean, sync: boolean): Generator<unknown, Template[]> {
|
||||||
if (partials && isString(this['file'])) {
|
if (partials && isString(this.file)) {
|
||||||
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile'])) as Template[]
|
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile)) as Template[]
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
public partialScope (): PartialScope | undefined {
|
public partialScope (): PartialScope | undefined {
|
||||||
if (isString(this['file'])) {
|
if (isString(this.file)) {
|
||||||
let names: Array<string | [string, Argument]>
|
let names: Array<string | [string, Argument]>
|
||||||
|
|
||||||
if (this.liquid.options.jekyllInclude) {
|
if (this.liquid.options.jekyllInclude) {
|
||||||
@@ -59,19 +61,19 @@ export default class extends Tag {
|
|||||||
} else {
|
} else {
|
||||||
names = Object.keys(this.hash.hash)
|
names = Object.keys(this.hash.hash)
|
||||||
if (this.withVar) {
|
if (this.withVar) {
|
||||||
names.push([this['file'], this.withVar])
|
names.push([this.file, this.withVar])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name: this['file'], isolated: false, scope: names }
|
return { name: this.file, isolated: false, scope: names }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public * arguments (): Arguments {
|
public * arguments (): Arguments {
|
||||||
yield * Object.values(this.hash.hash).filter(isValueToken)
|
yield * Object.values(this.hash.hash).filter(isValueToken)
|
||||||
|
|
||||||
if (isValueToken(this['file'])) {
|
if (isValueToken(this.file)) {
|
||||||
yield this['file']
|
yield this.file
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValueToken(this.withVar)) {
|
if (isValueToken(this.withVar)) {
|
||||||
|
|||||||
+4
-3
@@ -10,10 +10,11 @@ export default class extends Tag {
|
|||||||
args: Hash
|
args: Hash
|
||||||
templates: Template[]
|
templates: Template[]
|
||||||
file?: ParsedFileName
|
file?: ParsedFileName
|
||||||
|
private currentFile?: string
|
||||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
||||||
super(token, remainTokens, liquid)
|
super(token, remainTokens, liquid)
|
||||||
this.file = parseFilePath(this.tokenizer, this.liquid, parser)
|
this.file = parseFilePath(this.tokenizer, this.liquid, parser)
|
||||||
this['currentFile'] = token.file
|
this.currentFile = token.file
|
||||||
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator)
|
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator)
|
||||||
this.templates = parser.parseTokens(remainTokens)
|
this.templates = parser.parseTokens(remainTokens)
|
||||||
}
|
}
|
||||||
@@ -27,7 +28,7 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
|
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
|
||||||
assert(filepath, () => `illegal file path "${filepath}"`)
|
assert(filepath, () => `illegal file path "${filepath}"`)
|
||||||
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this['currentFile'])) as Template[]
|
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this.currentFile)) as Template[]
|
||||||
|
|
||||||
// render remaining contents and store rendered results
|
// render remaining contents and store rendered results
|
||||||
ctx.setRegister('blockMode', BlockMode.STORE)
|
ctx.setRegister('blockMode', BlockMode.STORE)
|
||||||
@@ -48,7 +49,7 @@ export default class extends Tag {
|
|||||||
const templates = this.templates.slice()
|
const templates = this.templates.slice()
|
||||||
|
|
||||||
if (partials && isString(this.file)) {
|
if (partials && isString(this.file)) {
|
||||||
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this['currentFile'])) as Template[])
|
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this.currentFile)) as Template[])
|
||||||
}
|
}
|
||||||
|
|
||||||
return templates
|
return templates
|
||||||
|
|||||||
+29
-23
@@ -1,16 +1,20 @@
|
|||||||
import { __assign } from 'tslib'
|
import { __assign } from 'tslib'
|
||||||
import { ForloopDrop } from '../drop'
|
import { ForloopDrop } from '../drop'
|
||||||
import { isString, isValueToken, toEnumerable } from '../util'
|
import { isString, isValueToken, toEnumerable } from '../util'
|
||||||
import { TopLevelToken, assert, Liquid, Token, Template, evalQuotedToken, TypeGuards, Tokenizer, evalToken, Hash, Emitter, TagToken, Context, Tag } from '..'
|
import { TopLevelToken, assert, Liquid, Token, ValueToken, Template, evalQuotedToken, TypeGuards, Tokenizer, evalToken, Hash, Emitter, TagToken, Context, Tag } from '..'
|
||||||
import { Parser } from '../parser'
|
import { Parser } from '../parser'
|
||||||
import { Argument, Arguments, PartialScope } from '../template'
|
import { Argument, Arguments, PartialScope } from '../template'
|
||||||
|
|
||||||
export type ParsedFileName = Template[] | Token | string | undefined
|
export type ParsedFileName = Template[] | Token | string | undefined
|
||||||
|
|
||||||
|
type RenderBinding = { value: ValueToken; alias?: string }
|
||||||
|
|
||||||
export default class extends Tag {
|
export default class extends Tag {
|
||||||
private file: ParsedFileName
|
private file: ParsedFileName
|
||||||
private currentFile?: string
|
private currentFile?: string
|
||||||
private hash: Hash
|
private hash: Hash
|
||||||
|
private with?: RenderBinding
|
||||||
|
private forBinding?: RenderBinding
|
||||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid, parser: Parser) {
|
||||||
super(token, remainTokens, liquid)
|
super(token, remainTokens, liquid)
|
||||||
const tokenizer = this.tokenizer
|
const tokenizer = this.tokenizer
|
||||||
@@ -33,7 +37,9 @@ export default class extends Tag {
|
|||||||
if (asStr.content === 'as') alias = tokenizer.readIdentifier()
|
if (asStr.content === 'as') alias = tokenizer.readIdentifier()
|
||||||
else tokenizer.p = beforeAs
|
else tokenizer.p = beforeAs
|
||||||
|
|
||||||
this[keyword.content] = { value, alias: alias && alias.content }
|
const binding: RenderBinding = { value, alias: alias && alias.content }
|
||||||
|
if (keyword.content === 'with') this.with = binding
|
||||||
|
else this.forBinding = binding
|
||||||
tokenizer.skipBlank()
|
tokenizer.skipBlank()
|
||||||
if (tokenizer.peek() === ',') tokenizer.advance()
|
if (tokenizer.peek() === ',') tokenizer.advance()
|
||||||
continue // matched!
|
continue // matched!
|
||||||
@@ -50,46 +56,46 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
|
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
|
||||||
const { liquid, hash } = this
|
const { liquid, hash } = this
|
||||||
const filepath = (yield renderFilePath(this['file'], ctx, liquid)) as string
|
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
|
||||||
assert(filepath, () => `illegal file path "${filepath}"`)
|
assert(filepath, () => `illegal file path "${filepath}"`)
|
||||||
|
|
||||||
const childCtx = ctx.spawn()
|
const childCtx = ctx.spawn()
|
||||||
const scope = childCtx.bottom()
|
const scope = childCtx.bottom()
|
||||||
__assign(scope, yield hash.render(ctx))
|
__assign(scope, yield hash.render(ctx))
|
||||||
if (this['with']) {
|
if (this.with) {
|
||||||
const { value, alias } = this['with']
|
const { value, alias } = this.with
|
||||||
scope[alias || filepath] = yield evalToken(value, ctx)
|
scope[alias || filepath] = yield evalToken(value, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this['for']) {
|
if (this.forBinding) {
|
||||||
const { value, alias } = this['for']
|
const { value, alias } = this.forBinding
|
||||||
const collection = toEnumerable(yield evalToken(value, ctx))
|
const collection = toEnumerable(yield evalToken(value, ctx))
|
||||||
scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias)
|
scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias as string)
|
||||||
for (const item of collection) {
|
for (const item of collection) {
|
||||||
scope[alias] = item
|
scope[alias as string] = item
|
||||||
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile'])) as Template[]
|
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile)) as Template[]
|
||||||
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
|
||||||
scope['forloop'].next()
|
scope['forloop'].next()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile'])) as Template[]
|
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile)) as Template[]
|
||||||
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public * children (partials: boolean, sync: boolean): Generator<unknown, Template[]> {
|
public * children (partials: boolean, sync: boolean): Generator<unknown, Template[]> {
|
||||||
if (partials && isString(this['file'])) {
|
if (partials && isString(this.file)) {
|
||||||
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile'])) as Template[]
|
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile)) as Template[]
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
public partialScope (): PartialScope | undefined {
|
public partialScope (): PartialScope | undefined {
|
||||||
if (isString(this['file'])) {
|
if (isString(this.file)) {
|
||||||
const names: Array<string | [string, Argument]> = Object.keys(this.hash.hash)
|
const names: Array<string | [string, Argument]> = Object.keys(this.hash.hash)
|
||||||
|
|
||||||
if (this['with']) {
|
if (this.with) {
|
||||||
const { value, alias } = this['with']
|
const { value, alias } = this.with
|
||||||
if (isString(alias)) {
|
if (isString(alias)) {
|
||||||
names.push([alias, value])
|
names.push([alias, value])
|
||||||
} else if (isString(this.file)) {
|
} else if (isString(this.file)) {
|
||||||
@@ -97,8 +103,8 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this['for']) {
|
if (this.forBinding) {
|
||||||
const { value, alias } = this['for']
|
const { value, alias } = this.forBinding
|
||||||
if (isString(alias)) {
|
if (isString(alias)) {
|
||||||
names.push([alias, value])
|
names.push([alias, value])
|
||||||
} else if (isString(this.file)) {
|
} else if (isString(this.file)) {
|
||||||
@@ -106,7 +112,7 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name: this['file'], isolated: true, scope: names }
|
return { name: this.file, isolated: true, scope: names }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,15 +123,15 @@ export default class extends Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this['with']) {
|
if (this.with) {
|
||||||
const { value } = this['with']
|
const { value } = this.with
|
||||||
if (isValueToken(value)) {
|
if (isValueToken(value)) {
|
||||||
yield value
|
yield value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this['for']) {
|
if (this.forBinding) {
|
||||||
const { value } = this['for']
|
const { value } = this.forBinding
|
||||||
if (isValueToken(value)) {
|
if (isValueToken(value)) {
|
||||||
yield value
|
yield value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
* render (ctx: Context): Generator<unknown, Record<string, any>, unknown> {
|
* render (ctx: Context): Generator<unknown, Record<string, any>, unknown> {
|
||||||
const hash = {}
|
const hash: Record<string, any> = {}
|
||||||
for (const key of Object.keys(this.hash)) {
|
for (const key of Object.keys(this.hash)) {
|
||||||
hash[key] = this.hash[key] === undefined ? true : yield evalToken(this.hash[key], ctx)
|
hash[key] = this.hash[key] === undefined ? true : yield evalToken(this.hash[key], ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Token } from './token'
|
import { Token } from './token'
|
||||||
import { TokenKind } from '../parser'
|
import { TokenKind } from '../parser'
|
||||||
import { literalValues, LiteralValue } from '../util'
|
import { literalValues, LiteralValue, LiteralKey } from '../util'
|
||||||
|
|
||||||
export class LiteralToken extends Token {
|
export class LiteralToken extends Token {
|
||||||
public content: LiteralValue
|
public content: LiteralValue
|
||||||
public literal: string
|
public literal: LiteralKey
|
||||||
public constructor (
|
public constructor (
|
||||||
public input: string,
|
public input: string,
|
||||||
public begin: number,
|
public begin: number,
|
||||||
@@ -12,7 +12,7 @@ export class LiteralToken extends Token {
|
|||||||
public file?: string
|
public file?: string
|
||||||
) {
|
) {
|
||||||
super(TokenKind.Literal, input, begin, end, file)
|
super(TokenKind.Literal, input, begin, end, file)
|
||||||
this.literal = this.getText()
|
this.literal = this.getText() as LiteralKey
|
||||||
this.content = literalValues[this.literal]
|
this.content = literalValues[this.literal]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ export const operatorTypes = {
|
|||||||
'or': OperatorType.Binary
|
'or': OperatorType.Binary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OperatorKey = keyof typeof operatorPrecedences
|
||||||
|
|
||||||
export class OperatorToken extends Token {
|
export class OperatorToken extends Token {
|
||||||
public operator: string
|
public operator: OperatorKey
|
||||||
public constructor (
|
public constructor (
|
||||||
public input: string,
|
public input: string,
|
||||||
public begin: number,
|
public begin: number,
|
||||||
@@ -41,10 +43,9 @@ export class OperatorToken extends Token {
|
|||||||
public file?: string
|
public file?: string
|
||||||
) {
|
) {
|
||||||
super(TokenKind.Operator, input, begin, end, file)
|
super(TokenKind.Operator, input, begin, end, file)
|
||||||
this.operator = this.getText()
|
this.operator = this.getText() as OperatorKey
|
||||||
}
|
}
|
||||||
getPrecedence () {
|
getPrecedence () {
|
||||||
const key = this.getText()
|
return this.operator in operatorPrecedences ? operatorPrecedences[this.operator] : 1
|
||||||
return key in operatorPrecedences ? operatorPrecedences[key] : 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -18,10 +18,10 @@ export async function toPromise<T> (val: Generator<unknown, T, unknown> | Promis
|
|||||||
if (!isIterator(val)) return val
|
if (!isIterator(val)) return val
|
||||||
let value: unknown
|
let value: unknown
|
||||||
let done = false
|
let done = false
|
||||||
let next = 'next'
|
let next: 'next' | 'throw' = 'next'
|
||||||
do {
|
do {
|
||||||
const state = val[next](value)
|
const state = val[next](value)
|
||||||
done = state.done
|
done = !!state.done
|
||||||
value = state.value
|
value = state.value
|
||||||
next = 'next'
|
next = 'next'
|
||||||
try {
|
try {
|
||||||
@@ -40,10 +40,10 @@ export function toValueSync<T> (val: Generator<unknown, T, unknown> | T): T {
|
|||||||
if (!isIterator(val)) return val
|
if (!isIterator(val)) return val
|
||||||
let value: any
|
let value: any
|
||||||
let done = false
|
let done = false
|
||||||
let next = 'next'
|
let next: 'next' | 'throw' = 'next'
|
||||||
do {
|
do {
|
||||||
const state = val[next](value)
|
const state = val[next](value)
|
||||||
done = state.done
|
done = !!state.done
|
||||||
value = state.value
|
value = state.value
|
||||||
next = 'next'
|
next = 'next'
|
||||||
if (isIterator(value)) {
|
if (isIterator(value)) {
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ export abstract class LiquidError extends Error {
|
|||||||
if (this.originalError) this.stack += '\nFrom ' + this.originalError.stack
|
if (this.originalError) this.stack += '\nFrom ' + this.originalError.stack
|
||||||
}
|
}
|
||||||
static is (obj: unknown): obj is LiquidError {
|
static is (obj: unknown): obj is LiquidError {
|
||||||
return obj?.[TRAIT] === 'LiquidError'
|
return (obj as Record<string, unknown> | null | undefined)?.[TRAIT] === 'LiquidError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,22 +4,16 @@ interface TrieInput<T> {
|
|||||||
[key: string]: T
|
[key: string]: T
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TrieLeafNode<T> {
|
export type Trie<T> = {
|
||||||
data: T;
|
data?: T
|
||||||
end: true;
|
end?: true
|
||||||
needBoundary?: true;
|
needBoundary?: true
|
||||||
}
|
} & Record<string, any>
|
||||||
|
|
||||||
export interface Trie<T> {
|
|
||||||
[key: string]: Trie<T> | TrieLeafNode<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TrieNode<T> = Trie<T> | TrieLeafNode<T>
|
|
||||||
|
|
||||||
export function createTrie<T = any> (input: TrieInput<T>): Trie<T> {
|
export function createTrie<T = any> (input: TrieInput<T>): Trie<T> {
|
||||||
const trie: Trie<T> = {}
|
const trie: Trie<T> = {}
|
||||||
for (const [name, data] of Object.entries(input)) {
|
for (const [name, data] of Object.entries(input)) {
|
||||||
let node: Trie<T> | TrieLeafNode<T> = trie
|
let node = trie
|
||||||
|
|
||||||
for (let i = 0; i < name.length; i++) {
|
for (let i = 0; i < name.length; i++) {
|
||||||
const c = name[i]
|
const c = name[i]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { Limiter } from './limiter'
|
|||||||
|
|
||||||
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/
|
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/
|
||||||
interface FormatOptions {
|
interface FormatOptions {
|
||||||
flags: object;
|
flags: Record<string, boolean>;
|
||||||
width?: string;
|
width?: string;
|
||||||
modifier?: string;
|
modifier?: string;
|
||||||
memoryLimit?: Pick<Limiter, 'use'>;
|
memoryLimit?: Pick<Limiter, 'use'>;
|
||||||
@@ -50,7 +50,7 @@ function century (d: LiquidDate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default to 0
|
// default to 0
|
||||||
const padWidths = {
|
const padWidths: Record<string, number> = {
|
||||||
d: 2,
|
d: 2,
|
||||||
e: 2,
|
e: 2,
|
||||||
H: 2,
|
H: 2,
|
||||||
@@ -77,7 +77,9 @@ function getTimezoneOffset (d: LiquidDate, opts: FormatOptions) {
|
|||||||
(opts.flags[':'] ? ':' : '') +
|
(opts.flags[':'] ? ':' : '') +
|
||||||
padStart(m, 2, '0')
|
padStart(m, 2, '0')
|
||||||
}
|
}
|
||||||
const formatCodes = {
|
type FormatCodeHandler = (d: LiquidDate, opts: FormatOptions) => unknown
|
||||||
|
|
||||||
|
const formatCodes: Record<string, FormatCodeHandler> = {
|
||||||
a: (d: LiquidDate) => d.getShortWeekdayName(),
|
a: (d: LiquidDate) => d.getShortWeekdayName(),
|
||||||
A: (d: LiquidDate) => d.getLongWeekdayName(),
|
A: (d: LiquidDate) => d.getLongWeekdayName(),
|
||||||
b: (d: LiquidDate) => d.getShortMonthName(),
|
b: (d: LiquidDate) => d.getShortMonthName(),
|
||||||
@@ -118,8 +120,8 @@ const formatCodes = {
|
|||||||
't': () => '\t',
|
't': () => '\t',
|
||||||
'n': () => '\n',
|
'n': () => '\n',
|
||||||
'%': () => '%'
|
'%': () => '%'
|
||||||
};
|
}
|
||||||
(formatCodes as any).h = formatCodes.b
|
formatCodes.h = formatCodes.b
|
||||||
|
|
||||||
export function strftime (d: LiquidDate, formatStr: string, memoryLimit?: Pick<Limiter, 'use'>) {
|
export function strftime (d: LiquidDate, formatStr: string, memoryLimit?: Pick<Limiter, 'use'>) {
|
||||||
let output = ''
|
let output = ''
|
||||||
@@ -137,11 +139,11 @@ function format (d: LiquidDate, match: RegExpExecArray, memoryLimit?: Pick<Limit
|
|||||||
const [input, flagStr = '', width, modifier, conversion] = match
|
const [input, flagStr = '', width, modifier, conversion] = match
|
||||||
const convert = formatCodes[conversion]
|
const convert = formatCodes[conversion]
|
||||||
if (!convert) return input
|
if (!convert) return input
|
||||||
const flags = {}
|
const flags: Record<string, boolean> = {}
|
||||||
for (const flag of flagStr) flags[flag] = true
|
for (const flag of flagStr) flags[flag] = true
|
||||||
let ret = String(convert(d, { flags, width, modifier, memoryLimit }))
|
let ret = String(convert(d, { flags, width, modifier, memoryLimit }))
|
||||||
let padChar = padSpaceChars.has(conversion) ? ' ' : '0'
|
let padChar = padSpaceChars.has(conversion) ? ' ' : '0'
|
||||||
let padWidth = width || padWidths[conversion] || 0
|
let padWidth = Number(width) || padWidths[conversion] || 0
|
||||||
if (flags['^']) ret = ret.toUpperCase()
|
if (flags['^']) ret = ret.toUpperCase()
|
||||||
else if (flags['#']) ret = changeCase(ret)
|
else if (flags['#']) ret = changeCase(ret)
|
||||||
if (flags['_']) padChar = ' '
|
if (flags['_']) padChar = ' '
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Liquid, Context, isFalsy } from '../../../src'
|
import { Liquid, Context, isFalsy } from '../../../src'
|
||||||
import { mock, restore } from '../../stub/mockfs'
|
import { mock, restore } from '../../stub/mockfs'
|
||||||
import { drainStream } from '../../stub/stream'
|
import { drainStream } from '../../stub/stream'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
describe('Liquid', function () {
|
describe('Liquid', function () {
|
||||||
describe('#plugin()', function () {
|
describe('#plugin()', function () {
|
||||||
@@ -144,7 +145,7 @@ describe('Liquid', function () {
|
|||||||
})
|
})
|
||||||
it('should fallback to require.resolve in Node.js', async function () {
|
it('should fallback to require.resolve in Node.js', async function () {
|
||||||
const engine = new Liquid({
|
const engine = new Liquid({
|
||||||
root: [process.cwd()],
|
root: [resolve(__dirname, '../../..')],
|
||||||
extname: '.html'
|
extname: '.html'
|
||||||
})
|
})
|
||||||
const tpls = await engine.parseFileSync('jest')
|
const tpls = await engine.parseFileSync('jest')
|
||||||
|
|||||||
+1
-3
@@ -9,9 +9,7 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"downlevelIteration": true,
|
"strict": true
|
||||||
"strict": true,
|
|
||||||
"suppressImplicitAnyIndexErrors": true
|
|
||||||
},
|
},
|
||||||
"all": true,
|
"all": true,
|
||||||
"exclude": [ "node_modules", "dist", "demo", "test" ]
|
"exclude": [ "node_modules", "dist", "demo", "test" ]
|
||||||
|
|||||||
Reference in New Issue
Block a user