diff --git a/src/context/context.ts b/src/context/context.ts index a0f729b59..c12c0269a 100644 --- a/src/context/context.ts +++ b/src/context/context.ts @@ -3,6 +3,7 @@ import { __assign } from 'tslib' import { NormalizedFullOptions, defaultOptions } from '../liquid-options' import { Scope } from './scope' import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore' +import { InternalUndefinedVariableError } from '../util/error' export class Context { private scopes: Scope[] = [{}] @@ -42,7 +43,7 @@ export class Context { return paths.reduce((scope, path) => { scope = readProperty(scope, path) if (isNil(scope) && this.opts.strictVariables) { - throw new TypeError(`undefined variable: ${path}`) + throw new InternalUndefinedVariableError(path) } return scope }, scope) diff --git a/src/render/expression.ts b/src/render/expression.ts index a2a2b0955..c97c28b27 100644 --- a/src/render/expression.ts +++ b/src/render/expression.ts @@ -12,6 +12,7 @@ import { Context } from '../context/context' import { range, toValue } from '../util/underscore' import { Tokenizer } from '../parser/tokenizer' import { operatorImpls } from '../render/operator' +import { UndefinedVariableError, InternalUndefinedVariableError } from '../util/error' export class Expression { private operands: any[] = [] @@ -49,12 +50,10 @@ export function evalToken (token: Token | undefined, ctx: Context, lenient: bool try { return ctx.get([variable, ...props]) } catch (e) { - // for lenient, we catch the error thrown by Context.getFromScope() for undefined vars. - // Alt, we could make this more robust by setting a flag or using a separate error class. - if (lenient && e instanceof TypeError && e.message.startsWith("undefined variable:")) { + if (lenient && e instanceof InternalUndefinedVariableError) { return null } else { - throw(e) + throw(new UndefinedVariableError(e, token)) } } } diff --git a/src/util/error.ts b/src/util/error.ts index f0a95ccec..f7a96776d 100644 --- a/src/util/error.ts +++ b/src/util/error.ts @@ -48,6 +48,27 @@ export class RenderError extends LiquidError { } } +export class UndefinedVariableError extends LiquidError { + public constructor (err: Error, token: Token) { + super(err, token) + this.name = 'UndefinedVariableError' + this.message = err.message + super.update() + } +} + +// only used internally; raised where we don't have token information, +// so it can't be an UndefinedVariableError. +export class InternalUndefinedVariableError extends Error { + variableName: string + + public constructor (variableName: string) { + super(`undefined variable: ${variableName}`) + this.name = 'InternalUndefinedVariableError' + this.variableName = variableName + } +} + export class AssertionError extends Error { public constructor (message: string) { super(message)