Skip to content

Commit

Permalink
feat: group_by/group_by_exp/find/find_exp from Jekyll, #443
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed Apr 14, 2024
1 parent 258780e commit 2b713b7
Show file tree
Hide file tree
Showing 29 changed files with 776 additions and 36 deletions.
5 changes: 5 additions & 0 deletions docs/source/_data/sidebar.yml
Expand Up @@ -8,6 +8,7 @@ tutorials:
express: use-in-expressjs.html
advanced:
caching: caching.html
escaping: escaping.html
registeration: register-filters-tags.html
access_scope_in_filters: access-scope-in-filters.html
parse_parameters: parse-parameters.html
Expand Down Expand Up @@ -40,8 +41,12 @@ filters:
downcase: downcase.html
escape: escape.html
escape_once: escape_once.html
find: find.html
find_exp: find_exp.html
first: first.html
floor: floor.html
group_by: group_by.html
group_by_exp: group_by_exp.html
join: join.html
json: json.html
last: last.html
Expand Down
25 changes: 25 additions & 0 deletions docs/source/filters/find.md
@@ -0,0 +1,25 @@
---
title: find
---

{% since %}v10.11.0{% endsince %}

Return the first object in an array for which the queried attribute has the given value or return `nil` if no item in the array satisfies the given criteria. For the following `members` array:

```javascript
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
```

Input
```liquid
{{ members | find: "graduation_year", 2014 | json }}
```

Output
```text
{"graduation_year":2014,"name":"John"}
```
25 changes: 25 additions & 0 deletions docs/source/filters/find_exp.md
@@ -0,0 +1,25 @@
---
title: find_exp
---

{% since %}v10.11.0{% endsince %}

Return the first object in an array for which the given expression evaluates to true or return `nil` if no item in the array satisfies the evaluated expression.

```javascript
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
```

Input
```liquid
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
```

Output
```text
{"graduation_year":2014,"name":"John"}
```
48 changes: 48 additions & 0 deletions docs/source/filters/group_by.md
@@ -0,0 +1,48 @@
---
title: group_by
---

{% since %}v10.11.0{% endsince %}

Group an array's items by a given property. For `members` array:

```javascript
const members = [
{ graduation_year: 2003, name: 'Jay' },
{ graduation_year: 2003, name: 'John' },
{ graduation_year: 2004, name: 'Jack' }
]
```

Input
```liquid
{{ members | group_by: "graduation_year" | json: 2 }}
```

Output
```text
[
{
"name": 2003,
"items": [
{
"graduation_year": 2003,
"name": "Jay"
},
{
"graduation_year": 2003,
"name": "John"
}
]
},
{
"name": 2004,
"items": [
{
"graduation_year": 2004,
"name": "Jack"
}
]
}
]
```
48 changes: 48 additions & 0 deletions docs/source/filters/group_by_exp.md
@@ -0,0 +1,48 @@
---
title: group_by_exp
---

{% since %}v10.11.0{% endsince %}

Group an array's items using a Liquid expression. For `members` array below:

```javascript
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2009, name: 'Jack' }
]
```

Input
```liquid
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
```

Output
```text
[
{
"name": "201",
"items": [
{
"graduation_year": 2013,
"name": "Jay"
},
{
"graduation_year": 2014,
"name": "John"
}
]
},
{
"name": "200",
"items": [
{
"graduation_year": 2009,
"name": "Jack"
}
]
}
]
```
21 changes: 21 additions & 0 deletions docs/source/filters/json.md
Expand Up @@ -16,3 +16,24 @@ Output
```text
["foo","bar","coo"]
```

## Space

{% since %}v10.11.0{% endsince %}

An additional `space` parameter can be specified to format the JSON.

Input
```liquid
{% assign arr = "foo bar coo" | split: " " %}
{{ arr | json: 4 }}
```

Output
```text
[
"foo",
"bar",
"coo"
]
```
2 changes: 1 addition & 1 deletion docs/source/filters/overview.md
Expand Up @@ -12,7 +12,7 @@ Categories | Filters
Math | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
String | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last,remove, remove_first, remove_last, truncate, truncatewords
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br
Array | slice, map, sort, sort_natural, uniq, where, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
Array | slice, map, sort, sort_natural, uniq, where, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
Date | date
Misc | default, json, raw

