Skip to main content

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.

template
Hello {{ nosuchthing }}
{% for thing in nosuchthing %}
{{ thing }}
{% endfor %}
output
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