mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
feat: export toPromise and toValue, see #158
This commit is contained in:
+5
-5
@@ -14,7 +14,7 @@ import { FilterMap } from './template/filter/filter-map'
|
|||||||
import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefault, normalize } from './liquid-options'
|
import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefault, normalize } from './liquid-options'
|
||||||
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||||
import { FS } from './fs/fs'
|
import { FS } from './fs/fs'
|
||||||
import { toThenable, toValue } from './util/async'
|
import { toPromise, toValue } from './util/async'
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export class Liquid {
|
|||||||
return this.renderer.renderTemplates(tpl, ctx)
|
return this.renderer.renderTemplates(tpl, ctx)
|
||||||
}
|
}
|
||||||
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<string> {
|
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<string> {
|
||||||
return toThenable(this._render(tpl, scope, opts, false))
|
return toPromise(this._render(tpl, scope, opts, false))
|
||||||
}
|
}
|
||||||
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): string {
|
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): string {
|
||||||
return toValue(this._render(tpl, scope, opts, true))
|
return toValue(this._render(tpl, scope, opts, true))
|
||||||
@@ -60,7 +60,7 @@ export class Liquid {
|
|||||||
return this._render(tpl, scope, opts, sync)
|
return this._render(tpl, scope, opts, sync)
|
||||||
}
|
}
|
||||||
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<string> {
|
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<string> {
|
||||||
return toThenable(this._parseAndRender(html, scope, opts, false))
|
return toPromise(this._parseAndRender(html, scope, opts, false))
|
||||||
}
|
}
|
||||||
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): string {
|
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): string {
|
||||||
return toValue(this._parseAndRender(html, scope, opts, true))
|
return toValue(this._parseAndRender(html, scope, opts, true))
|
||||||
@@ -88,7 +88,7 @@ export class Liquid {
|
|||||||
throw this.lookupError(file, options.root)
|
throw this.lookupError(file, options.root)
|
||||||
}
|
}
|
||||||
public async parseFile (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
public async parseFile (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
||||||
return toThenable(this._parseFile(file, opts, false))
|
return toPromise(this._parseFile(file, opts, false))
|
||||||
}
|
}
|
||||||
public parseFileSync (file: string, opts?: LiquidOptions): Template[] {
|
public parseFileSync (file: string, opts?: LiquidOptions): Template[] {
|
||||||
return toValue(this._parseFile(file, opts, true))
|
return toValue(this._parseFile(file, opts, true))
|
||||||
@@ -108,7 +108,7 @@ export class Liquid {
|
|||||||
return value.value(ctx)
|
return value.value(ctx)
|
||||||
}
|
}
|
||||||
public async evalValue (str: string, ctx: Context): Promise<any> {
|
public async evalValue (str: string, ctx: Context): Promise<any> {
|
||||||
return toThenable(this._evalValue(str, ctx))
|
return toPromise(this._evalValue(str, ctx))
|
||||||
}
|
}
|
||||||
public evalValueSync (str: string, ctx: Context): any {
|
public evalValueSync (str: string, ctx: Context): any {
|
||||||
return toValue(this._evalValue(str, ctx))
|
return toValue(this._evalValue(str, ctx))
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class Hash {
|
|||||||
* render (ctx: Context) {
|
* render (ctx: Context) {
|
||||||
const hash = {}
|
const hash = {}
|
||||||
for (const key of Object.keys(this.hash)) {
|
for (const key of Object.keys(this.hash)) {
|
||||||
hash[key] = evalToken(this.hash[key], ctx)
|
hash[key] = yield evalToken(this.hash[key], ctx)
|
||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ export { TopLevelToken } from './tokens/toplevel-token'
|
|||||||
export { Tokenizer } from './parser/tokenizer'
|
export { Tokenizer } from './parser/tokenizer'
|
||||||
export { Hash } from './template/tag/hash'
|
export { Hash } from './template/tag/hash'
|
||||||
export { evalToken, evalQuotedToken } from './render/expression'
|
export { evalToken, evalQuotedToken } from './render/expression'
|
||||||
|
export { toPromise, toThenable, toValue } from './util/async'
|
||||||
|
|||||||
+16
-10
@@ -7,7 +7,7 @@ interface Thenable {
|
|||||||
catch (reject: resolver): Thenable;
|
catch (reject: resolver): Thenable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkResolve (value: any) {
|
function createResolvedThenable (value: any): Thenable {
|
||||||
const ret = {
|
const ret = {
|
||||||
then: (resolve: resolver) => resolve(value),
|
then: (resolve: resolver) => resolve(value),
|
||||||
catch: () => ret
|
catch: () => ret
|
||||||
@@ -15,7 +15,7 @@ function mkResolve (value: any) {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkReject (err: Error) {
|
function createRejectedThenable (err: Error): Thenable {
|
||||||
const ret = {
|
const ret = {
|
||||||
then: (resolve: resolver, reject?: resolver) => {
|
then: (resolve: resolver, reject?: resolver) => {
|
||||||
if (reject) return reject(err)
|
if (reject) return reject(err)
|
||||||
@@ -30,43 +30,49 @@ function isThenable (val: any): val is Thenable {
|
|||||||
return val && isFunction(val.then)
|
return val && isFunction(val.then)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCustomIterable (val: any): val is IterableIterator<any> {
|
function isAsyncIterator (val: any): val is IterableIterator<any> {
|
||||||
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
|
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert an async iterator to a thenable (Promise compatible)
|
||||||
export function toThenable (val: IterableIterator<any> | Thenable | any): Thenable {
|
export function toThenable (val: IterableIterator<any> | Thenable | any): Thenable {
|
||||||
if (isThenable(val)) return val
|
if (isThenable(val)) return val
|
||||||
if (isCustomIterable(val)) return reduce()
|
if (isAsyncIterator(val)) return reduce()
|
||||||
return mkResolve(val)
|
return createResolvedThenable(val)
|
||||||
|
|
||||||
function reduce (prev?: any): Thenable {
|
function reduce (prev?: any): Thenable {
|
||||||
let state
|
let state
|
||||||
try {
|
try {
|
||||||
state = (val as IterableIterator<any>).next(prev)
|
state = (val as IterableIterator<any>).next(prev)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return mkReject(err)
|
return createRejectedThenable(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.done) return mkResolve(state.value)
|
if (state.done) return createResolvedThenable(state.value)
|
||||||
return toThenable(state.value!).then(reduce, err => {
|
return toThenable(state.value!).then(reduce, err => {
|
||||||
let state
|
let state
|
||||||
try {
|
try {
|
||||||
state = (val as IterableIterator<any>).throw!(err)
|
state = (val as IterableIterator<any>).throw!(err)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return mkReject(e)
|
return createRejectedThenable(e)
|
||||||
}
|
}
|
||||||
if (state.done) return mkResolve(state.value)
|
if (state.done) return createResolvedThenable(state.value)
|
||||||
return reduce(state.value)
|
return reduce(state.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toPromise (val: IterableIterator<any> | Thenable | any): Promise<any> {
|
||||||
|
return Promise.resolve(toThenable(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the value of async iterator in synchronous manner
|
||||||
export function toValue (val: IterableIterator<any> | Thenable | any) {
|
export function toValue (val: IterableIterator<any> | Thenable | any) {
|
||||||
let ret: any
|
let ret: any
|
||||||
toThenable(val)
|
toThenable(val)
|
||||||
.then((x: any) => {
|
.then((x: any) => {
|
||||||
ret = x
|
ret = x
|
||||||
return mkResolve(ret)
|
return createResolvedThenable(ret)
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
throw err
|
throw err
|
||||||
|
|||||||
+10
-1
@@ -1,10 +1,19 @@
|
|||||||
import { toThenable, toValue } from '../../../src/util/async'
|
import { toThenable, toPromise, toValue } from '../../../src/util/async'
|
||||||
import { expect, use } from 'chai'
|
import { expect, use } from 'chai'
|
||||||
import * as chaiAsPromised from 'chai-as-promised'
|
import * as chaiAsPromised from 'chai-as-promised'
|
||||||
|
|
||||||
use(chaiAsPromised)
|
use(chaiAsPromised)
|
||||||
|
|
||||||
describe('utils/async', () => {
|
describe('utils/async', () => {
|
||||||
|
describe('#toPromise()', function () {
|
||||||
|
it('should return a promise', async () => {
|
||||||
|
function * foo () {
|
||||||
|
return 'foo'
|
||||||
|
}
|
||||||
|
const result = await toPromise(foo())
|
||||||
|
expect(result).to.equal('foo')
|
||||||
|
})
|
||||||
|
})
|
||||||
describe('#toThenable()', function () {
|
describe('#toThenable()', function () {
|
||||||
it('should support iterable with single return statement', async () => {
|
it('should support iterable with single return statement', async () => {
|
||||||
function * foo () {
|
function * foo () {
|
||||||
|
|||||||
Reference in New Issue
Block a user