Expand Down
24 changes: 24 additions & 0 deletions docs/source/filters/where.md
Expand Up @@ -79,4 +79,28 @@ Output
Featured product: Hawaiian print sweater vest
```

Additionally, `property` can be any valid Liquid variable expression as used in output syntax, except that the scope of this expression is within each item. For the following `products` array:

```javascript
const products = [
{ meta: { details: { class: 'A' } }, order: 1 },
{ meta: { details: { class: 'B' } }, order: 2 },
{ meta: { details: { class: 'B' } }, order: 3 }
]
```

Input
```liquid
{% assign selected = products | where: 'meta.details["class"]', "B" %}
{% for item in selected -%}
- {{ item.order }}
{% endfor %}
```

Ouput
```text
- 2
- 3
```

[truthy]: ../tutorials/truthy-and-falsy.html
14 changes: 11 additions & 3 deletions docs/source/tutorials/differences.md
Expand Up @@ -16,7 +16,7 @@ In the meantime, it's now implemented in JavaScript, that means it has to be mor
* **Async as first-class citizen**. Filters and tags can be implemented asynchronously by return a `Promise`.
* **Also can be sync**. For scenarios that are not I/O intensive, render synchronously can be much faster. You can call synchronous APIs like `.renderSync()` as long as all the filters and tags in template support to be rendered synchronously. All builtin filters/tags support both sync and async render.
* **[Abstract file system][afs]**. Along with async feature, LiquidJS can be used to serve templates stored in Databases [#414][#414], on remote HTTP server [#485][#485], and so on.
* **Additional tags and filters** like `layout` and `json`.
* **Additional tags and filters** like `layout` and `json`, see below for details.

## Differences

Expand All @@ -29,11 +29,16 @@ Though we're trying to be compatible with the Ruby version, there are still some
* Iteration order for objects. The iteration order of JavaScript objects, and thus LiquidJS objects, is a combination of the insertion order for string keys, and ascending order for number-like keys, while the iteration order of Ruby Hash is simply the insertion order.
* Sort stability. The [sort][sort] stability is also not defined in both shopify/liquid and LiquidJS, but it's [considered stable][stable-sort] for LiquidJS in Node.js 12+ and Google Chrome 70+.
* Trailing unmatched characters inside filters are allowed in shopify/liquid but not in LiquidJS. It means filter arguments without a colon like `{%raw%}{{ "a b" | split " "}}{%endraw%}` will throw an error in LiquidJS. This is intended to improve Liquid usability, see [#208][#208] and [#212][#212].
* LiquidJS has additional tags: [layout][layout] and corresponding `block` tag.
* LiquidJS has more tags/filters than [the Liquid language][liquid]:
* LiquidJS-defined tags: [layout][layout], [render][render] and corresponding `block` tag.
* LiquidJS-defined filters: [json][json].
* Tags/filters that don't depend on Shopify platform are borrowed from [Shopify][shopify-tags].
* Tags/filters that don't depend on Jekyll framework are borrowed from [Jekyll][jekyll-filters]
* LiquidJS [date][date] filter supports `%q` for date ordinals like `{{ '2023/02/02' | date: '%d%q of %b'}}` => `02nd of Feb`

[date]: https://liquidjs.com/filters/date.html
[layout]: https://liquidjs.com/tags/layout.html
[layout]: ../tags/layout.html
[render]: ../tags/render.html
[json]: https://liquidjs.com/filters/json.html
[#26]: https://github.com/harttle/liquidjs/pull/26
[#59]: https://github.com/harttle/liquidjs/issues/59
Expand All @@ -47,3 +52,6 @@ Though we're trying to be compatible with the Ruby version, there are still some
[plugins]: ./plugins.html#Plugin-List
[ruby-liquid]: https://github.com/Shopify/liquid
[afs]: https://liquidjs.com/tutorials/render-file.html#Abstract-File-System
[liquid]: https://shopify.github.io/liquid/basics/introduction/
[shopify-tags]: https://shopify.dev/docs/api/liquid/tags
[jekyll-filters]: https://jekyllrb.com/docs/liquid/filters/
76 changes: 76 additions & 0 deletions docs/source/tutorials/escaping.md
@@ -0,0 +1,76 @@
---
title: Escaping
---

Escaping is important in all languages, including LiquidJS. While escaping has 2 different meanings for a template engine:

1. Escaping for the output, i.e. HTML escape. Used to escape HTML special characters so the output will not break HTML structures, aka HTML safe.
2. Escaping for the language itself, i.e. Liquid escape. Used to output strings that's considered special in Liquid language. This will be useful when you're writing an article in Liquid template to introduce Liquid language.

## HTML Escape

By default output is not escaped. While you can use [escape][escape] filter for this:

Input
```liquid
{{ "1 < 2" | escape }}
```

Output
```text
1 &lt; 2
```

There's also [escape_once][escape_once], [newline_to_br][newline_to_br], [strip_html][strip_html] filters for you to fine tune your output.

In cases where variables are mostly not trusted, [outputEscape][outputEscape] can be set to `"escape"` to apply escape by default. In this case, when you need some output not to be escaped, [raw][raw] filter can be used:

Input
```liquid
{{ "1 < 2" }}
{{ "<button>OK</button>" | raw }}
```

Output
```text
1 &lt; 2
<button>OK</button>
```

## Liquid Escape

To disable Liquid language and output strings like `{{` and `{%`, the [raw][raw] tag can be used.

Input
```liquid
{% raw %}
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
{{{ that }}} will not.
{% endraw %}
```

Output
```text
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
{{{ that }}} will not.
```

Within strings literals in LiquidJS template, `\` can be used to escape special characters in string syntax. For example:

Input
```liquid
{{ "\"" }}
```

Output
```liquid
"
```

[outputEscape]: ./options.html#outputEscape
[escape]: ../filters/escape.html
[raw]: ../filters/raw.html
[escape_once]: ../filters/escape.html
[strip_html]: ../filters/strip_html.html
[newline_to_br]: ../filters/newline_to_br.html
[raw]: ../tags/raw.html
10 changes: 10 additions & 0 deletions docs/source/tutorials/options.md
Expand Up @@ -102,6 +102,14 @@ Before 2.0.1, <code>extname</code> is set to `.liquid` by default. To change tha

it defaults to false. For example, when set to true, a blank string would evaluate to false with jsTruthy. With Shopify's truthiness, a blank string is true.

## outputEscape

[outputEscape][outputEscape] can be used to automatically escape output strings. It can be one of `"escape"`, `"json"`, or `(val: unknown) => string`, defaults to `undefined`.

- For untrusted output variables, set `outputEscape: "escape"` makes them be HTML escaped by default. You'll need [raw][raw] filter for direct output.
- `"json"` is useful when you're using LiquidJS to create valid JSON files.
- It can even be a function which allows you to control what variables are output throughout LiquidJS. Please note the input can be any type other than string, e.g. an filter returned an non-string value.

## Date

**timezoneOffset** is used to specify a different timezone to output dates, your local timezone will be used if not specified. For example, set `timezoneOffset: 0` to output all dates in UTC/GMT 00:00.
Expand Down Expand Up @@ -151,3 +159,5 @@ Parameter orders are ignored by default, for ea `{% for i in (1..8) reversed lim
[wc]: ./whitespace-control.html
[intro]: ./intro-to-liquid.html
[jekyllInclude]: /api/interfaces/LiquidOptions.html#jekyllInclude
[raw]: ../filters/raw.html
[outputEscape]: /api/interfaces/LiquidOptions.html#outputEscape
25 changes: 25 additions & 0 deletions docs/source/zh-cn/filters/find.md
@@ -0,0 +1,25 @@
---
title: find
---

{% since %}v10.11.0{% endsince %}

在数组中找到给定的属性为给定的值的第一个元素并返回;如果没有这样的元素则返回 `nil`。对于 `members` 数组:

```javascript
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
```

输入
```liquid
{{ members | find: "graduation_year", 2014 | json }}
```

输出
```text
{"graduation_year":2014,"name":"John"}
```

0 comments on commit 2b713b7

Please sign in to comment.