Skip to content

Commit 2e5ab98

Browse files
committed
feat: export toPromise and toValue, see #158
1 parent 0dbb779 commit 2e5ab98

File tree

5 files changed

+33
-17
lines changed

5 files changed

+33
-17
lines changed

src/liquid.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { FilterMap } from './template/filter/filter-map'
1414
import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefault, normalize } from './liquid-options'
1515
import { FilterImplOptions } from './template/filter/filter-impl-options'
1616
import { FS } from './fs/fs'
17-
import { toThenable, toValue } from './util/async'
17+
import { toPromise, toValue } from './util/async'
1818

1919
export * from './types'
2020

@@ -49,7 +49,7 @@ export class Liquid {
4949
return this.renderer.renderTemplates(tpl, ctx)
5050
}
5151
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<string> {
52-
return toThenable(this._render(tpl, scope, opts, false))
52+
return toPromise(this._render(tpl, scope, opts, false))
5353
}
5454
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): string {
5555
return toValue(this._render(tpl, scope, opts, true))
@@ -60,7 +60,7 @@ export class Liquid {
6060
return this._render(tpl, scope, opts, sync)
6161
}
6262
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<string> {
63-
return toThenable(this._parseAndRender(html, scope, opts, false))
63+
return toPromise(this._parseAndRender(html, scope, opts, false))
6464
}
6565
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): string {
6666
return toValue(this._parseAndRender(html, scope, opts, true))
@@ -88,7 +88,7 @@ export class Liquid {
8888
throw this.lookupError(file, options.root)
8989
}
9090
public async parseFile (file: string, opts?: LiquidOptions): Promise<Template[]> {
91-
return toThenable(this._parseFile(file, opts, false))
91+
return toPromise(this._parseFile(file, opts, false))
9292
}
9393
public parseFileSync (file: string, opts?: LiquidOptions): Template[] {
9494
return toValue(this._parseFile(file, opts, true))
@@ -108,7 +108,7 @@ export class Liquid {
108108
return value.value(ctx)
109109
}
110110
public async evalValue (str: string, ctx: Context): Promise<any> {
111-
return toThenable(this._evalValue(str, ctx))
111+
return toPromise(this._evalValue(str, ctx))
112112
}
113113
public evalValueSync (str: string, ctx: Context): any {
114114
return toValue(this._evalValue(str, ctx))

src/template/tag/hash.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class Hash {
2121
* render (ctx: Context) {
2222
const hash = {}
2323
for (const key of Object.keys(this.hash)) {
24-
hash[key] = evalToken(this.hash[key], ctx)
24+
hash[key] = yield evalToken(this.hash[key], ctx)
2525
}
2626
return hash
2727
}

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ export { TopLevelToken } from './tokens/toplevel-token'
1717
export { Tokenizer } from './parser/tokenizer'
1818
export { Hash } from './template/tag/hash'
1919
export { evalToken, evalQuotedToken } from './render/expression'
20+
export { toPromise, toThenable, toValue } from './util/async'

src/util/async.ts

+16-10
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ interface Thenable {
77
catch (reject: resolver): Thenable;
88
}
99

10-
function mkResolve (value: any) {
10+
function createResolvedThenable (value: any): Thenable {
1111
const ret = {
1212
then: (resolve: resolver) => resolve(value),
1313
catch: () => ret
1414
}
1515
return ret
1616
}
1717

18-
function mkReject (err: Error) {
18+
function createRejectedThenable (err: Error): Thenable {
1919
const ret = {
2020
then: (resolve: resolver, reject?: resolver) => {
2121
if (reject) return reject(err)
@@ -30,43 +30,49 @@ function isThenable (val: any): val is Thenable {
3030
return val && isFunction(val.then)
3131
}
3232

33-
function isCustomIterable (val: any): val is IterableIterator<any> {
33+
function isAsyncIterator (val: any): val is IterableIterator<any> {
3434
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
3535
}
3636

37+
// convert an async iterator to a thenable (Promise compatible)
3738
export function toThenable (val: IterableIterator<any> | Thenable | any): Thenable {
3839
if (isThenable(val)) return val
39-
if (isCustomIterable(val)) return reduce()
40-
return mkResolve(val)
40+
if (isAsyncIterator(val)) return reduce()
41+
return createResolvedThenable(val)
4142

4243
function reduce (prev?: any): Thenable {
4344
let state
4445
try {
4546
state = (val as IterableIterator<any>).next(prev)
4647
} catch (err) {
47-
return mkReject(err)
48+
return createRejectedThenable(err)
4849
}
4950

50-
if (state.done) return mkResolve(state.value)
51+
if (state.done) return createResolvedThenable(state.value)
5152
return toThenable(state.value!).then(reduce, err => {
5253
let state
5354
try {
5455
state = (val as IterableIterator<any>).throw!(err)
5556
} catch (e) {
56-
return mkReject(e)
57+
return createRejectedThenable(e)
5758
}
58-
if (state.done) return mkResolve(state.value)
59+
if (state.done) return createResolvedThenable(state.value)
5960
return reduce(state.value)
6061
})
6162
}
6263
}
6364

65+
export function toPromise (val: IterableIterator<any> | Thenable | any): Promise<any> {
66+
return Promise.resolve(toThenable(val))
67+
}
68+
69+
// get the value of async iterator in synchronous manner
6470
export function toValue (val: IterableIterator<any> | Thenable | any) {
6571
let ret: any
6672
toThenable(val)
6773
.then((x: any) => {
6874
ret = x
69-
return mkResolve(ret)
75+
return createResolvedThenable(ret)
7076
})
7177
.catch((err: Error) => {
7278
throw err

test/unit/util/async.ts

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

55
use(chaiAsPromised)
66

77
describe('utils/async', () => {
8+
describe('#toPromise()', function () {
9+
it('should return a promise', async () => {
10+
function * foo () {
11+
return 'foo'
12+
}
13+
const result = await toPromise(foo())
14+
expect(result).to.equal('foo')
15+
})
16+
})
817
describe('#toThenable()', function () {
918
it('should support iterable with single return statement', async () => {
1019
function * foo () {

0 commit comments

Comments
 (0)