mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-19 06:20:38 -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.
|
**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
|
## 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.
|
**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[] {
|
export function where<T extends object> (this: FilterImpl, arr: T[], property: string, expected?: any): T[] {
|
||||||
return toArray(arr).filter(obj => {
|
return toArray(arr).filter(obj => {
|
||||||
const value = this.context.getFromScope(obj, String(property).split('.'))
|
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 { isFalsy } from '../../render/boolean'
|
||||||
import { isArray, isString, toValue } from '../../util/underscore'
|
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
|
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) {
|
export function json (v: any) {
|
||||||
return JSON.stringify(v)
|
return JSON.stringify(v)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default {
|
|||||||
|
|
||||||
for (const branch of this.branches) {
|
for (const branch of this.branches) {
|
||||||
const cond = yield new Expression(branch.cond).value(ctx)
|
const cond = yield new Expression(branch.cond).value(ctx)
|
||||||
if (isTruthy(cond)) {
|
if (isTruthy(cond, ctx)) {
|
||||||
yield r.renderTemplates(branch.templates, ctx, emitter)
|
yield r.renderTemplates(branch.templates, ctx, emitter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default {
|
|||||||
render: function * (ctx: Context, emitter: Emitter) {
|
render: function * (ctx: Context, emitter: Emitter) {
|
||||||
const r = this.liquid.renderer
|
const r = this.liquid.renderer
|
||||||
const cond = yield new Expression(this.cond).value(ctx)
|
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.templates, ctx, emitter)
|
||||||
: r.renderTemplates(this.elseTemplates, ctx, emitter))
|
: r.renderTemplates(this.elseTemplates, ctx, emitter))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export interface LiquidOptions {
|
|||||||
extname?: string;
|
extname?: string;
|
||||||
/** Whether or not to cache resolved templates. Defaults to `false`. */
|
/** Whether or not to cache resolved templates. Defaults to `false`. */
|
||||||
cache?: boolean | number | Cache<Template[]>;
|
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`. */
|
/** 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;
|
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`. */
|
/** 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[];
|
root: string[];
|
||||||
extname: string;
|
extname: string;
|
||||||
cache: undefined | Cache<Template[]>;
|
cache: undefined | Cache<Template[]>;
|
||||||
|
jsTruthy: boolean;
|
||||||
dynamicPartials: boolean;
|
dynamicPartials: boolean;
|
||||||
strictFilters: boolean;
|
strictFilters: boolean;
|
||||||
strictVariables: boolean;
|
strictVariables: boolean;
|
||||||
@@ -70,6 +73,7 @@ export const defaultOptions: NormalizedFullOptions = {
|
|||||||
cache: undefined,
|
cache: undefined,
|
||||||
extname: '',
|
extname: '',
|
||||||
dynamicPartials: true,
|
dynamicPartials: true,
|
||||||
|
jsTruthy: false,
|
||||||
trimTagRight: false,
|
trimTagRight: false,
|
||||||
trimTagLeft: false,
|
trimTagLeft: false,
|
||||||
trimOutputRight: false,
|
trimOutputRight: false,
|
||||||
|
|||||||
+13
-4
@@ -1,6 +1,15 @@
|
|||||||
export function isTruthy (val: any): boolean {
|
import { Context } from '../context/context'
|
||||||
return !isFalsy(val)
|
|
||||||
|
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)) {
|
if (TypeGuards.isOperatorToken(token)) {
|
||||||
const r = this.operands.pop()
|
const r = this.operands.pop()
|
||||||
const l = 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)
|
this.operands.push(result)
|
||||||
} else {
|
} else {
|
||||||
this.operands.push(evalToken(token, ctx))
|
this.operands.push(evalToken(token, ctx))
|
||||||
@@ -62,9 +62,9 @@ export function evalQuotedToken (token: QuotedToken) {
|
|||||||
return parseStringLiteral(token.getText())
|
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]
|
const impl = operatorImpls[token.operator]
|
||||||
return impl(lhs, rhs)
|
return impl(lhs, rhs, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
function evalLiteralToken (token: LiteralToken) {
|
function evalLiteralToken (token: LiteralToken) {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { isComparable } from '../drop/comparable'
|
import { isComparable } from '../drop/comparable'
|
||||||
|
import { Context } from '../context/context'
|
||||||
import { isFunction } from '../util/underscore'
|
import { isFunction } from '../util/underscore'
|
||||||
import { isTruthy } from '../render/boolean'
|
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) => {
|
'==': (l: any, r: any) => {
|
||||||
if (isComparable(l)) return l.equals(r)
|
if (isComparable(l)) return l.equals(r)
|
||||||
if (isComparable(r)) return r.equals(l)
|
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) => {
|
'contains': (l: any, r: any) => {
|
||||||
return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false
|
return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false
|
||||||
},
|
},
|
||||||
'and': (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) => isTruthy(l) || isTruthy(r)
|
'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'
|
import { expect } from 'chai'
|
||||||
|
|
||||||
describe('boolean', async function () {
|
describe('boolean Shopify', async function () {
|
||||||
describe('.isTruthy()', async function () {
|
describe('.isTruthy()', async function () {
|
||||||
// Spec: https://shopify.github.io/liquid/basics/truthy-and-falsy/
|
// Spec: https://shopify.github.io/liquid/basics/truthy-and-falsy/
|
||||||
it('true is truthy', function () {
|
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