mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-13 11:20:40 -07:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd426789c | ||
|
|
1c6316111d | ||
|
|
71aa1b1998 | ||
|
|
350f95c8f7 | ||
|
|
955b7971c0 | ||
|
|
8686876067 | ||
|
|
3a02eb12bf | ||
|
|
906707833e | ||
|
|
a2da822cb8 | ||
|
|
86fc135d9e | ||
|
|
5d953132e8 |
@@ -757,6 +757,33 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rosomri",
|
||||
"name": "Omri Rosner",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/68001413?v=4",
|
||||
"profile": "https://github.com/rosomri",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "immerrr",
|
||||
"name": "immerrr again",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/579798?v=4",
|
||||
"profile": "https://github.com/immerrr",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rongjiecomputer",
|
||||
"name": "Loo Rong Jie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13115060?v=4",
|
||||
"profile": "https://github.com/rongjiecomputer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [10.24.0](https://github.com/harttle/liquidjs/compare/v10.23.0...v10.24.0) (2025-10-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **filters:** Add base64_encode and base64_decode filters for Shopify compatibility ([#828](https://github.com/harttle/liquidjs/issues/828)) ([86fc135](https://github.com/harttle/liquidjs/commit/86fc135d9ec0137689faf150535b9315e75ecc30))
|
||||
|
||||
# [10.23.0](https://github.com/harttle/liquidjs/compare/v10.22.0...v10.23.0) (2025-10-23)
|
||||
|
||||
|
||||
|
||||
@@ -72,35 +72,30 @@ Feel free to create a PR or contact me to add your use case into this list!
|
||||
If you personally love LiquidJS or it's benefiting your business, please consider financially support us via [GitHub Sponsors](https://github.com/sponsors/harttle). Special thanks to our sponsors!
|
||||
|
||||
<!-- FINANCIAL-CONTRIBUTORS-BEGIN -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.opensense.com/"><img src="https://images.opencollective.com/opensense-inc/bf840ae/logo/256.png?height=100" width="100px;" alt="Opensense Inc."/><br /><sub><b>Opensense</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.11ty.dev/"><img src="https://avatars.githubusercontent.com/u/35147177?v=4&s=100" width="100px;" alt="Eleventy"/><br /><sub><b>Eleventy</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://about.me/peterdehaan"><img src="https://avatars2.githubusercontent.com/u/557895?v=4&s=100" width="100px;" alt="Peter deHaan"/><br /><sub><b>Peter deHaan</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://opencollective.com/touchless"><img src="https://images.opencollective.com/touchless/273bc74/logo/256.png?height=100" width="100px;" alt="Touchless"/><br /><sub><b>Touchless</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.dropkiq.com/"><img src="https://images.opencollective.com/1bertlol/43a8ea8/logo/256.png?height=100" width="100px;" alt="Adam Darrah"/><br /><sub><b>Dropkiq</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://dailycontributors.com/"><img src="https://images.opencollective.com/dailycontributors/3c2e057/logo/256.png?height=100&width=100" width="100px;" alt="Dailycontributors"/><br /><sub><b>Dailycontributors</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/coni2k"><img src="https://avatars0.githubusercontent.com/u/1284601?v=4&s=100" width="100px;" alt="coni2k"/><br /><sub><b>Serkan Holat</b></sub></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4&s=100" width="100px;" alt="amit777"/><br /><sub><b>amit777</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://opencollective.com/khaled-salem"><img src="https://images.opencollective.com/khaled-salem/avatar/256.png?height=256" width="100px;" alt="Khaled Salem"/><br /><sub><b>Khaled Salem</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://sentry.io/"><img src="https://avatars.githubusercontent.com/u/1396951?v=4&s=100" width="100px;" alt="Sentry"/><br /><sub><b>Sentry</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.checkoutblocks.com/"><img src="https://avatars.githubusercontent.com/u/114603307?v=4&s=100" width="100px;" alt="Checkout Blocks"/><br /><sub><b>Checkout Blocks</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://customer.io/"><img src="https://avatars.githubusercontent.com/u/1152079?v=4&s=100" width="100px;" alt="Customer IO"/><br /><sub><b>Customer IO</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/15fathoms"><img src="https://avatars.githubusercontent.com/u/79156039?v=4&s=100" width="100px;" alt="Emmanuel Cartelli"/><br /><sub><b>Emmanuel Cartelli</b></sub></a><br /></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/microsoft"><img src="https://avatars.githubusercontent.com/u/6154722?v=4&s=100" width="100px;" alt="Microsoft"/><br /><sub><b>Microsoft</b></sub></a><br /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.pakstyle.pk/"><img src="https://images.opencollective.com/pakstyle/2b81605/logo/256.png?height=100" width="100px;" alt="PakStyle.pk"/><br /><sub><b>PakStyle.pk</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://syntax.fm/"><img src="https://avatars.githubusercontent.com/u/130389858?v=4&s=100" width="100px;" alt="Syntax Podcast"/><br /><sub><b>Syntax Podcast</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://opencollective.com/cartelli-emmanuel"><img src="https://images.opencollective.com/cartelli-emmanuel/avatar/256.png?height=100" width="100px;" alt="Cartelli Emmanuel"/><br /><sub><b>Cartelli Emmanuel</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.escorta.com/"><img src="https://images.opencollective.com/escortacom/avatar/256.png?height=100" width="100px;" alt="EscortA.com"/><br /><sub><b>EscortA.com</b></sub></a></td>
|
||||
<td align="center" valign="middle" width="14.28%"><a href="https://chudovo.com/"><img src="https://images.opencollective.com/Chudovo/avatar/256.png?height=100" style="max-width:100px;max-height:100px;" alt="Chudovo"/><br /><sub><b>Chudovo</b></sub></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p align="center" style="line-height: 2.5;">
|
||||
<a href="https://www.11ty.dev/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/35147177?v=4&s=100" height="80" style="vertical-align: middle;" alt="Eleventy" title="Eleventy"/></a>
|
||||
<a href="https://www.opensense.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/opensense-inc/bf840ae/logo/256.png?height=100" height="80" style="vertical-align: middle;" alt="Opensense Inc." title="Opensense"/></a>
|
||||
<a href="https://github.com/microsoft" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/6154722?v=4&s=100" height="80" style="vertical-align: middle;" alt="Microsoft" title="Microsoft"/></a>
|
||||
<a href="https://sentry.io/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/1396951?v=4&s=100" height="80" style="vertical-align: middle;" alt="Sentry" title="Sentry"/></a>
|
||||
<a href="https://www.checkoutblocks.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/114603307?v=4&s=100" height="80" style="vertical-align: middle;" alt="Checkout Blocks" title="Checkout Blocks"/></a>
|
||||
<a href="https://customer.io/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/1152079?v=4&s=100" height="80" style="vertical-align: middle;" alt="Customer IO" title="Customer IO"/></a>
|
||||
<a href="https://syntax.fm/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/130389858?v=4&s=100" height="80" style="vertical-align: middle;" alt="Syntax Podcast" title="Syntax Podcast"/></a>
|
||||
<br/>
|
||||
<a href="https://www.lambdatest.com/?utm_source=liquidjs&utm_medium=sponsor" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://www.lambdatest.com/blue-logo.png" width="240" style="vertical-align: middle;" alt="LambdaTest" title="LambdaTest"/></a>
|
||||
<a href="https://chudovo.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/Chudovo/avatar/256.png?height=100" width="160" style="vertical-align: middle;background: white;padding: 8px 16px;" alt="Chudovo" title="Chudovo"/></a>
|
||||
<a href="https://dailycontributors.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/dailycontributors/3c2e057/logo/256.png?height=50&width=100" width="120" style="vertical-align: middle;" alt="Dailycontributors" title="Dailycontributors"/></a>
|
||||
<a href="https://www.pakstyle.pk/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/pakstyle/2b81605/logo/256.png?height=100" height="80" style="vertical-align: middle;" alt="PakStyle.pk" title="PakStyle.pk"/></a>
|
||||
<a href="https://www.escorta.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/escortacom/avatar/256.png?height=100" height="45" style="vertical-align: middle;" alt="EscortA.com" title="EscortA.com"/></a>
|
||||
<br/>
|
||||
<a href="https://opencollective.com/touchless" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/touchless/273bc74/logo/256.png?height=100" height="80" style="vertical-align: middle;" alt="Touchless" title="Touchless"/></a>
|
||||
<a href="https://www.dropkiq.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/1bertlol/43a8ea8/logo/256.png?height=100" height="80" style="vertical-align: middle;" alt="Dropkiq" title="Dropkiq"/></a>
|
||||
<a href="https://about.me/peterdehaan" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars2.githubusercontent.com/u/557895?v=4&s=100" height="80" style="vertical-align: middle;" alt="Peter deHaan" title="Peter deHaan"/></a>
|
||||
<a href="https://github.com/coni2k" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars0.githubusercontent.com/u/1284601?v=4&s=100" height="80" style="vertical-align: middle;" alt="Serkan Holat" title="Serkan Holat"/></a>
|
||||
<a href="https://github.com/amit777" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4&s=100" height="80" style="vertical-align: middle;" alt="amit777" title="amit777"/></a>
|
||||
<a href="https://opencollective.com/khaled-salem" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/khaled-salem/avatar/256.png?height=256" height="80" style="vertical-align: middle;" alt="Khaled Salem" title="Khaled Salem"/></a>
|
||||
<a href="https://github.com/15fathoms" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/79156039?v=4&s=100" height="80" style="vertical-align: middle;" alt="Emmanuel Cartelli" title="Emmanuel Cartelli"/></a>
|
||||
<a href="https://opencollective.com/cartelli-emmanuel" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/cartelli-emmanuel/avatar/256.png?height=100" height="80" style="vertical-align: middle;" alt="Cartelli Emmanuel" title="Cartelli Emmanuel"/></a>
|
||||
</p>
|
||||
<!-- FINANCIAL-CONTRIBUTORS-END -->
|
||||
|
||||
## Contributors ✨
|
||||
@@ -217,6 +212,11 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://gurdiga.com"><img src="https://avatars.githubusercontent.com/u/53922?v=4?s=100" width="100px;" alt="Vlad GURDIGA"/><br /><sub><b>Vlad GURDIGA</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=gurdiga" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.streakingman.com"><img src="https://avatars.githubusercontent.com/u/30397306?v=4?s=100" width="100px;" alt="裸奔狂甩丁丁"/><br /><sub><b>裸奔狂甩丁丁</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=StreakingMan" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/skynetigor"><img src="https://avatars.githubusercontent.com/u/20903171?v=4?s=100" width="100px;" alt="Ihor Panasiuk"/><br /><sub><b>Ihor Panasiuk</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=skynetigor" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rosomri"><img src="https://avatars.githubusercontent.com/u/68001413?v=4?s=100" width="100px;" alt="Omri Rosner"/><br /><sub><b>Omri Rosner</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=rosomri" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/immerrr"><img src="https://avatars.githubusercontent.com/u/579798?v=4?s=100" width="100px;" alt="immerrr again"/><br /><sub><b>immerrr again</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=immerrr" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rongjiecomputer"><img src="https://avatars.githubusercontent.com/u/13115060?v=4?s=100" width="100px;" alt="Loo Rong Jie"/><br /><sub><b>Loo Rong Jie</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=rongjiecomputer" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -20,7 +20,6 @@ awk '/ALL-CONTRIBUTORS-LIST:START/{flag=1;next}/ALL-CONTRIBUTORS-LIST:END/{flag=
|
||||
# create docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
awk '/FINANCIAL-CONTRIBUTORS-BEGIN/{flag=1;next}/FINANCIAL-CONTRIBUTORS-END/{flag=0}flag' README.md | \
|
||||
sed 's/<br \/>.*<\/td>/<\/a><\/td>/g' | \
|
||||
sed 's/width="[^"]*"//g' | \
|
||||
tr -d '\n' | \
|
||||
sed 's/<\/tr>\s*<tr>//g' \
|
||||
> docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: base64_decode
|
||||
---
|
||||
|
||||
{% since %}v10.24.0{% endsince %}
|
||||
|
||||
Decodes a Base64-formatted string back to its original text.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "b25lIHR3byB0aHJlZQ==" | base64_decode }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
one two three
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "SGVsbG8sIFdvcmxkISBAIyQl" | base64_decode }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
Hello, World! @#$%
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: base64_encode
|
||||
---
|
||||
|
||||
{% since %}v10.24.0{% endsince %}
|
||||
|
||||
Encodes a string into Base64 format.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "one two three" | base64_encode }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
b25lIHR3byB0aHJlZQ==
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "Hello, World! @#$%" | base64_encode }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
SGVsbG8sIFdvcmxkISBAIyQl
|
||||
```
|
||||
@@ -15,5 +15,6 @@ HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_b
|
||||
Array | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
|
||||
Date | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string
|
||||
Misc | default, json, jsonify, inspect, raw, to_integer
|
||||
Base64 | base64_encode, base64_decode
|
||||
|
||||
[shopify/liquid]: https://github.com/Shopify/liquid
|
||||
|
||||
@@ -24,6 +24,7 @@ Though we're trying to be compatible with the Ruby version, there are still some
|
||||
|
||||
* Truthy and Falsy. All values except `undefined`, `null`, `false` are truthy, whereas in Ruby Liquid all except `nil` and `false` are truthy. See [#26][#26].
|
||||
* Number. In JavaScript we cannot distinguish or convert between `float` and `integer`, see [#59][#59]. And when applied `size` filter, numbers always return 0, which is 8 for integer in ruby, cause they do not have a `length` property.
|
||||
* Stringify: We've aligned string coercion for primitive types. While some differences remain; for example, in Shopify/liquid, `strip` returns the "inspected" string of an input array, whereas in LiquidJS, the `strip` filter simply stringifies the input array [#852][#852].
|
||||
* [.to_liquid()](https://github.com/Shopify/liquid/wiki/Introduction-to-Drops) is replaced by `.toLiquid()`
|
||||
* [.to_s()](https://www.rubydoc.info/gems/liquid/Liquid/Drop) is replaced by JavaScript `.toString()`
|
||||
* 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.
|
||||
@@ -47,6 +48,7 @@ Though we're trying to be compatible with the Ruby version, there are still some
|
||||
[#236]: https://github.com/harttle/liquidjs/issues/236
|
||||
[#414]: https://github.com/harttle/liquidjs/discussions/414
|
||||
[#485]: https://github.com/harttle/liquidjs/discussions/485
|
||||
[#852]: https://github.com/harttle/liquidjs/discussions/852
|
||||
[sort]: https://liquidjs.com/filters/sort.html
|
||||
[stable-sort]: https://v8.dev/features/stable-sort
|
||||
[plugins]: ./plugins.html#Plugin-List
|
||||
|
||||
@@ -5,17 +5,64 @@ title: Operators
|
||||
LiquidJS operators are very simple and different. There're 2 types of operators supported:
|
||||
|
||||
* Comparison operators: `==`, `!=`, `>`, `<`, `>=`, `<=`
|
||||
* Logic operators: `or`, `and`, `contains`
|
||||
* Logic operators: `not`, `or`, `and`, `contains`
|
||||
|
||||
Thus numerical operators are not supported and you cannot even plus two numbers like this `{% raw %}{{a + b}}{% endraw %}`, instead we need a filter `{% raw %}{{ a | plus: b}}{% endraw %}`. Actually `+` is a valid variable name in LiquidJS.
|
||||
|
||||
## Logic Operators
|
||||
|
||||
### not
|
||||
|
||||
Negates a condition. Returns `true` if the condition is false, and `false` if the condition is true.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% if not user.active %}
|
||||
User is inactive
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### and
|
||||
|
||||
Returns `true` if both conditions are true.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% if user.age >= 18 and user.verified %}
|
||||
Access granted
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### or
|
||||
|
||||
Returns `true` if at least one condition is true.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% if user.isAdmin or user.isModerator %}
|
||||
You have elevated privileges
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### contains
|
||||
|
||||
Checks if a string contains a substring, or if an array contains an element.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% if product.title contains "Pack" %}
|
||||
This is a pack
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## Precedence
|
||||
|
||||
1. Comparison operators. All comparison operations have the same precedence and higher than logic operators.
|
||||
2. Logic operators. All logic operators have the same precedence.
|
||||
1. Comparison operators, and `contains`. All comparison operators alongside `contains` have the same (highest) precedence.
|
||||
2. `not` operator. It has slightly more precedence than `or` and `and`.
|
||||
3. `or` and `and` operators. These logic operators have the same (lowest) precedence.
|
||||
|
||||
## Associativity
|
||||
|
||||
Logic operators are evaluated from right to left, see [shopify docs][operator-order].
|
||||
|
||||
[operator-order]: https://help.shopify.com/en/themes/liquid/basics/operators#order-of-operations
|
||||
[operator-order]: https://shopify.dev/docs/api/liquid/basics#order-of-operations
|
||||
|
||||
@@ -24,6 +24,7 @@ LiquidJS 一直很重视兼容于 Ruby 版本的 Liquid。Liquid 模板语言最
|
||||
|
||||
* 真和假。在 LiquidJS 中 `undefined`, `null`, `false` 是假,之外的都是真;在 Ruby 中 `nil` 和 `false` 是假,其他都是真。见 [#26][#26]。
|
||||
* 数字。JavaScript 不区分浮点数和整数,因此缺失一部分整数算术,见 [#59][#59]。此外 `size` 过滤器作用于数字时总是返回零,而不是 Ruby 中的浮点数或整数的内存大小。
|
||||
* 输出字符串。基本类型的输出已经和 Shopify/liquid 对齐,但是仍然存在一些区别。比如在 Shopify/liquid 中 `strip` 会返回 inspect 字符串,但 LiquidJS `strip` 只是简单地把输入转换为字符串 [#852][#852]。
|
||||
* Drop 中的 [.to_liquid()](https://github.com/Shopify/liquid/wiki/Introduction-to-Drops) 替换为 `.toLiquid()`。
|
||||
* 数据的 [.to_s()](https://www.rubydoc.info/gems/liquid/Liquid/Drop) 替换为 `.toString()`。
|
||||
* 对象的迭代顺序。JavaScript 对象的迭代顺序是插入顺序和数字键递增顺序的组合,但 Ruby Hash 中只是插入顺序(JavaScript 字面量 Object 和 Ruby 字面量 Hash 的插入顺序解释也不同)。
|
||||
@@ -46,6 +47,7 @@ LiquidJS 一直很重视兼容于 Ruby 版本的 Liquid。Liquid 模板语言最
|
||||
[#236]: https://github.com/harttle/liquidjs/issues/236
|
||||
[#414]: https://github.com/harttle/liquidjs/discussions/414
|
||||
[#485]: https://github.com/harttle/liquidjs/discussions/485
|
||||
[#852]: https://github.com/harttle/liquidjs/discussions/852
|
||||
[sort]: https://liquidjs.com/filters/sort.html
|
||||
[stable-sort]: https://v8.dev/features/stable-sort
|
||||
[plugins]: ./plugins.html#插件列表
|
||||
|
||||
@@ -5,14 +5,61 @@ title: 运算符
|
||||
LiquidJS 运算符非常简单也很特别,只支持两类运算符:
|
||||
|
||||
* 比较运算符:`==`, `!=`, `>`, `<`, `>=`, `<=`
|
||||
* 逻辑运算符:`or`, `and`, `contains`
|
||||
* 逻辑运算符:`not`, `or`, `and`, `contains`
|
||||
|
||||
因此普通的数学运算是不支持的,比如 `{% raw %}{{a + b}}{% endraw %}`。它的替代方案是过滤器 `{% raw %}{{ a | plus: b}}{% endraw %}`。事实上 `+` 在 LiquidJS 中是一个合法的变量名。
|
||||
|
||||
## 逻辑运算符
|
||||
|
||||
### not
|
||||
|
||||
对条件取反。如果条件为假则返回 `true`,如果条件为真则返回 `false`。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% if not user.active %}
|
||||
用户未激活
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### and
|
||||
|
||||
当两个条件都为真时返回 `true`。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% if user.age >= 18 and user.verified %}
|
||||
允许访问
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### or
|
||||
|
||||
当至少一个条件为真时返回 `true`。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% if user.isAdmin or user.isModerator %}
|
||||
您拥有提升的权限
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### contains
|
||||
|
||||
检查字符串是否包含子字符串,或数组是否包含元素。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% if product.title contains "Pack" %}
|
||||
这是一个套装
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## 优先级
|
||||
|
||||
1. 比较运算符。所有比较运算符具有同样的优先级,且高于逻辑运算符。
|
||||
2. 逻辑运算符。所有逻辑运算符具有同样的有衔接。
|
||||
1. 比较运算符和 `contains`。所有比较运算符和 `contains` 具有同样的(最高)优先级。
|
||||
2. `not` 运算符。它的优先级略高于 `or` 和 `and`。
|
||||
3. `or` 和 `and` 运算符。这些逻辑运算符具有同样的(最低)优先级。
|
||||
|
||||
## 结合性
|
||||
|
||||
|
||||
Vendored
+15
-15
@@ -1,5 +1,5 @@
|
||||
<header id="banner" class="wrapper">
|
||||
<div class="inner">
|
||||
<div class="inner inner-content">
|
||||
<h2 id="banner-title">{{ page.subtitle }}</h2>
|
||||
<div id="banner-start">
|
||||
<code id="banner-start-command">npm install liquidjs</code><a id="banner-start-link" href="./tutorials/setup.html"><i class="icon-arrow-right"></i></a>
|
||||
@@ -9,7 +9,7 @@
|
||||
</header>
|
||||
<div id="intro-news-list">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<div class="inner inner-content">
|
||||
<div id="intro-news-flex">
|
||||
{% for news in site.data.news %}
|
||||
<a href="{{news.url}}" class="intro-news-wrap">
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div id="content-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<div class="inner inner-content">
|
||||
{{ page.content }}
|
||||
<div id="intro-cmd-wrap" class="highlight typescript"><pre><span class="line"><span class="keyword">import</span> { Liquid } <span class="keyword">from</span> <span class="string">'liquidjs'</span></span><br><span class="line"><span class="keyword">const</span> engine = <span class="keyword">new</span> Liquid()</span><br><span class="line"><span class="keyword">const</span> tpl = engine.parse(<span class="string">'Welcome to {% raw %}{{v}}{% endraw %}!'</span>)</span><br><span class="line">engine.render(tpl, {<span class="attr">v</span>: <span class="string">"Liquid"</span>}).then(<span class="built_in">console</span>.log)</span><br><span class="line"><span class="comment">// Outputs "Welcome to Liquid!"</span></span><br></pre></div>
|
||||
<div id="intro-get-started-wrap">
|
||||
@@ -32,20 +32,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contributors-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<h3>{{__('index.contributors.title')}}</h3>
|
||||
<p class="description">{{__('index.contributors.description')}}</p>
|
||||
<div class="contributors">
|
||||
{{ partial('partial/all-contributors') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sponsors-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<div class="inner inner-content">
|
||||
<h3>{{__('index.sponsors.title')}}</h3>
|
||||
<p class="description">{{__('index.sponsors.description')}}</p>
|
||||
<div class="contributors">
|
||||
@@ -54,3 +43,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contributors-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner inner-content">
|
||||
<h3>{{__('index.contributors.title')}}</h3>
|
||||
<p class="description">{{__('index.contributors.description')}}</p>
|
||||
<div class="contributors">
|
||||
{{ partial('partial/all-contributors') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+5
-1
@@ -54,7 +54,11 @@ a
|
||||
|
||||
.inner
|
||||
@media screen
|
||||
padding: 0 gutter-width
|
||||
margin: 0 gutter-width
|
||||
.inner.inner-content
|
||||
margin: 0 32px
|
||||
@media mq-tablet
|
||||
margin: 0 48px
|
||||
|
||||
#content-wrap
|
||||
background: var(--color-content-bg)
|
||||
|
||||
+20
-17
@@ -66,6 +66,10 @@
|
||||
.intro-news-wrap
|
||||
padding: 24px 30px
|
||||
text-decoration: none
|
||||
&:first-child
|
||||
padding-left: 0
|
||||
&:last-child
|
||||
padding-right: 0
|
||||
&:hover
|
||||
background-color: var(--color-link-hover)
|
||||
@media mq-normal
|
||||
@@ -105,7 +109,7 @@
|
||||
text-align: center
|
||||
@media mq-normal
|
||||
text-align: left
|
||||
padding-left: 70px
|
||||
padding-left: 48px
|
||||
|
||||
.intro-feature-icon
|
||||
color: var(--color-link)
|
||||
@@ -116,7 +120,7 @@
|
||||
margin-bottom: 26px
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 20px
|
||||
left: 0
|
||||
font-size: 24px
|
||||
width: @font-size
|
||||
|
||||
@@ -177,9 +181,11 @@
|
||||
border-bottom: 1px solid #161d24
|
||||
margin: -1px 0
|
||||
.inner
|
||||
margin: 48px 16px
|
||||
margin-top: 48px
|
||||
margin-bottom: 48px
|
||||
@media mq-tablet
|
||||
margin: 64px 32px
|
||||
margin-top: 64px
|
||||
margin-bottom: 64px
|
||||
h3
|
||||
color: #fff
|
||||
font-size: 32px
|
||||
@@ -192,20 +198,7 @@
|
||||
text-decoration: none
|
||||
|
||||
#contributors-wrap
|
||||
.description a
|
||||
color: #fff
|
||||
&:hover
|
||||
background: #fff
|
||||
color: var(--color-navy-lighter)
|
||||
|
||||
#sponsors-wrap
|
||||
border: none
|
||||
text-align: right
|
||||
background: var(--color-content-bg)
|
||||
overflow: hidden;
|
||||
.contributors
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
.inner
|
||||
h3
|
||||
color: var(--color-default)
|
||||
@@ -227,10 +220,20 @@
|
||||
background: #fff
|
||||
color: var(--color-navy-lighter)
|
||||
|
||||
#sponsors-wrap
|
||||
border: none
|
||||
overflow: hidden;
|
||||
.description a
|
||||
color: #fff
|
||||
&:hover
|
||||
background: #fff
|
||||
color: var(--color-navy-lighter)
|
||||
|
||||
.contributors
|
||||
tr
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center;
|
||||
td
|
||||
margin: 10px 2px 0
|
||||
a img
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.23.0",
|
||||
"version": "10.24.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "liquidjs",
|
||||
"version": "10.23.0",
|
||||
"version": "10.24.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^10.0.0"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.23.0",
|
||||
"version": "10.24.0",
|
||||
"sideEffects": false,
|
||||
"description": "A simple, expressive and safe Shopify / Github Pages compatible template engine in pure JavaScript.",
|
||||
"main": "dist/liquid.node.js",
|
||||
|
||||
@@ -45,6 +45,11 @@ const browserFS = {
|
||||
delimiters: ['', ''],
|
||||
'./fs/fs-impl': './build/fs-impl-browser'
|
||||
}
|
||||
const browserBase64 = {
|
||||
include: './src/filters/base64.ts',
|
||||
delimiters: ['', ''],
|
||||
'./base64-impl': '../build/base64-impl-browser'
|
||||
}
|
||||
const browserStream = {
|
||||
include: './src/emitters/index.ts',
|
||||
delimiters: ['', ''],
|
||||
@@ -94,6 +99,7 @@ const browserEsm = {
|
||||
plugins: [
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es6'))
|
||||
],
|
||||
@@ -112,6 +118,7 @@ const browserUmd = {
|
||||
plugins: [
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5'))
|
||||
],
|
||||
@@ -130,6 +137,7 @@ const browserMin = {
|
||||
plugins: [
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5')),
|
||||
uglify()
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import * as base64 from './base64-impl-browser'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
describe('base64-impl/browser', function () {
|
||||
if (+(process.version.match(/^v(\d+)/) as RegExpMatchArray)[1] < 8) {
|
||||
console.info('jsdom not supported, skipping base64-impl-browser...')
|
||||
return
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
const dom = new JSDOM(``, {
|
||||
url: 'https://example.com/',
|
||||
contentType: 'text/html',
|
||||
includeNodeLocations: true
|
||||
})
|
||||
|
||||
// Mock btoa and atob on global object
|
||||
Object.defineProperty(global, 'btoa', {
|
||||
value: dom.window.btoa,
|
||||
writable: true,
|
||||
configurable: true
|
||||
})
|
||||
Object.defineProperty(global, 'atob', {
|
||||
value: dom.window.atob,
|
||||
writable: true,
|
||||
configurable: true
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
delete (global as any).btoa
|
||||
delete (global as any).atob
|
||||
})
|
||||
|
||||
describe('#base64Encode()', function () {
|
||||
it('should encode a simple string', function () {
|
||||
expect(base64.base64Encode('one two three')).toBe('b25lIHR3byB0aHJlZQ==')
|
||||
})
|
||||
|
||||
it('should encode an empty string', function () {
|
||||
expect(base64.base64Encode('')).toBe('')
|
||||
})
|
||||
|
||||
it('should encode a string with special characters', function () {
|
||||
expect(base64.base64Encode('Hello, World! @#$%')).toBe('SGVsbG8sIFdvcmxkISBAIyQl')
|
||||
})
|
||||
|
||||
it('should encode numeric strings', function () {
|
||||
expect(base64.base64Encode('123')).toBe('MTIz')
|
||||
})
|
||||
|
||||
it('should encode boolean strings', function () {
|
||||
expect(base64.base64Encode('true')).toBe('dHJ1ZQ==')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#base64Decode()', function () {
|
||||
it('should decode a simple string', function () {
|
||||
expect(base64.base64Decode('b25lIHR3byB0aHJlZQ==')).toBe('one two three')
|
||||
})
|
||||
|
||||
it('should decode an empty string', function () {
|
||||
expect(base64.base64Decode('')).toBe('')
|
||||
})
|
||||
|
||||
it('should decode a string with special characters', function () {
|
||||
expect(base64.base64Decode('SGVsbG8sIFdvcmxkISBAIyQl')).toBe('Hello, World! @#$%')
|
||||
})
|
||||
|
||||
it('should decode numeric strings', function () {
|
||||
expect(base64.base64Decode('MTIz')).toBe('123')
|
||||
})
|
||||
|
||||
it('should decode boolean strings', function () {
|
||||
expect(base64.base64Decode('dHJ1ZQ==')).toBe('true')
|
||||
})
|
||||
})
|
||||
|
||||
describe('round-trip encoding/decoding', function () {
|
||||
it('should encode and decode back to original', function () {
|
||||
const original = 'Hello, World!'
|
||||
const encoded = base64.base64Encode(original)
|
||||
const decoded = base64.base64Decode(encoded)
|
||||
expect(decoded).toBe(original)
|
||||
})
|
||||
|
||||
it('should handle complex strings with special characters', function () {
|
||||
const original = 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?'
|
||||
const encoded = base64.base64Encode(original)
|
||||
const decoded = base64.base64Decode(encoded)
|
||||
expect(decoded).toBe(original)
|
||||
})
|
||||
|
||||
it('should handle mixed unicode and ASCII', function () {
|
||||
const original = 'Hello 🌍'
|
||||
const encoded = base64.base64Encode(original)
|
||||
const decoded = base64.base64Decode(encoded)
|
||||
expect(decoded).toBe(original)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
export function base64Encode (str: string): string {
|
||||
return btoa(String.fromCharCode(...new TextEncoder().encode(str)))
|
||||
}
|
||||
|
||||
export function base64Decode (str: string): string {
|
||||
return new TextDecoder().decode(
|
||||
Uint8Array.from(atob(str), c => c.charCodeAt(0))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export function base64Encode (str: string): string {
|
||||
return Buffer.from(str, 'utf8').toString('base64')
|
||||
}
|
||||
|
||||
export function base64Decode (str: string): string {
|
||||
return Buffer.from(str, 'base64').toString('utf8')
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Base64 related filters
|
||||
*
|
||||
* Implements base64_encode and base64_decode filters for Shopify compatibility
|
||||
*/
|
||||
|
||||
import { FilterImpl } from '../template'
|
||||
import { stringify } from '../util'
|
||||
import { base64Encode, base64Decode } from './base64-impl'
|
||||
|
||||
export function base64_encode (this: FilterImpl, value: string): string {
|
||||
const str = stringify(value)
|
||||
this.context.memoryLimit.use(str.length)
|
||||
return base64Encode(str)
|
||||
}
|
||||
|
||||
export function base64_decode (this: FilterImpl, value: string): string {
|
||||
const str = stringify(value)
|
||||
this.context.memoryLimit.use(str.length)
|
||||
return base64Decode(str)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import * as urlFilters from './url'
|
||||
import * as arrayFilters from './array'
|
||||
import * as dateFilters from './date'
|
||||
import * as stringFilters from './string'
|
||||
import * as base64Filters from './base64'
|
||||
import misc from './misc'
|
||||
import { FilterImplOptions } from '../template'
|
||||
|
||||
@@ -14,5 +15,6 @@ export const filters: Record<string, FilterImplOptions> = {
|
||||
...arrayFilters,
|
||||
...dateFilters,
|
||||
...stringFilters,
|
||||
...base64Filters,
|
||||
...misc
|
||||
}
|
||||
|
||||
+6
-2
@@ -2,16 +2,20 @@
|
||||
export const version = '[VI]{version}[/VI]'
|
||||
export * as TypeGuards from './util/type-guards'
|
||||
export { toValue, createTrie, Trie, toPromise, toValueSync, assert, LiquidError, ParseError, RenderError, UndefinedVariableError, TokenizationError, AssertionError } from './util'
|
||||
export type { LiquidErrors } from './util/error'
|
||||
export { Drop } from './drop'
|
||||
export type { Comparable } from './drop'
|
||||
export { Emitter } from './emitters'
|
||||
export { defaultOperators, Operators, evalToken, evalQuotedToken, Expression, isFalsy, isTruthy } from './render'
|
||||
export { Context, Scope } from './context'
|
||||
export { Value, Hash, Template, FilterImplOptions, Tag, Filter, Output, Variable, VariableLocation, VariableSegments, Variables, StaticAnalysis, StaticAnalysisOptions, analyze, analyzeSync, Arguments, PartialScope } from './template'
|
||||
export type { TagRenderReturn } from './template'
|
||||
export { Token, TopLevelToken, TagToken, ValueToken } from './tokens'
|
||||
export type { RangeToken, LiteralToken, QuotedToken, PropertyAccessToken, NumberToken } from './tokens'
|
||||
export { TokenKind, Tokenizer, ParseStream, Parser } from './parser'
|
||||
export { filters } from './filters'
|
||||
export * from './tags'
|
||||
export { defaultOptions, LiquidOptions } from './liquid-options'
|
||||
export { FS } from './fs'
|
||||
export { defaultOptions } from './liquid-options'
|
||||
export type { LiquidOptions, RenderOptions, RenderFileOptions } from './liquid-options'
|
||||
export { FS, LookupType } from './fs'
|
||||
export { Liquid } from './liquid'
|
||||
|
||||
@@ -422,7 +422,7 @@ export class Tokenizer {
|
||||
|
||||
* readFileNameTemplate (options: NormalizedFullOptions): IterableIterator<TopLevelToken> {
|
||||
const { outputDelimiterLeft } = options
|
||||
const htmlStopStrings = [',', ' ', outputDelimiterLeft]
|
||||
const htmlStopStrings = [',', ' ', '\r', '\n', '\t', outputDelimiterLeft]
|
||||
const htmlStopStringSet = new Set(htmlStopStrings)
|
||||
// break on ',' and ' ', outputDelimiterLeft only stops HTML token
|
||||
while (this.p < this.N && !htmlStopStringSet.has(this.peek())) {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { test } from '../../stub/render'
|
||||
|
||||
describe('filters/base64', function () {
|
||||
describe('base64_encode', function () {
|
||||
it('should encode a simple string', () => {
|
||||
return test('{{ "one two three" | base64_encode }}', 'b25lIHR3byB0aHJlZQ==')
|
||||
})
|
||||
|
||||
it('should encode an empty string', () => {
|
||||
return test('{{ "" | base64_encode }}', '')
|
||||
})
|
||||
|
||||
it('should encode a string with special characters', () => {
|
||||
return test('{{ "Hello, World! @#$%" | base64_encode }}', 'SGVsbG8sIFdvcmxkISBAIyQl')
|
||||
})
|
||||
|
||||
it('should encode unicode characters', () => {
|
||||
return test('{{ "你好世界" | base64_encode }}', '5L2g5aW95LiW55WM')
|
||||
})
|
||||
|
||||
it('should handle undefined input', () => {
|
||||
return test('{{ foo | base64_encode }}', '')
|
||||
})
|
||||
|
||||
it('should handle null input', () => {
|
||||
return test('{{ null | base64_encode }}', '')
|
||||
})
|
||||
|
||||
it('should handle numeric input', () => {
|
||||
return test('{{ 123 | base64_encode }}', 'MTIz')
|
||||
})
|
||||
|
||||
it('should handle boolean input', () => {
|
||||
return test('{{ true | base64_encode }}', 'dHJ1ZQ==')
|
||||
})
|
||||
})
|
||||
|
||||
describe('base64_decode', function () {
|
||||
it('should decode a simple string', () => {
|
||||
return test('{{ "b25lIHR3byB0aHJlZQ==" | base64_decode }}', 'one two three')
|
||||
})
|
||||
|
||||
it('should decode an empty string', () => {
|
||||
return test('{{ "" | base64_decode }}', '')
|
||||
})
|
||||
|
||||
it('should decode a string with special characters', () => {
|
||||
return test('{{ "SGVsbG8sIFdvcmxkISBAIyQl" | base64_decode }}', 'Hello, World! @#$%')
|
||||
})
|
||||
|
||||
it('should handle undefined input', () => {
|
||||
return test('{{ foo | base64_decode }}', '')
|
||||
})
|
||||
|
||||
it('should handle null input', () => {
|
||||
return test('{{ null | base64_decode }}', '')
|
||||
})
|
||||
|
||||
it('should handle numeric input', () => {
|
||||
return test('{{ "MTIz" | base64_decode }}', '123')
|
||||
})
|
||||
|
||||
it('should handle boolean input', () => {
|
||||
return test('{{ "dHJ1ZQ==" | base64_decode }}', 'true')
|
||||
})
|
||||
})
|
||||
|
||||
describe('base64 round-trip', function () {
|
||||
it('should encode and decode back to original', () => {
|
||||
return test('{{ "Hello, World!" | base64_encode | base64_decode }}', 'Hello, World!')
|
||||
})
|
||||
|
||||
it('should handle complex strings', () => {
|
||||
const complexString = 'Special chars: !@#$%^&*()_+-=[]{}|;:,.<>?'
|
||||
return test(`{{ "${complexString}" | base64_encode | base64_decode }}`, complexString)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -265,6 +265,14 @@ describe('tags/include', function () {
|
||||
const html = liquid.renderFileSync('/current.html')
|
||||
return expect(html).toBe('FOO-')
|
||||
})
|
||||
it('should support Jekyll style include with other whitespace before filename', function () {
|
||||
mock({
|
||||
'/current.html': '{% include bar/foo.html\r\n\ntitle="TITLE"\tcontent="FOO" %}',
|
||||
'/bar/foo.html': '{{include.title}}={{include.content}}-{{content}}'
|
||||
})
|
||||
const html = liquid.renderFileSync('/current.html')
|
||||
return expect(html).toBe('TITLE=FOO-')
|
||||
})
|
||||
it('should support multiple parameters', function () {
|
||||
mock({
|
||||
'/current.html': '{% include bar/foo.html header="HEADER" content="CONTENT" %}',
|
||||
|
||||
Reference in New Issue
Block a user