Skip to content

Commit 4289b4e

Browse files
committed
refactor: rename toValue to toValueSync
1 parent c3e51ca commit 4289b4e

File tree

7 files changed

+77
-37
lines changed

7 files changed

+77
-37
lines changed

src/liquid.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { TagMap } from './template/tag/tag-map'
1212
import { FilterMap } from './template/filter/filter-map'
1313
import { LiquidOptions, normalizeDirectoryList, NormalizedFullOptions, normalize, RenderOptions } from './liquid-options'
1414
import { FilterImplOptions } from './template/filter/filter-impl-options'
15-
import { toPromise, toValue } from './util/async'
15+
import { toPromise, toValueSync } from './util/async'
1616

1717
export * from './util/error'
1818
export * from './types'
@@ -47,7 +47,7 @@ export class Liquid {
4747
return toPromise(this._render(tpl, scope, { ...renderOptions, sync: false }))
4848
}
4949
public renderSync (tpl: Template[], scope?: object, renderOptions?: RenderOptions): any {
50-
return toValue(this._render(tpl, scope, { ...renderOptions, sync: true }))
50+
return toValueSync(this._render(tpl, scope, { ...renderOptions, sync: true }))
5151
}
5252
public renderToNodeStream (tpl: Template[], scope?: object, renderOptions: RenderOptions = {}): NodeJS.ReadableStream {
5353
const ctx = new Context(scope, this.options, renderOptions)
@@ -62,7 +62,7 @@ export class Liquid {
6262
return toPromise(this._parseAndRender(html, scope, { ...renderOptions, sync: false }))
6363
}
6464
public parseAndRenderSync (html: string, scope?: object, renderOptions?: RenderOptions): any {
65-
return toValue(this._parseAndRender(html, scope, { ...renderOptions, sync: true }))
65+
return toValueSync(this._parseAndRender(html, scope, { ...renderOptions, sync: true }))
6666
}
6767

6868
public _parsePartialFile (file: string, sync?: boolean, currentFile?: string) {
@@ -75,7 +75,7 @@ export class Liquid {
7575
return toPromise<Template[]>(this.parser.parseFile(file, false))
7676
}
7777
public parseFileSync (file: string): Template[] {
78-
return toValue<Template[]>(this.parser.parseFile(file, true))
78+
return toValueSync<Template[]>(this.parser.parseFile(file, true))
7979
}
8080
public async renderFile (file: string, ctx?: object, renderOptions?: RenderOptions) {
8181
const templates = await this.parseFile(file)
@@ -98,7 +98,7 @@ export class Liquid {
9898
return toPromise(this._evalValue(str, ctx))
9999
}
100100
public evalValueSync (str: string, ctx: Context): any {
101-
return toValue(this._evalValue(str, ctx))
101+
return toValueSync(this._evalValue(str, ctx))
102102
}
103103

104104
public registerFilter (name: string, filter: FilterImplOptions) {

src/util/async.ts

+10-25
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,8 @@
1-
import { isFunction } from './underscore'
2-
3-
type resolver = (x?: any) => any
4-
5-
export interface Thenable<T> {
6-
then (resolve: resolver, reject?: resolver): Thenable<T>;
7-
catch (reject: resolver): Thenable<T>;
8-
}
9-
10-
function isThenable<T> (val: any): val is Thenable<T> {
11-
return val && isFunction(val.then)
12-
}
13-
14-
function isAsyncIterator (val: any): val is IterableIterator<any> {
15-
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
16-
}
1+
import { isPromise, isIterator } from './underscore'
172

183
// convert an async iterator to a Promise
19-
export async function toPromise<T> (val: Generator<unknown, T, unknown> | Thenable<T> | T): Promise<T> {
20-
if (!isAsyncIterator(val)) return val
4+
export async function toPromise<T> (val: Generator<unknown, T, unknown> | Promise<T> | T): Promise<T> {
5+
if (!isIterator(val)) return val
216
let value: unknown
227
let done = false
238
let next = 'next'
@@ -27,8 +12,8 @@ export async function toPromise<T> (val: Generator<unknown, T, unknown> | Thenab
2712
value = state.value
2813
next = 'next'
2914
try {
30-
if (isAsyncIterator(value)) value = toPromise(value)
31-
if (isThenable(value)) value = await value
15+
if (isIterator(value)) value = toPromise(value)
16+
if (isPromise(value)) value = await value
3217
} catch (err) {
3318
next = 'throw'
3419
value = err
@@ -37,9 +22,9 @@ export async function toPromise<T> (val: Generator<unknown, T, unknown> | Thenab
3722
return value as T
3823
}
3924

40-
// convert an async iterator to a value in a synchronous maner
41-
export function toValue<T> (val: Generator<unknown, T, unknown> | T): T {
42-
if (!isAsyncIterator(val)) return val
25+
// convert an async iterator to a value in a synchronous manner
26+
export function toValueSync<T> (val: Generator<unknown, T, unknown> | T): T {
27+
if (!isIterator(val)) return val
4328
let value: any
4429
let done = false
4530
let next = 'next'
@@ -48,9 +33,9 @@ export function toValue<T> (val: Generator<unknown, T, unknown> | T): T {
4833
done = state.done
4934
value = state.value
5035
next = 'next'
51-
if (isAsyncIterator(value)) {
36+
if (isIterator(value)) {
5237
try {
53-
value = toValue(value)
38+
value = toValueSync(value)
5439
} catch (err) {
5540
next = 'throw'
5641
value = err

src/util/underscore.ts

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ export function isFunction (value: any): value is Function {
1414
return typeof value === 'function'
1515
}
1616

17+
export function isPromise<T> (val: any): val is Promise<T> {
18+
return val && isFunction(val.then)
19+
}
20+
21+
export function isIterator (val: any): val is IterableIterator<any> {
22+
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
23+
}
24+
1725
export function escapeRegex (str: string) {
1826
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
1927
}

test/integration/liquid/liquid.ts

+27
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,33 @@ describe('Liquid', function () {
5858
expect(html).to.equal('FOO')
5959
})
6060
})
61+
describe('#parseAndRenderSync', function () {
62+
const engine = new Liquid()
63+
it('should parse and render variable output', function () {
64+
const html = engine.parseAndRenderSync('{{"foo"}}')
65+
expect(html).to.equal('foo')
66+
})
67+
it('should parse and render complex output', function () {
68+
const tpl = '{{ "Welcome|to]Liquid" | split: "|" | join: "("}}'
69+
const html = engine.parseAndRenderSync(tpl)
70+
expect(html).to.equal('Welcome(to]Liquid')
71+
})
72+
it('should support for-in with variable', function () {
73+
const src = '{% assign total = 3 | minus: 1 %}' +
74+
'{% for i in (1..total) %}{{ i }}{% endfor %}'
75+
const html = engine.parseAndRenderSync(src, {})
76+
return expect(html).to.equal('12')
77+
})
78+
it('should support `globals` render option', function () {
79+
const src = '{{ foo }}'
80+
const html = engine.parseAndRenderSync(src, {}, { globals: { foo: 'FOO' } })
81+
return expect(html).to.equal('FOO')
82+
})
83+
it('should support `strictVariables` render option', function () {
84+
const src = '{{ foo }}'
85+
return expect(() => engine.parseAndRenderSync(src, {}, { strictVariables: true })).throw(/undefined variable/)
86+
})
87+
})
6188
describe('#express()', function () {
6289
const liquid = new Liquid({ root: '/root' })
6390
const render = liquid.express()

test/unit/render/expression.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Tokenizer } from '../../../src/parser/tokenizer'
22
import { expect } from 'chai'
33
import { Drop } from '../../../src/drop/drop'
44
import { Context } from '../../../src/context/context'
5-
import { toPromise } from '../../../src/util/async'
5+
import { toPromise, toValueSync } from '../../../src/util/async'
66
import { defaultOperators } from '../../../src/render/operator'
77
import { createTrie } from '../../../src/util/operator-trie'
88

@@ -158,4 +158,21 @@ describe('Expression', function () {
158158
expect(await toPromise(create('obj[keys["what\'s this"]]').evaluate(ctx, false))).to.equal('FOO')
159159
})
160160
})
161+
162+
describe('sync', function () {
163+
it('should eval literal', function () {
164+
expect(toValueSync(create('2.4').evaluate(ctx, false))).to.equal(2.4)
165+
})
166+
it('should return false for "1==2"', () => {
167+
expect(toValueSync(create('1==2').evaluate(ctx, false))).to.equal(false)
168+
})
169+
it('should escape quote', function () {
170+
const ctx = new Context({ quote: '"' })
171+
expect(toValueSync(create('"\\"" == quote').evaluate(ctx, false))).to.equal(true)
172+
})
173+
it('should allow nested property access', function () {
174+
const ctx = new Context({ obj: { foo: 'FOO' }, keys: { "what's this": 'foo' } })
175+
expect(toValueSync(create('obj[keys["what\'s this"]]').evaluate(ctx, false))).to.equal('FOO')
176+
})
177+
})
161178
})

test/unit/template/filter/filter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@ describe('filter', function () {
7676
it('should support key value pairs', async function () {
7777
filters.set('add', (a, b) => b[0] + ':' + (a + b[1]))
7878
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
79-
expect(await toPromise((filters.create('add', [['num', two]]).render(3, ctx)))).to.equal('num:5')
79+
expect(await toPromise(filters.create('add', [['num', two]]).render(3, ctx))).to.equal('num:5')
8080
})
8181
})

test/unit/util/async.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { toPromise, toValue } from '../../../src/util/async'
1+
import { toPromise, toValueSync } from '../../../src/util/async'
22
import { expect, use } from 'chai'
33
import * as chaiAsPromised from 'chai-as-promised'
44

@@ -75,15 +75,15 @@ describe('utils/async', () => {
7575
expect(ret).to.equal('barfoo')
7676
})
7777
})
78-
describe('#toValue()', function () {
78+
describe('#toValueSync()', function () {
7979
it('should throw Error if dependency throws syncly', () => {
8080
function * foo (): Generator<Generator<never>> {
8181
return yield bar()
8282
}
8383
function * bar (): Generator<never> {
8484
throw new Error('bar')
8585
}
86-
expect(() => toValue(foo())).to.throw('bar')
86+
expect(() => toValueSync(foo())).to.throw('bar')
8787
})
8888
it('should resume yield after catch', () => {
8989
function * foo (): Generator<unknown, never, never> {
@@ -95,7 +95,7 @@ describe('utils/async', () => {
9595
function * bar (): Generator<never> {
9696
throw new Error('bar')
9797
}
98-
expect(toValue(foo())).to.equal('foo')
98+
expect(toValueSync(foo())).to.equal('foo')
9999
})
100100
it('should resume return after catch', () => {
101101
function * foo (): Generator<Generator<never>, string> {
@@ -107,7 +107,10 @@ describe('utils/async', () => {
107107
function * bar (): Generator<never> {
108108
throw new Error('bar')
109109
}
110-
expect(toValue(foo())).to.equal('foo')
110+
expect(toValueSync(foo())).to.equal('foo')
111+
})
112+
it('should return non iterator value as it is', () => {
113+
expect(toValueSync('foo')).to.equal('foo')
111114
})
112115
})
113116
})

0 commit comments

Comments
 (0)