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 file system abstraction and template loading capabilities supporting both file-based and in-memory template sources.
Abstract file system interface for template loading.
interface FS {
/**
* Check if file exists
* @param file - File path
* @returns Boolean or Promise resolving to boolean
*/
exists(file: string): boolean | Promise<boolean>;
/**
* Read file contents
* @param file - File path
* @returns File contents as string or Promise
*/
readFile(file: string): string | Promise<string>;
/**
* Get directory name from file path
* @param file - File path
* @returns Directory path
*/
dirname(file: string): string;
/**
* Path separator character
*/
sep: string;
}Load templates from files with different lookup types.
/**
* Parse template file asynchronously
* @param file - Path to template file
* @param lookupType - Type of lookup (Root, Partials, Layouts)
* @returns Promise resolving to Template array
*/
parseFile(file: string, lookupType?: LookupType): Promise<Template[]>;
/**
* Parse template file synchronously
* @param file - Path to template file
* @param lookupType - Type of lookup (Root, Partials, Layouts)
* @returns Template array
*/
parseFileSync(file: string, lookupType?: LookupType): Template[];
/**
* Render template file asynchronously
* @param file - Path to template file
* @param ctx - Data context for rendering
* @param renderFileOptions - File rendering options
* @returns Promise resolving to rendered output
*/
renderFile(file: string, ctx?: Context | object, renderFileOptions?: RenderFileOptions): Promise<any>;
/**
* Render template file synchronously
* @param file - Path to template file
* @param ctx - Data context for rendering
* @param renderFileOptions - File rendering options
* @returns Rendered output
*/
renderFileSync(file: string, ctx?: Context | object, renderFileOptions?: RenderFileOptions): any;
/**
* Render template file to Node.js readable stream
* @param file - Path to template file
* @param scope - Data context for rendering
* @param renderOptions - Rendering options
* @returns Promise resolving to Node.js ReadableStream
*/
renderFileToNodeStream(file: string, scope?: object, renderOptions?: RenderOptions): Promise<NodeJS.ReadableStream>;Different template lookup strategies.
enum LookupType {
Root = 'fs',
Partials = 'partials',
Layouts = 'layouts'
}Use in-memory template storage instead of file system.
interface LiquidOptions {
/**
* In-memory template mapping (overrides file system)
* Key is template name, value is template content
*/
templates?: {[key: string]: string};
}import { Liquid } from "liquidjs";
// Multiple root directories
const engine = new Liquid({
root: ['./templates', './shared-templates'],
partials: './partials',
layouts: './layouts',
extname: '.liquid'
});
// Render from file
const html = await engine.renderFile('page.liquid', {
title: 'Welcome',
content: 'Hello World'
});// Define templates in memory
const memoryEngine = new Liquid({
templates: {
'layout': `
<!DOCTYPE html>
<html>
<head><title>{{ title }}</title></head>
<body>{{ content }}</body>
</html>
`,
'header': '<h1>{{ heading }}</h1>',
'footer': '<p>© {{ year }} {{ company }}</p>'
}
});
// Use in-memory templates
const result = await memoryEngine.parseAndRender(`
{% layout 'layout' %}
{% include 'header' %}
<p>{{ message }}</p>
{% include 'footer' %}
`, {
title: 'My Site',
heading: 'Welcome',
message: 'Hello from LiquidJS!',
year: 2024,
company: 'My Company'
});// Implement custom file system
class CustomFS implements FS {
sep = '/';
exists(file: string): boolean {
// Custom existence check
return true;
}
readFile(file: string): string {
// Custom file reading
return `<h1>Template: ${file}</h1>`;
}
dirname(file: string): string {
const parts = file.split('/');
parts.pop();
return parts.join('/');
}
}
const customEngine = new Liquid({
fs: new CustomFS()
});