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