Undefined
LiquidScript does not have a "lax" mode like some Liquid engines, but we can control what happens if a template author attempts to use an undefined variable or filter.
Undefined Variables
When rendering a Liquid template, if a variable name can not be resolved, an instance of Undefined
is used instead. We can customize template rendering behavior by supplying an Undefined
factory function to the Environment
constructor or Template.fromString()
.
import { Environment, StrictUndefined } from "liquidsscript";
const env = new Environment({ undefinedFactory: StrictUndefined.from });
env.fromString("{{ nosuchthing }}").renderSync();
// LiquidUndefinedError: 'nosuchthing' is undefined (<string>:1)
Built-in Undefined
types are LaxUndefined
(the default), StrictUndefined
and FalsyStrictUndefined
.
Default Undefined
All operations on the default Undefined
type are silently ignored and, when rendered, it produces an empty string. For example, you can access properties and iterate an undefined variable without error.
Hello {{ nosuchthing }}
{% for thing in nosuchthing %}
{{ thing }}
{% endfor %}
Hello
Strict Undefined
Given StrictUndefined.from
as the undefinedFactory
option to an environment or Template.fromString()
, any operation on an undefined variable will raise a LiquidUndefinedError
.
import { Environment, StrictUndefined } from "liquidscript";
const env = new Environment({ undefinedFactory: StrictUndefined.from });
env.fromString("{{ nosuchthing }}").renderSync();
// LiquidUndefinedError: 'nosuchthing' is undefined (<string>:1)
Note that the "standard" default
filter does not handle undefined values the way you might expect. The following example will raise a LiquidUndefinedError
if username is undefined.
Hello {{ username | default: "user" }}
Similarly, standard {% if %}
expressions do not allow you to detect undefined values. See Shopify Liquid issue #1034.
import { Environment, StrictUndefined } from "liquidscript";
const env = new Environment({ undefinedFactory: StrictUndefined.from });
env
.fromString("{% if nosuchthing %}true{% else %}false{% endif %}")
.renderSync();
// LiquidUndefinedError: 'nosuchthing' is undefined (<string>:1)
Falsy Undefined
FalsyStrictUndefined
addresses the issues with StrictUndefined
described above. Given FalsyStrictUndefined.from
as the undefinedFactory
option to an environment or Template.fromString()
, undefined values can be tested for truthiness and compared to other values in an if
/unless
expression without throwing an error. An UndefinedError
will be thrown when an undefined value is iterated, output or when accessing its properties.
import { Environment, FalsyStrictUndefined } from "liquidscript";
const env = new Environment({ undefinedFactory: FalsyStrictUndefined.from });
env
.fromString("{% if nosuchthing %}true{% else %}false{% endif %}")
.renderSync();
// false
env.fromString("{{ nosuchthing | default: 'hello' }}").renderSync();
// hello
env.fromString("{{ nosuchthing }}").renderSync();
// LiquidUndefinedError: 'nosuchthing' is undefined (<string>:1)
Undefined Filters
By default, attempts to use an undefined filter will raise a NoSuchFilterError
.
import { Environment } from "liquidscript";
const env = new Environment();
env.fromString("{{ 'hello' | camel_case }}").renderSync();
// NoSuchFilterError: unknown filter camel_case (<string>:1)
Set the strictFilters
option on the Environment
constructor or Template.fromString
to false
, and undefined filters will be silently ignored.
import { Environment } from "liquidscript";
const env = new Environment({ strictFilters: false });
console.log(env.fromString("{{ 'hello' | camel_case }}").renderSync());
// hello