mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 12:20:40 -07:00
Added jsTruthy flag to constuctor in order to enable JS style truthiness evaluation
This commit is contained in:
@@ -65,6 +65,12 @@ Before 2.0.1, <code>extname</code> is set to `.liquid` by default. To change tha
|
||||
|
||||
**globals** is used to define global variables available to all templates even in cases of [render tag][render]. See [3185][185] for details.
|
||||
|
||||
## jsTruthy
|
||||
|
||||
**jsTruthy** is used to use standard Javascript truthiness rather than the Shopify.
|
||||
|
||||
it defaults to false. For example, when set to true, a blank string would evaluate to false with jsTruthy. With Shopify's truthiness, a blank string is true.
|
||||
|
||||
## Trimming
|
||||
|
||||
**greedy**, **trimOutputLeft**, **trimOutputRight**, **trimTagLeft**, **trimTagRight** options are used to eliminate extra newlines and indents in templates arround Liquid Constructs. See [Whitespace Control][wc] for details.
|
||||
|
||||
@@ -36,7 +36,7 @@ export function slice<T> (v: T[], begin: number, length = 1): T[] {
|
||||
export function where<T extends object> (this: FilterImpl, arr: T[], property: string, expected?: any): T[] {
|
||||
return toArray(arr).filter(obj => {
|
||||
const value = this.context.getFromScope(obj, String(property).split('.'))
|
||||
return expected === undefined ? isTruthy(value) : value === expected
|
||||
return expected === undefined ? isTruthy(value, this.context) : value === expected
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { isFalsy } from '../../render/boolean'
|
||||
import { isArray, isString, toValue } from '../../util/underscore'
|
||||
import { FilterImpl } from '../../template/filter/filter-impl'
|
||||
|
||||
export function Default<T1, T2> (v: string | T1, arg: T2): string | T1 | T2 {
|
||||
export function Default<T1, T2> (this: FilterImpl, v: string | T1, arg: T2): string | T1 | T2 {
|
||||
if (isArray(v) || isString(v)) return v.length ? v : arg
|
||||
return isFalsy(toValue(v)) ? arg : v
|
||||
return isFalsy(toValue(v), this.context) ? arg : v
|
||||
}
|
||||
export function json (v: any) {
|
||||
return JSON.stringify(v)
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
|
||||
for (const branch of this.branches) {
|
||||
const cond = yield new Expression(branch.cond).value(ctx)
|
||||
if (isTruthy(cond)) {
|
||||
if (isTruthy(cond, ctx)) {
|
||||
yield r.renderTemplates(branch.templates, ctx, emitter)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
render: function * (ctx: Context, emitter: Emitter) {
|
||||
const r = this.liquid.renderer
|
||||
const cond = yield new Expression(this.cond).value(ctx)
|
||||
yield (isFalsy(cond)
|
||||
yield (isFalsy(cond, ctx)
|
||||
? r.renderTemplates(this.templates, ctx, emitter)
|
||||
: r.renderTemplates(this.elseTemplates, ctx, emitter))
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface LiquidOptions {
|
||||
extname?: string;
|
||||
/** Whether or not to cache resolved templates. Defaults to `false`. */
|
||||
cache?: boolean | number | Cache<Template[]>;
|
||||
/** Use Javascript Truthiness.Defaults to `false`. */
|
||||
jsTruthy?: boolean;
|
||||
/** If set, treat the `filepath` parameter in `{%include filepath %}` and `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`. */
|
||||
dynamicPartials?: boolean;
|
||||
/** Whether or not to assert filter existence. If set to `false`, undefined filters will be skipped. Otherwise, undefined filters will cause an exception. Defaults to `false`. */
|
||||
@@ -50,6 +52,7 @@ export interface NormalizedFullOptions extends NormalizedOptions {
|
||||
root: string[];
|
||||
extname: string;
|
||||
cache: undefined | Cache<Template[]>;
|
||||
jsTruthy: boolean;
|
||||
dynamicPartials: boolean;
|
||||
strictFilters: boolean;
|
||||
strictVariables: boolean;
|
||||
@@ -70,6 +73,7 @@ export const defaultOptions: NormalizedFullOptions = {
|
||||
cache: undefined,
|
||||
extname: '',
|
||||
dynamicPartials: true,
|
||||
jsTruthy: false,
|
||||
trimTagRight: false,
|
||||
trimTagLeft: false,
|
||||
trimOutputRight: false,
|
||||
|
||||
+13
-4
@@ -1,6 +1,15 @@
|
||||
export function isTruthy (val: any): boolean {
|
||||
return !isFalsy(val)
|
||||
import { Context } from '../context/context'
|
||||
|
||||
export function isTruthy (val: any, ctx: Context): boolean {
|
||||
if ((val === null) && ctx && ctx.opts && ctx.opts.jsTruthy ) return false;
|
||||
return !isFalsy(val, ctx)
|
||||
}
|
||||
export function isFalsy (val: any): boolean {
|
||||
return val === false || undefined === val || val === null
|
||||
|
||||
export function isFalsy (val: any, ctx: Context): boolean {
|
||||
if(ctx && ctx.opts && ctx.opts.jsTruthy) {
|
||||
return val == false
|
||||
}
|
||||
else {
|
||||
return val === false || undefined === val || val === null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class Expression {
|
||||
if (TypeGuards.isOperatorToken(token)) {
|
||||
const r = this.operands.pop()
|
||||
const l = this.operands.pop()
|
||||
const result = evalOperatorToken(token, l, r)
|
||||
const result = evalOperatorToken(token, l, r, ctx)
|
||||
this.operands.push(result)
|
||||
} else {
|
||||
this.operands.push(evalToken(token, ctx))
|
||||
@@ -62,9 +62,9 @@ export function evalQuotedToken (token: QuotedToken) {
|
||||
return parseStringLiteral(token.getText())
|
||||
}
|
||||
|
||||
function evalOperatorToken (token: OperatorToken, lhs: any, rhs: any) {
|
||||
function evalOperatorToken (token: OperatorToken, lhs: any, rhs: any, ctx: Context) {
|
||||
const impl = operatorImpls[token.operator]
|
||||
return impl(lhs, rhs)
|
||||
return impl(lhs, rhs, ctx)
|
||||
}
|
||||
|
||||
function evalLiteralToken (token: LiteralToken) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { isComparable } from '../drop/comparable'
|
||||
import { Context } from '../context/context'
|
||||
import { isFunction } from '../util/underscore'
|
||||
import { isTruthy } from '../render/boolean'
|
||||
|
||||
export const operatorImpls: {[key: string]: (lhs: any, rhs: any) => boolean} = {
|
||||
|
||||
export const operatorImpls: {[key: string]: (lhs: any, rhs: any, ctx: Context) => boolean} = {
|
||||
'==': (l: any, r: any) => {
|
||||
if (isComparable(l)) return l.equals(r)
|
||||
if (isComparable(r)) return r.equals(l)
|
||||
@@ -36,6 +38,6 @@ export const operatorImpls: {[key: string]: (lhs: any, rhs: any) => boolean} = {
|
||||
'contains': (l: any, r: any) => {
|
||||
return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false
|
||||
},
|
||||
'and': (l: any, r: any) => isTruthy(l) && isTruthy(r),
|
||||
'or': (l: any, r: any) => isTruthy(l) || isTruthy(r)
|
||||
'and': (l: any, r: any, ctx: Context) => isTruthy(l, ctx) && isTruthy(r, ctx),
|
||||
'or': (l: any, r: any, ctx: Context) => isTruthy(l, ctx) || isTruthy(r, ctx)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isTruthy } from '../../../src/render/boolean'
|
||||
import { isTruthy, isFalsy } from '../../../src/render/boolean'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('boolean', async function () {
|
||||
describe('boolean Shopify', async function () {
|
||||
describe('.isTruthy()', async function () {
|
||||
// Spec: https://shopify.github.io/liquid/basics/truthy-and-falsy/
|
||||
it('true is truthy', function () {
|
||||
@@ -36,3 +36,53 @@ describe('boolean', async function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('boolean jsTruthy', async function () {
|
||||
const ctx = {
|
||||
opts: {
|
||||
jsTruthy: true
|
||||
}
|
||||
}
|
||||
|
||||
describe('.isFalsy()', async function () {
|
||||
it('null is always falsy', function () {
|
||||
expect(isFalsy(null, ctx)).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('.isTruthy()', async function () {
|
||||
it('true is truthy', function () {
|
||||
expect(isTruthy(true, ctx)).to.be.true
|
||||
})
|
||||
it('false is falsy', function () {
|
||||
expect(isTruthy(false, ctx)).to.be.false
|
||||
})
|
||||
it('null is always falsy', function () {
|
||||
expect(isTruthy(null, ctx)).to.be.false
|
||||
})
|
||||
it('null is always falsy', function () {
|
||||
expect(isTruthy(null, ctx)).to.be.false
|
||||
})
|
||||
it('"foo" is truthy', function () {
|
||||
expect(isTruthy('foo', ctx)).to.be.true
|
||||
})
|
||||
it('"" is falsy', function () {
|
||||
expect(isTruthy('', ctx)).to.be.false
|
||||
})
|
||||
it('0 is falsy', function () {
|
||||
expect(isTruthy(0, ctx)).to.be.false
|
||||
})
|
||||
it('1 is truthy', function () {
|
||||
expect(isTruthy(1, ctx)).to.be.true
|
||||
})
|
||||
it('1.1 is truthy', function () {
|
||||
expect(isTruthy(1.1, ctx)).to.be.true
|
||||
})
|
||||
it('[1] is truthy', function () {
|
||||
expect(isTruthy([1], ctx)).to.be.true
|
||||
})
|
||||
it('[] is falsy', function () {
|
||||
expect(isTruthy([], ctx)).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user