A simple, expressive and safe Shopify / Github Pages compatible template engine in pure JavaScript.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
LiquidJS provides comprehensive configuration options through the LiquidOptions interface, enabling fine-tuned control over template processing, file handling, performance limits, and output formatting.
Main configuration interface for customizing LiquidJS behavior.
interface LiquidOptions {
// File System Configuration
root?: string | string[];
partials?: string | string[];
layouts?: string | string[];
fs?: FS;
relativeReference?: boolean;
extname?: string;
templates?: {[key: string]: string};
// Template Processing
strictFilters?: boolean;
strictVariables?: boolean;
dynamicPartials?: boolean;
jekyllInclude?: boolean;
jekyllWhere?: boolean;
jsTruthy?: boolean;
lenientIf?: boolean;
ownPropertyOnly?: boolean;
catchAllErrors?: boolean;
// Output Control
outputEscape?: 'escape' | 'json' | ((value: any) => string);
keepOutputType?: boolean;
trimTagRight?: boolean;
trimTagLeft?: boolean;
trimOutputRight?: boolean;
trimOutputLeft?: boolean;
greedy?: boolean;
// Delimiters
tagDelimiterLeft?: string;
tagDelimiterRight?: string;
outputDelimiterLeft?: string;
outputDelimiterRight?: string;
// Internationalization
timezoneOffset?: number | string;
dateFormat?: string;
locale?: string;
preserveTimezones?: boolean;
// Caching
cache?: boolean | number | LiquidCache;
// Performance Limits
parseLimit?: number;
renderLimit?: number;
memoryLimit?: number;
// Advanced
globals?: object;
operators?: Operators;
keyValueSeparator?: string;
orderedFilterParameters?: boolean;
}Control how templates are loaded from the file system.
/**
* Root directories for template resolution
* Default: ["."]
*/
root?: string | string[];
/**
* Directories for partial template resolution
* Default: same as root
*/
partials?: string | string[];
/**
* Directories for layout template resolution
* Default: same as root
*/
layouts?: string | string[];
/**
* Custom file system implementation
*/
fs?: FS;
/**
* Allow relative pathname references
* Default: true
*/
relativeReference?: boolean;
/**
* Default file extension for template lookup
* Default: ""
*/
extname?: string;
/**
* In-memory template mapping (overrides file system)
*/
templates?: {[key: string]: string};Usage Examples:
import { Liquid } from "liquidjs";
// File system configuration
const engine = new Liquid({
root: ["./templates", "./shared"],
partials: "./partials",
layouts: "./layouts",
extname: ".liquid"
});
// In-memory templates
const memoryEngine = new Liquid({
templates: {
"header": "<h1>{{ title }}</h1>",
"footer": "<p>© {{ year }}</p>"
}
});Control template parsing and rendering behavior.
/**
* Assert filter existence (throw error for undefined filters)
* Default: false
*/
strictFilters?: boolean;
/**
* Assert variable existence (throw error for undefined variables)
* Default: false
*/
strictVariables?: boolean;
/**
* Treat include/layout filepath as variable
* Default: true
*/
dynamicPartials?: boolean;
/**
* Use Jekyll-style includes with parameter passing
* Default: false
*/
jekyllInclude?: boolean;
/**
* Enable Jekyll-style where filter for array matching
* Default: false
*/
jekyllWhere?: boolean;
/**
* Use JavaScript truthiness evaluation
* Default: false
*/
jsTruthy?: boolean;
/**
* Allow undefined variables in conditionals
* Default: false
*/
lenientIf?: boolean;
/**
* Hide prototype properties from scope
* Default: true
*/
ownPropertyOnly?: boolean;
/**
* Continue processing after errors instead of stopping
* Default: false
*/
catchAllErrors?: boolean;Usage Examples:
// Strict mode for development
const strictEngine = new Liquid({
strictFilters: true,
strictVariables: true,
catchAllErrors: false
});
// Lenient mode for production
const lenientEngine = new Liquid({
strictFilters: false,
strictVariables: false,
lenientIf: true,
catchAllErrors: true
});
// Jekyll compatibility
const jekyllEngine = new Liquid({
jekyllInclude: true,
jekyllWhere: true,
dynamicPartials: false
});Configure template output formatting and escaping.
/**
* Default escape function for all outputs
* Options: 'escape', 'json', or custom function
* Default: undefined (no escaping)
*/
outputEscape?: 'escape' | 'json' | ((value: any) => string);
/**
* Preserve original data types in output
* Default: false (convert to strings)
*/
keepOutputType?: boolean;
/**
* Trim whitespace after tags until newline
* Default: false
*/
trimTagRight?: boolean;
/**
* Trim whitespace before tags until newline
* Default: false
*/
trimTagLeft?: boolean;
/**
* Trim whitespace after outputs until newline
* Default: false
*/
trimOutputRight?: boolean;
/**
* Trim whitespace before outputs until newline
* Default: false
*/
trimOutputLeft?: boolean;
/**
* Aggressive whitespace trimming (all consecutive whitespace)
* Default: true
*/
greedy?: boolean;Usage Examples:
// HTML escaping by default
const htmlEngine = new Liquid({
outputEscape: 'escape'
});
// Custom escaping function
const customEngine = new Liquid({
outputEscape: (value) => String(value).replace(/</g, '<')
});
// Whitespace control
const cleanEngine = new Liquid({
trimTagLeft: true,
trimTagRight: true,
trimOutputLeft: true,
trimOutputRight: true,
greedy: true
});Customize tag and output delimiters.
/**
* Left delimiter for tags
* Default: "{%"
*/
tagDelimiterLeft?: string;
/**
* Right delimiter for tags
* Default: "%}"
*/
tagDelimiterRight?: string;
/**
* Left delimiter for outputs
* Default: "{{"
*/
outputDelimiterLeft?: string;
/**
* Right delimiter for outputs
* Default: "}}"
*/
outputDelimiterRight?: string;Usage Examples:
// Custom delimiters
const customDelimiters = new Liquid({
tagDelimiterLeft: "<%",
tagDelimiterRight: "%>",
outputDelimiterLeft: "<%=",
outputDelimiterRight: "%>"
});
// Templates now use: <% if condition %>...<%=%> value <%=>...<%/%>Configure performance and DoS protection limits.
/**
* Maximum template length to parse in one call
* Default: Infinity
*/
parseLimit?: number;
/**
* Maximum render time in milliseconds
* Default: Infinity
*/
renderLimit?: number;
/**
* Maximum memory usage for operations
* Default: Infinity
*/
memoryLimit?: number;Usage Examples:
// Production safety limits
const safeEngine = new Liquid({
parseLimit: 1_000_000, // 1MB template limit
renderLimit: 5_000, // 5 second render limit
memoryLimit: 100_000_000 // 100MB memory limit
});Configure template caching for improved performance.
/**
* Template caching configuration
* - false: no caching
* - true: default LRU cache (1024 entries)
* - number: LRU cache with specified size
* - LiquidCache: custom cache implementation
*/
cache?: boolean | number | LiquidCache;
interface LiquidCache {
lookup(key: string): Template[] | undefined;
assign(key: string, value: Template[]): void;
remove(key: string): void;
clear(): void;
}Usage Examples:
// Enable default caching
const cachedEngine = new Liquid({
cache: true
});
// Custom cache size
const largeCacheEngine = new Liquid({
cache: 5000
});
// Custom cache implementation
class CustomCache implements LiquidCache {
private store = new Map<string, Template[]>();
lookup(key: string) { return this.store.get(key); }
assign(key: string, value: Template[]) { this.store.set(key, value); }
remove(key: string) { this.store.delete(key); }
clear() { this.store.clear(); }
}
const customCacheEngine = new Liquid({
cache: new CustomCache()
});Configure date formatting and locale settings.
/**
* Timezone offset for date operations
* Can be number (minutes) or timezone name
* Default: local timezone
*/
timezoneOffset?: number | string;
/**
* Default date format string
* Default: "%A, %B %-e, %Y at %-l:%M %P %z"
*/
dateFormat?: string;
/**
* Default locale for date formatting
* Default: system locale
*/
locale?: string;
/**
* Preserve input timezone in date operations
* Default: false
*/
preserveTimezones?: boolean;Usage Examples:
// Timezone and locale configuration
const i18nEngine = new Liquid({
timezoneOffset: "America/New_York",
dateFormat: "%Y-%m-%d %H:%M:%S",
locale: "en-US",
preserveTimezones: true
});const defaultOptions: NormalizedFullOptions = {
root: ['.'],
layouts: ['.'],
partials: ['.'],
relativeReference: true,
jekyllInclude: false,
cache: undefined,
extname: '',
dynamicPartials: true,
jsTruthy: false,
dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
locale: '',
trimTagRight: false,
trimTagLeft: false,
trimOutputRight: false,
trimOutputLeft: false,
greedy: true,
tagDelimiterLeft: '{%',
tagDelimiterRight: '%}',
outputDelimiterLeft: '{{',
outputDelimiterRight: '}}',
preserveTimezones: false,
strictFilters: false,
strictVariables: false,
ownPropertyOnly: true,
lenientIf: false,
globals: {},
keepOutputType: false,
memoryLimit: Infinity,
parseLimit: Infinity,
renderLimit: Infinity
};