LiquidJS Documentation

repository·master·Indexed 23 days ago

https://github.com/harttle/liquidjs

A simple, expressive, and extensible Liquid template engine for JavaScript, compatible with Shopify, Jekyll, and GitHub Pages. It supports Node.js, browsers, and CLI environments, and is written in TypeScript. The library provides a comprehensive set of filters and a CLI tool for rendering templates from strings or files.

Tokens
45.1K
Snippets
188
Records
284
Agent score
81%

What's inside liquidjs

  1. Overview of LiquidJS tags

    master

    LiquidJS implements a wide range of tags used for template logic, following the specification used by shopify/liquid. These tags are categorized by their primary function:

    • Iteration: Used to iterate over collections (e.g., for, cycle, tablerow).
    • Control Flow: Used to control the execution branches of template rendering (e.g., if, unless, elsif, else, case, when).
    • Variable: Used to define or alter variables (e.g., assign, increment, decrement, capture, echo).
    • File: Used to include other templates or extend layout templates (e.g., render, include, layout).
    • Language: Used to temporarily disable LiquidJS syntax (e.g., #, raw, comment, liquid).
  2. Overview of LiquidJS filters

    master
    LiquidJS provides over 40 business-logic independent filters, following the specification typically implemented in shopify/liquid. Filters are used in templates to transform data (e.g., {{ value | filter }}). They are categorized into functional groups such as Math, String, HTML/URI, Array, Date, Misc, Base64, and Crypto.
  3. How dynamicPartials works

    master

    The dynamicPartials option (defaults to true) determines how filename arguments in include, render, and layout tags are treated.

    • When true (Default): Filename arguments are treated as variables. For example, {% include file %} with scope { file: 'foo.html' } will include foo.html.
    • When false: Filename arguments are treated as literal strings. {% include file %} will look for a file literally named file.
    {% include file %}
  4. Set default content for blocks

    master

    If a {% block %} in the layout file contains content, that content will be used as the default if the calling template does not provide an override for that specific block.

    // default-layout.liquid
    {% block header %}Header{% endblock %}
    {% block content %}{% endblock %}
    {% block footer %}Footer{% endblock %}
    
    // page.liquid
    {% layout "default-layout.liquid" %}
    {% block content %}My page content{% endblock %}
  5. Implement custom functionality with Liquid Drops

    master

    Liquid Drops allow you to incorporate custom logic into your templates by providing objects that can resolve variable values through properties or methods. To create a Drop, extend the Drop class from liquidjs.

    Drops are fully async-friendly; you can define methods as async or return a Promise from them. This is useful for fetching data from databases or external APIs during template rendering.

    import { Liquid, Drop } from 'liquidjs'
    
    class SettingsDrop extends Drop {
      constructor() {
        super()
        this.foo = 'FOO'
      }
      bar() {
        return 'BAR'
      }
    }
    
    const engine = new Liquid()
    const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
    const context = { settings: new SettingsDrop() }
    // Outputs: "foo: FOO, bar: BAR"
    engine.parseAndRender(template, context).then(html => console.log(html))
  6. Understand LiquidJS compatibility with Shopify and Jekyll

    master

    LiquidJS aims to be compatible with the Ruby implementation of Liquid used by Shopify and Jekyll.

    Compatibility Guarantees:

    • Well-formed templates: Standard Liquid syntax (e.g., forloop.index being 1-indexed) should work as expected.
    • Built-in parity: All non-business-logic specific filters and tags from shopify/liquid are intended to be built into LiquidJS.
    • Semantics: LiquidJS attempts to implement the same semantics (e.g., rendering nil as an empty string).

    Note on Business Logic: Tags or filters specific to the Shopify platform itself are not built-in and should be implemented as plugins.

  7. Supported Operators in LiquidJS

    master

    LiquidJS supports two specific types of operators: Comparison and Logical.

    Important: Arithmetic operators (like +, -, *, /) are not supported directly in expressions. To perform math, you must use filters instead of operators. For example, instead of {{ a + b }}, use {{ a | plus: b }}.

    Comparison Operators

    • == (Equal to)
    • != (Not equal to)
    • > (Greater than)
    • < (Less than)
    • >= (Greater than or equal to)
    • <= (Less than or equal to)

    Logical Operators

    • not (Negation)
    • and (Logical AND)
    • or (Logical OR)
    • contains (Substring or array element check)
  8. Handling Async and Promises in Tag Implementations

    master

    LiquidJS uses generators to handle asynchronous operations. This allows you to use both synchronous and asynchronous rendering methods seamlessly.

    • To support async operations: Use a generator function for the render method (* render(context, emitter)).
    • To await a Promise: Instead of using await somePromise, use yield somePromise inside your generator.
    • Compatibility: This implementation is valid for renderSync(), parseAndRenderSync(), and renderFileSync() as well, because LiquidJS can call generators in a synchronous manner.

    Always prefer * render() over async render() when working with LiquidJS tags to ensure compatibility with the engine's generator-based async model.

  9. Understand the Liquid template language syntax

    master

    LiquidJS uses two primary types of markup to render templates:

    1. Outputs: Used to inject values (like variables) into the template. They are wrapped in double curly braces {{ ... }}. You can optionally apply filters to transform these values.
    2. Tags: Used for logic and template control (e.g., loops, conditionals, variable assignment). They are wrapped in {% ... %}. Tags often come in pairs with a start and an end tag (e.g., {% if %} and {% endif %}).
  10. Configure Template Lookup in Express.js

    master

    When using LiquidJS with Express.js, template resolution respects both the LiquidJS root option and the Express.js views option.

    If you define a root in the Liquid constructor, Liquid will look for templates there. If you define views in Express, Express will also look there. This allows you to resolve templates from multiple directories. For example, if root is ./views1/ and Express views is ./views2, both views1/hello.liquid and views2/world.liquid can be rendered using res.render('hello') and res.render('world') respectively.

    var { Liquid } = require('liquidjs');
    var engine = new Liquid({
        root: './views1/'
    });
    
    app.engine('liquid', engine.express()); 
    app.set('views', './views2');            // specify the views directory
    app.set('view engine', 'liquid');       // set liquid to default