mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-13 11:20:40 -07:00
Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd6bb0fb60 | ||
|
|
9b0b9be849 | ||
|
|
be18f33e20 | ||
|
|
09bfe13f65 | ||
|
|
a75033e2ca | ||
|
|
a8fd734b5e | ||
|
|
47d3f1b1cf | ||
|
|
c20c0af02d | ||
|
|
457fae0736 | ||
|
|
3616a744b9 | ||
|
|
3129d46dc9 | ||
|
|
5b9c346908 | ||
|
|
dbbf628803 | ||
|
|
26ea2856c7 | ||
|
|
a55f543f49 | ||
|
|
d1d517d1ec | ||
|
|
1c816d4fc3 | ||
|
|
34877950bf | ||
|
|
75c815a4d7 | ||
|
|
f1f896c29d | ||
|
|
0ee6dbb511 | ||
|
|
30e04ba16d | ||
|
|
e2311dfd6e | ||
|
|
2def22c85e | ||
|
|
4af7be695c | ||
|
|
05c47da46d | ||
|
|
66011d14b0 | ||
|
|
1cdf10b57d | ||
|
|
4f9a49988a | ||
|
|
f41c1fc02f | ||
|
|
db4348507e | ||
|
|
e743da0020 | ||
|
|
8f69a08399 | ||
|
|
529dd67eeb | ||
|
|
abc058be0f | ||
|
|
521177e3f6 | ||
|
|
75e06eff92 | ||
|
|
0ad2b11ab1 | ||
|
|
97d829116c | ||
|
|
35d5230263 | ||
|
|
94440a0653 | ||
|
|
95ddefc056 | ||
|
|
1b85fdaa9c | ||
|
|
93c38c7c6d | ||
|
|
c7a291b46b | ||
|
|
eb4683ee3f | ||
|
|
f1fc573a65 | ||
|
|
524cd92cfe | ||
|
|
0d9e797889 | ||
|
|
3cd024d652 | ||
|
|
85233e0568 | ||
|
|
02403a1879 | ||
|
|
1c6316111d | ||
|
|
71aa1b1998 | ||
|
|
350f95c8f7 | ||
|
|
955b7971c0 | ||
|
|
8686876067 | ||
|
|
3a02eb12bf | ||
|
|
906707833e | ||
|
|
a2da822cb8 | ||
|
|
86fc135d9e | ||
|
|
5d953132e8 | ||
|
|
2858271c8f | ||
|
|
d22945ed5f | ||
|
|
4f7d2fd84a | ||
|
|
597ce7305f | ||
|
|
d7fa8ba5f1 | ||
|
|
1b356d350d |
+91
-1
@@ -14,7 +14,7 @@
|
||||
"login": "harttle",
|
||||
"name": "Jun Yang",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/4427974?v=4",
|
||||
"profile": "https://harttle.land",
|
||||
"profile": "https://github.com/harttle",
|
||||
"contributions": [
|
||||
"maintenance",
|
||||
"code"
|
||||
@@ -748,6 +748,96 @@
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "skynetigor",
|
||||
"name": "Ihor Panasiuk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20903171?v=4",
|
||||
"profile": "https://github.com/skynetigor",
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MorielHarush",
|
||||
"name": "MorielHarush",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/93482738?v=4",
|
||||
"profile": "https://github.com/MorielHarush",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "peaktwilight",
|
||||
"name": "Peak Twilight",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/77903714?v=4",
|
||||
"profile": "https://doruk.ch",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joecottam",
|
||||
"name": "Joe Cottam",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/44173086?v=4",
|
||||
"profile": "https://github.com/joecottam",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timbze",
|
||||
"name": "Timmy Braun",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/35117769?v=4",
|
||||
"profile": "https://github.com/timbze",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "talboren",
|
||||
"name": "Tal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/68807791?v=4",
|
||||
"profile": "https://github.com/talboren",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "VladimirFilonov",
|
||||
"name": "Vladimir Filonov",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/813224?v=4",
|
||||
"profile": "https://filonov.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
description: Architecture overview for liquidjs internals
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
## Async/sync duality via generators
|
||||
|
||||
All core logic is written once as a `Generator` function (`function *`). Use `yield` where you'd normally `await` a potentially async value.
|
||||
|
||||
- `toPromise(generator)` drives it **asynchronously** — awaits yielded promises.
|
||||
- `toValueSync(generator)` drives it **synchronously** — passes yielded values through as-is.
|
||||
|
||||
Never duplicate logic into separate async and sync methods. A single generator serves both paths.
|
||||
|
||||
When wrapping an async+sync function pair (e.g. `contains`/`containsSync`, `exists`/`existsSync`, `readFile`/`readFileSync`), use `toLiquidAsync(asyncFn, syncFn?)` which returns a `LiquidAsync<F>` — one function that picks the sync or async implementation based on a leading `sync: boolean` arg. Then `yield` the result inside a generator to let the driver handle it in both modes.
|
||||
|
||||
See `src/util/async.ts`.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
description: Project conventions for liquidjs
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- Keep edits minimal: change only what the task requires, match existing style.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
description: Testing conventions — e2e uses built dist, integration uses src
|
||||
globs: test/**/*.ts
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Testing
|
||||
|
||||
## End-to-end tests (`test/e2e`)
|
||||
|
||||
- **Use the built package**, not TypeScript sources under `src/`.
|
||||
- Import the public API from the package root (for example `import { Liquid } from '../..'`), which resolves through `package.json` to **`dist/`** (`main`, `module`, etc.).
|
||||
- **Avoid** `import … from '../../src/liquid'` (or other `src/` paths) in `test/e2e/**` so e2e matches what consumers get from npm and you do not depend on an unbuilt tree.
|
||||
|
||||
## Integration and unit tests
|
||||
|
||||
- Tests under `test/integration/`, `src/**/*.spec.ts`, and similar may import from **`src/`** when the suite is meant to run against the current TypeScript sources (typical for this repo’s Jest setup).
|
||||
@@ -4,6 +4,11 @@ jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -12,7 +17,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '22'
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Release
|
||||
|
||||
+115
@@ -1,3 +1,118 @@
|
||||
# [10.27.0](https://github.com/harttle/liquidjs/compare/v10.26.0...v10.27.0) (2026-05-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **context:** null-prototype scope frames via createScope ([#899](https://github.com/harttle/liquidjs/issues/899)) ([47d3f1b](https://github.com/harttle/liquidjs/commit/47d3f1b1cf33be91fe587821f288d1c9d8e1ace7))
|
||||
|
||||
# [10.26.0](https://github.com/harttle/liquidjs/compare/v10.25.7...v10.26.0) (2026-05-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **date:** cap strftime widths and account padding in memoryLimit ([#895](https://github.com/harttle/liquidjs/issues/895)) ([3129d46](https://github.com/harttle/liquidjs/commit/3129d46dc95efa357b00e5a57ee1af80a13d72ed))
|
||||
* enforce renderLimit for empty renderTemplates calls ([#894](https://github.com/harttle/liquidjs/issues/894)) ([5b9c346](https://github.com/harttle/liquidjs/commit/5b9c3469085e01c79e2d0af28e2a13f730e1793d))
|
||||
* propagate ownPropertyOnly into Context.spawn() for {% render %} ([#893](https://github.com/harttle/liquidjs/issues/893)) ([dbbf628](https://github.com/harttle/liquidjs/commit/dbbf6288030591bf6da28d8c1cce5a17bca97bb6))
|
||||
* **security:** block Object.prototype filter/tag lookups (RCE) ([#897](https://github.com/harttle/liquidjs/issues/897)) ([457fae0](https://github.com/harttle/liquidjs/commit/457fae0736c3ec862539b9dbf7f477e6c08fb6c6))
|
||||
* strip html newline tags ([#892](https://github.com/harttle/liquidjs/issues/892)) ([26ea285](https://github.com/harttle/liquidjs/commit/26ea2856c7a90aec892b98d94a9b7a3e18539045))
|
||||
* **strip_html:** rewrite as linear single-pass scan to avoid ReDoS ([#896](https://github.com/harttle/liquidjs/issues/896)) ([3616a74](https://github.com/harttle/liquidjs/commit/3616a744b9abeb425c217b340a2397d46176afb8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add sha256 and hmac_sha256 filters for cryptographic operations ([#889](https://github.com/harttle/liquidjs/issues/889)) ([1c816d4](https://github.com/harttle/liquidjs/commit/1c816d4fc3bcd2cba011f7a84f56a4251fca0622))
|
||||
|
||||
## [10.25.7](https://github.com/harttle/liquidjs/compare/v10.25.6...v10.25.7) (2026-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **filters:** support Buffer input in base64_encode to prevent binary data corruption ([#881](https://github.com/harttle/liquidjs/issues/881)) ([0ee6dbb](https://github.com/harttle/liquidjs/commit/0ee6dbb511aa926f6d490293282060abf3bab37f))
|
||||
|
||||
## [10.25.6](https://github.com/harttle/liquidjs/compare/v10.25.5...v10.25.6) (2026-04-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* nested block for layout ([#883](https://github.com/harttle/liquidjs/issues/883)) ([e2311df](https://github.com/harttle/liquidjs/commit/e2311dfd6e82f73509308aa8a3a1fafc92e226f0))
|
||||
|
||||
## [10.25.5](https://github.com/harttle/liquidjs/compare/v10.25.4...v10.25.5) (2026-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enforce root containment for renderFile/parseFile lookups ([#870](https://github.com/harttle/liquidjs/issues/870)) ([f41c1fc](https://github.com/harttle/liquidjs/commit/f41c1fc02fe901598f3328118b42b13bc6bc9b04))
|
||||
* null date should return empty ([#868](https://github.com/harttle/liquidjs/issues/868)) ([#872](https://github.com/harttle/liquidjs/issues/872)) ([4f9a499](https://github.com/harttle/liquidjs/commit/4f9a49988a93c156524981e189a4fec238e682b8))
|
||||
* rounding negative away from zero when half ([#873](https://github.com/harttle/liquidjs/issues/873)) ([1cdf10b](https://github.com/harttle/liquidjs/commit/1cdf10b57d82f0592414efbfca19e204b37aea9f))
|
||||
|
||||
## [10.25.4](https://github.com/harttle/liquidjs/compare/v10.25.3...v10.25.4) (2026-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sort and sort_natural filters bypass ownPropertyOnly ([#869](https://github.com/harttle/liquidjs/issues/869)) ([e743da0](https://github.com/harttle/liquidjs/commit/e743da0020d34e2ee547e1cc1a86b58377ebe1ce))
|
||||
|
||||
## [10.25.3](https://github.com/harttle/liquidjs/compare/v10.25.2...v10.25.3) (2026-04-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* precise memoryLimit for string replace ([abc058b](https://github.com/harttle/liquidjs/commit/abc058be0f33d6372cd2216f4945183167abeb25))
|
||||
* use realpath for fs.contains ([#867](https://github.com/harttle/liquidjs/issues/867)) ([529dd67](https://github.com/harttle/liquidjs/commit/529dd67eeb6b125637623d6a723601f0938d3613))
|
||||
|
||||
## [10.25.2](https://github.com/harttle/liquidjs/compare/v10.25.1...v10.25.2) (2026-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle undefined replacement argument in replace filter ([#864](https://github.com/harttle/liquidjs/issues/864)) ([0ad2b11](https://github.com/harttle/liquidjs/commit/0ad2b11ab15e7da608a9ef936b2a00a6a6517038))
|
||||
|
||||
## [10.25.1](https://github.com/harttle/liquidjs/compare/v10.25.0...v10.25.1) (2026-03-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mem limiter for invalid ranges ([95ddefc](https://github.com/harttle/liquidjs/commit/95ddefc056a11a44d9e753fd47a39db2c241e578))
|
||||
* treat args for replace_first as literal ([35d5230](https://github.com/harttle/liquidjs/commit/35d523026345d80458df24c72e653db78b5d061d))
|
||||
|
||||
# [10.25.0](https://github.com/harttle/liquidjs/compare/v10.24.0...v10.25.0) (2026-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* path traversal vulnerability, [#851](https://github.com/harttle/liquidjs/issues/851) ([#855](https://github.com/harttle/liquidjs/issues/855)) ([3cd024d](https://github.com/harttle/liquidjs/commit/3cd024d652dc883c46307581e979fe32302adbac))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* export error types, resolving [#837](https://github.com/harttle/liquidjs/issues/837) ([#840](https://github.com/harttle/liquidjs/issues/840)) ([71aa1b1](https://github.com/harttle/liquidjs/commit/71aa1b1998a3a66e536af67c6ea8947a28616eaf))
|
||||
|
||||
# [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Export specific tokens as types ([#824](https://github.com/harttle/liquidjs/issues/824)) ([4f7d2fd](https://github.com/harttle/liquidjs/commit/4f7d2fd84a8884e1009b13346d331a99b9721149))
|
||||
|
||||
# [10.22.0](https://github.com/harttle/liquidjs/compare/v10.21.1...v10.22.0) (2025-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* math filters coerce invalid string to 0, [#813](https://github.com/harttle/liquidjs/issues/813) ([#819](https://github.com/harttle/liquidjs/issues/819)) ([e8e502c](https://github.com/harttle/liquidjs/commit/e8e502c5854c9649bf7611a671a068dc260011d1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow context access in liquidMethodMissing, [#808](https://github.com/harttle/liquidjs/issues/808) ([#820](https://github.com/harttle/liquidjs/issues/820)) ([e551288](https://github.com/harttle/liquidjs/commit/e55128850e507687f9d85a012fc3a72ac2550f3b))
|
||||
|
||||
## [10.21.1](https://github.com/harttle/liquidjs/compare/v10.21.0...v10.21.1) (2025-05-14)
|
||||
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ For more details, refer to the [Setup Guide][setup].
|
||||
|
||||
- [Eleventy](https://www.11ty.dev/): Eleventy, a simpler static site generator.
|
||||
- [Github Docs](https://github.com/github/docs): The open-source repo for docs.github.com.
|
||||
- [Kibana](https://github.com/elastic/kibana): Elastic's analytics and visualization platform for Elasticsearch; workflow features use LiquidJS for Liquid templates.
|
||||
- [Opensense](https://www.opensense.com/): The smarter way to send email.
|
||||
- [Directus](https://docs.directus.io/): an instant REST+GraphQL API and intuitive no-code data collaboration app for any SQL database.
|
||||
- [Semgrep](https://github.com/returntocorp/semgrep): Lightweight static analysis for many languages.
|
||||
- [Rock](https://www.rockrms.com/): An open source CMS, Relationship Management System (RMS) and Church Management System (ChMS) all rolled into one.
|
||||
- [Mitosis](https://github.com/BuilderIO/mitosis): Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
|
||||
- [Pattern Lab](https://patternlab.io/): a frontend workshop environment that helps you build, view, test, and showcase your design system's UI components.
|
||||
@@ -64,6 +64,7 @@ For more details, refer to the [Setup Guide][setup].
|
||||
- [Microsoft Power Pages](https://learn.microsoft.com/en-us/power-pages/introduction): a secure, enterprise-grade, low-code software as a service (SaaS) platform for creating, hosting, and administering modern external-facing business websites.
|
||||
- [Azure API Management developer portal](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-developer-portal): an automatically generated, fully customizable website with the documentation of your APIs.
|
||||
- [WISMOlabs](https://wismolabs.com/): Post Purchase Experience platform for eCommerce retailers enhancing customer satisfaction by using LiquidJS to provide customizable post-purchase experiences through programmable email, SMS, order tracking pages, and webhooks.
|
||||
- [Freshet](https://chromewebstore.google.com/detail/freshet/mpclplhdencffbilobpcapccnihpelcg): *JSON in, page out* — a Chrome extension that uses LiquidJS templates per URL pattern, so the JSON becomes a rendered, useful page.
|
||||
|
||||
Feel free to create a PR or contact me to add your use case into this list!
|
||||
|
||||
@@ -72,35 +73,31 @@ 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.testmuai.com/?utm_medium=sponsor&utm_source=liquidjs" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/27130435?s=200&v=4" width="80" style="vertical-align: middle;" alt="TestMu AI" title="TestMu AI"/></a>
|
||||
<a href="https://github.com/talboren" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/68807791?v=4&s=100" height="80" style="vertical-align: middle;" alt="Tal" title="Tal (@talboren)"/></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 ✨
|
||||
@@ -113,7 +110,7 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://harttle.land"><img src="https://avatars3.githubusercontent.com/u/4427974?v=4?s=100" width="100px;" alt="Jun Yang"/><br /><sub><b>Jun Yang</b></sub></a><br /><a href="#maintenance-harttle" title="Maintenance">🚧</a> <a href="https://github.com/harttle/liquidjs/commits?author=harttle" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/harttle"><img src="https://avatars3.githubusercontent.com/u/4427974?v=4?s=100" width="100px;" alt="Jun Yang"/><br /><sub><b>Jun Yang</b></sub></a><br /><a href="#maintenance-harttle" title="Maintenance">🚧</a> <a href="https://github.com/harttle/liquidjs/commits?author=harttle" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chenos"><img src="https://avatars0.githubusercontent.com/u/2993310?v=4?s=100" width="100px;" alt="chenos"/><br /><sub><b>chenos</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=chenos" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://zachleat.com/"><img src="https://avatars2.githubusercontent.com/u/39355?v=4?s=100" width="100px;" alt="Zach Leatherman"/><br /><sub><b>Zach Leatherman</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/issues?q=author%3Azachleat" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/thardy"><img src="https://avatars3.githubusercontent.com/u/120636?v=4?s=100" width="100px;" alt="Tim Hardy"/><br /><sub><b>Tim Hardy</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=thardy" title="Code">💻</a></td>
|
||||
@@ -216,6 +213,18 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/edh649"><img src="https://avatars.githubusercontent.com/u/527604?v=4?s=100" width="100px;" alt="Ed Hanton"/><br /><sub><b>Ed Hanton</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=edh649" title="Documentation">📖</a></td>
|
||||
<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>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MorielHarush"><img src="https://avatars.githubusercontent.com/u/93482738?v=4?s=100" width="100px;" alt="MorielHarush"/><br /><sub><b>MorielHarush</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=MorielHarush" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://doruk.ch"><img src="https://avatars.githubusercontent.com/u/77903714?v=4?s=100" width="100px;" alt="Peak Twilight"/><br /><sub><b>Peak Twilight</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=peaktwilight" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/joecottam"><img src="https://avatars.githubusercontent.com/u/44173086?v=4?s=100" width="100px;" alt="Joe Cottam"/><br /><sub><b>Joe Cottam</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=joecottam" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/timbze"><img src="https://avatars.githubusercontent.com/u/35117769?v=4?s=100" width="100px;" alt="Timmy Braun"/><br /><sub><b>Timmy Braun</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=timbze" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/talboren"><img src="https://avatars.githubusercontent.com/u/68807791?v=4?s=100" width="100px;" alt="Tal"/><br /><sub><b>Tal</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=talboren" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://filonov.dev"><img src="https://avatars.githubusercontent.com/u/813224?v=4?s=100" width="100px;" alt="Vladimir Filonov"/><br /><sub><b>Vladimir Filonov</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=VladimirFilonov" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ Only the latest major version is supported with security updates. It can be chan
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please contact yangjvn@126.com to report a vulnerability or change request.
|
||||
Please contact harttleharttle@gmail.com to report a vulnerability or change request.
|
||||
|
||||
- If the vulnerability in question affects common use cases, it will be treated as a bug and fixed very soon (typically within 1 week).
|
||||
- Otherwise, it'll be scheduled in the same priority of feature request (which is lower than bugs).
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rm -rf docs/source/api
|
||||
typedoc --plugin typedoc-plugin-missing-exports ./src --gitRevision master --out docs/source/api
|
||||
@@ -0,0 +1,22 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const src = path.join(root, 'CHANGELOG.md')
|
||||
|
||||
let content = fs.readFileSync(src, 'utf8').replace(/\r\n/g, '\n')
|
||||
|
||||
const lines = content.split('\n')
|
||||
lines[0] = lines[0]
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
content = lines.join('\n')
|
||||
|
||||
content = content
|
||||
.replace(/{%/g, '{% raw %}{%{% endraw %}')
|
||||
.replace(/\{\{/g, '{% raw %}{{{% endraw %}')
|
||||
|
||||
const enFrontmatter = '---\ntitle: Changelog\nauto: true\n---\n\n'
|
||||
|
||||
fs.writeFileSync(path.join(root, 'docs/source/tutorials/changelog.md'), enFrontmatter + content)
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd docs
|
||||
cp ../CHANGELOG.md source/tutorials/changelog.md
|
||||
sed -i \
|
||||
-e 's/{%/{% raw %}{%{% endraw %}/g' \
|
||||
-e 's/{{/{% raw %}{{{% endraw %}/g' \
|
||||
-e '1 s/"/\"/g' \
|
||||
-e '1 s/</\</g' \
|
||||
-e '1 s/>/\>/g' \
|
||||
source/tutorials/changelog.md
|
||||
cp source/tutorials/changelog.md source/zh-cn/tutorials/changelog.md
|
||||
|
||||
sed -i -e '1i\---\ntitle: Changelog\nauto: true\n---\n' source/tutorials/changelog.md
|
||||
sed -i -e '1i\---\ntitle: 更新日志\nauto: true\n---\n' source/zh-cn/tutorials/changelog.md
|
||||
@@ -0,0 +1,39 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const readme = fs.readFileSync(path.join(root, 'README.md'), 'utf8').replace(/\r\n/g, '\n')
|
||||
|
||||
function extractSection (text, beginMarker, endMarker) {
|
||||
const lines = text.split('\n')
|
||||
let inside = false
|
||||
const result = []
|
||||
for (const line of lines) {
|
||||
if (line.includes(endMarker)) inside = false
|
||||
if (inside) result.push(line)
|
||||
if (line.includes(beginMarker)) inside = true
|
||||
}
|
||||
return result.join('\n')
|
||||
}
|
||||
|
||||
function transformContributors (html) {
|
||||
return html
|
||||
.replace(/<br \/>.*?<\/td>/g, '</a></td>')
|
||||
.replace(/width="[^"]*"/g, '')
|
||||
.replace(/\n/g, '')
|
||||
.replace(/<\/tr>\s*<tr>/g, '')
|
||||
}
|
||||
|
||||
function transformFinancial (html) {
|
||||
return html
|
||||
.replace(/<br \/>.*?<\/td>/g, '</a></td>')
|
||||
.replace(/\n/g, '')
|
||||
.replace(/<\/tr>\s*<tr>/g, '')
|
||||
}
|
||||
|
||||
const allContributors = transformContributors(extractSection(readme, 'ALL-CONTRIBUTORS-LIST:START', 'ALL-CONTRIBUTORS-LIST:END'))
|
||||
const financialContributors = transformFinancial(extractSection(readme, 'FINANCIAL-CONTRIBUTORS-BEGIN', 'FINANCIAL-CONTRIBUTORS-END'))
|
||||
|
||||
const outDir = path.join(root, 'docs/themes/navy/layout/partial')
|
||||
fs.writeFileSync(path.join(outDir, 'all-contributors.swig'), allContributors)
|
||||
fs.writeFileSync(path.join(outDir, 'financial-contributors.swig'), financialContributors)
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run `sed` in a way that's compatible with both macOS (BSD) and Linux (GNU)
|
||||
sedi() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
/usr/bin/sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# create docs/themes/navy/layout/partial/all-contributors.swig
|
||||
awk '/ALL-CONTRIBUTORS-LIST:START/{flag=1;next}/ALL-CONTRIBUTORS-LIST: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/all-contributors.swig
|
||||
|
||||
# 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
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BUNDLES=min npm run build
|
||||
mkdir -p docs/public/js/
|
||||
cp dist/liquid.browser.min.js docs/public/js/
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
./bin/build-docs-liquid.sh
|
||||
./bin/build-contributors.sh
|
||||
./bin/build-apidoc.sh
|
||||
./bin/build-changelog.sh
|
||||
|
||||
cd docs
|
||||
npm ci
|
||||
npm run build
|
||||
cp CNAME public/
|
||||
|
||||
if [ "$HEXO_ALGOLIA_INDEXING_KEY" != "" ]; then
|
||||
npm run index
|
||||
fi
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prerequisites:
|
||||
# 1.imagemagick. try brew install imagemagick
|
||||
# 2. logo.png in size 512x512
|
||||
# 3. this script should be run with cwd docs/source/icon/
|
||||
|
||||
echo creating apple touch icons...
|
||||
apple=(57x57 60x60 72x72 76x76 114x114 120x120 144x144 152x152)
|
||||
for size in "${apple[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size apple-touch-icon-$size.png
|
||||
done
|
||||
cp logo.png apple-touch-icon.png
|
||||
convert logo.png \
|
||||
\( +clone -alpha extract \
|
||||
-draw 'fill black polygon 0,0 0,80 80,0 fill white circle 80,80 80,0' \
|
||||
\( +clone -flip \) -compose Multiply -composite \
|
||||
\( +clone -flop \) -compose Multiply -composite \
|
||||
\) -alpha off -compose CopyOpacity -composite apple-touch-icon-precomposed.png
|
||||
|
||||
echo creating favicon...
|
||||
convert logo.png -resize 48x48 ../favicon.ico
|
||||
favicon=(16x16 32x32 96x96 160x160 196x196)
|
||||
for size in "${favicon[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size favicon-$size.png
|
||||
done
|
||||
|
||||
echo creating mstile icons...
|
||||
mstile=(70x70 144x144 150x150 310x310)
|
||||
for size in "${mstile[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size mstile-$size.png
|
||||
done
|
||||
convert mstile-150x150.png -gravity center -background white -extent 310x150 mstile-310x150.png
|
||||
@@ -0,0 +1,17 @@
|
||||
const { execSync } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const version = require(path.join(root, 'package.json')).version
|
||||
|
||||
const fileLocal = path.join(root, 'dist/liquid.node.js')
|
||||
const fileLatest = path.join(root, `dist/liquid.node.${version}.js`)
|
||||
|
||||
if (!fs.existsSync(fileLatest)) {
|
||||
const url = `https://unpkg.com/liquidjs@${version}/dist/liquid.node.js`
|
||||
console.log(`Downloading liquidjs@${version}...`)
|
||||
execSync(`curl -sL -o "${fileLatest}" "${url}"`)
|
||||
}
|
||||
|
||||
execSync(`node benchmark/diff.js "${fileLocal}" "${fileLatest}"`, { cwd: root, stdio: 'inherit' })
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION_LATEST=$(cat package.json | grep '"version":' | head -1 | awk -F'"' '{print $4}')
|
||||
FILE_LOCAL=dist/liquid.node.js
|
||||
FILE_LATEST=dist/liquid.node.$VERSION_LATEST.js
|
||||
URL_LATEST=https://unpkg.com/liquidjs@$VERSION_LATEST/dist/liquid.node.js
|
||||
|
||||
if [ ! -f "$FILE_LATEST" ]; then
|
||||
curl $URL_LATEST > $FILE_LATEST
|
||||
fi
|
||||
|
||||
exec node benchmark/diff.js $FILE_LOCAL $FILE_LATEST
|
||||
@@ -8,7 +8,7 @@
|
||||
"test": "echo not implemented",
|
||||
"start": "http-server -c-1 "
|
||||
},
|
||||
"author": "harttle <yangjvn@126.com>",
|
||||
"author": "harttle <harttleharttle@gmail.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"http-server": "^0.11.1",
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ const engine = new Liquid({
|
||||
// layout files for `{% layout %}`
|
||||
layouts: process.cwd() + '/layouts',
|
||||
// partial files for `{% include %}` and `{% render %}`
|
||||
partials: process.cwd() + '/partials'
|
||||
partials: [process.cwd() + '/partials', 'node_modules']
|
||||
})
|
||||
|
||||
const ctx = {
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
npm start | grep 'LiquidJS Demo'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set -x
|
||||
set -e
|
||||
|
||||
LOG_FILE=$(mktemp)
|
||||
npm start > $LOG_FILE 2>&1 &
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
npm start | grep 'NodeJS Demo for LiquidJS'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
npm start | grep '\[11:8] {{ todo }}'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
npm run build && npm start | grep 'TypeScript Demo for LiquidJS'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
npm run build
|
||||
npm start | grep 'Webpack Demo for LiquidJS'
|
||||
|
||||
+3
-5
@@ -1,10 +1,8 @@
|
||||
title: LiquidJS
|
||||
subtitle: "A simple, expressive and safe template engine."
|
||||
description: "LiquidJS is a simple, expressive and safe Shopify / GitHub Pages compatible template engine in pure JavaScript."
|
||||
subtitle: "A simple, expressive, and safe template engine for JavaScript."
|
||||
description: "LiquidJS is a simple, expressive, and safe template engine for JavaScript, compatible with Shopify and GitHub Pages."
|
||||
author: Harttle
|
||||
language:
|
||||
- en
|
||||
- zh-cn
|
||||
language: en
|
||||
timezone: UTC
|
||||
|
||||
url: https://liquidjs.com
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
en: English
|
||||
zh-cn:
|
||||
name: 简体中文
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
-
|
||||
url: https://opencollective.com/liquidjs/#section-contribute
|
||||
date: '2020-02-26'
|
||||
title:
|
||||
zh-cn: '赞助人:第一个 backer 通过 Open Collective 贡献于 LiquidJS。'
|
||||
en: 'Backers: the first backer contributed to LiquidJS via Open Collective.'
|
||||
-
|
||||
url: https://github.com/harttle/liquidjs/pull/202
|
||||
date: '2020-03-11'
|
||||
title:
|
||||
zh-cn: '内存优化:用更精细的手法重写了解析器,来避免临时字符串的生成,内存占用降低 57.7% 以上。'
|
||||
en: 'Memory Optimization: a more elaborate parser reducing the memory footprint by 57.7%.'
|
||||
-
|
||||
url: https://github.com/harttle/liquidjs/pull/205
|
||||
date: '2020-03-15'
|
||||
title:
|
||||
zh-cn: '性能提升:引入 AST 并重新设计 Token 类型系统,使渲染性能平均提升 100.3%。'
|
||||
en: 'Performance Boost: a simple AST to improve render performance by 100.3%.'
|
||||
-
|
||||
url: https://github.com/harttle/liquidjs/milestone/3?closed=1
|
||||
date: '2021-09-30'
|
||||
title:
|
||||
zh-cn: '流式渲染:4 倍渲染速度,并增加了对流式渲染的支持。'
|
||||
en: 'Streamed Rendering: now render is 4x faster and support streamed rendering.'
|
||||
@@ -19,7 +19,7 @@ tutorials:
|
||||
plugins: plugins.html
|
||||
operators: operators.html
|
||||
truth: truthy-and-falsy.html
|
||||
dos: dos.html
|
||||
security_model: security-model.html
|
||||
static_analysis: static-analysis.html
|
||||
miscellaneous:
|
||||
migration9: migrate-to-9.html
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: hmac_sha256
|
||||
---
|
||||
|
||||
{% since %}vNEXT{% endsince %}
|
||||
|
||||
Converts a string into an SHA-256 hash using a hash message authentication code (HMAC). The secret key is passed as the filter argument. The output is a lowercase hexadecimal string.
|
||||
|
||||
Input
|
||||
|
||||
```liquid
|
||||
{%- assign secret_potion = 'Polyjuice' | hmac_sha256: 'Polina' -%}
|
||||
My secret potion: {{ secret_potion }}
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
My secret potion: 8e0d5d65cff1242a4af66c8f4a32854fd5fb80edcc8aabe9b302b29c7c71dc20
|
||||
```
|
||||
@@ -15,5 +15,7 @@ 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
|
||||
Crypto | sha256, hmac_sha256
|
||||
|
||||
[shopify/liquid]: https://github.com/Shopify/liquid
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: sha256
|
||||
---
|
||||
|
||||
{% since %}vNEXT{% endsince %}
|
||||
|
||||
Converts a string into an SHA-256 hash. The output is a lowercase hexadecimal string.
|
||||
|
||||
Input
|
||||
|
||||
```liquid
|
||||
{%- assign secret_potion = 'Polyjuice' | sha256 -%}
|
||||
My secret potion: {{ secret_potion }}
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
My secret potion: 44ac1d7a2936e30a5de07082fd65d6fe9b1fb658a1a98bfe65bc5959beac5dd0
|
||||
```
|
||||
@@ -7,23 +7,23 @@ ul#intro-feature-list
|
||||
.intro-feature
|
||||
.intro-feature-icon
|
||||
i.icon-shield
|
||||
h3.intro-feature-title Safe Rendering
|
||||
p.intro-feature-desc Liquid templates are highly readable and fault-tolerant thus suitable for designers and customers. Operators and expressions are parsed to AST and no #[code eval] or #[code new Function] are used.
|
||||
h3.intro-feature-title Safe & Typed
|
||||
p.intro-feature-desc Templates are readable and fault-tolerant, parsed to an AST with no #[code eval] or #[code new Function]. The whole repo is written in TypeScript strict mode, so types stay precise and docs accurate.
|
||||
li.intro-feature-wrap
|
||||
.intro-feature
|
||||
.intro-feature-icon
|
||||
i.icon-rocket
|
||||
h3.intro-feature-title Pure JavaScript
|
||||
p.intro-feature-desc Written with pure JavaScript with no native bindings, available in both Node.js and browsers. All of the CMD, ESM and CJS bundles are available on CDN.
|
||||
p.intro-feature-desc Written in pure JavaScript with no native bindings, running in both Node.js and the browser. The CMD, ESM and CJS bundles are all available on CDN.
|
||||
li.intro-feature-wrap
|
||||
.intro-feature
|
||||
.intro-feature-icon
|
||||
i.icon-shopify
|
||||
h3.intro-feature-title Shopify Compatible
|
||||
p.intro-feature-desc All filters and tags from Ruby #[a(href="https://github.com/shopify/liquid") shopify/liquid] are supported by LiquidJS. #[a(href="https://jekyllrb.com/") Jekyll sites], #[a(href="https://pages.github.com/") GitHub Pages] and #[a(href="https://themes.shopify.com/") Shopify templates] can be ported to Node.js without pain.
|
||||
h3.intro-feature-title Shopify & Jekyll
|
||||
p.intro-feature-desc All filters and tags from Ruby #[a(href="https://github.com/shopify/liquid") shopify/liquid] are supported, so #[a(href="https://themes.shopify.com/") Shopify templates] work out of the box — as do #[a(href="https://jekyllrb.com/") Jekyll] sites and #[a(href="https://pages.github.com/") GitHub Pages].
|
||||
li.intro-feature-wrap
|
||||
.intro-feature
|
||||
.intro-feature-icon
|
||||
i.icon-typescript
|
||||
h3.intro-feature-title TypeScript Strict
|
||||
p.intro-feature-desc The whole repo is re-written in TypeScript strict mode to ensure a smooth experience using this lib and the document is precise and always up to date.
|
||||
i.icon-network
|
||||
h3.intro-feature-title Streaming
|
||||
p.intro-feature-desc Render directly to a Node.js stream with #[code renderToNodeStream], emitting output as it's produced — for a faster time to first byte and low memory usage on large pages.
|
||||
@@ -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
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
title: DoS Prevention
|
||||
---
|
||||
|
||||
When the template or data context cannot be trusted, enabling DoS prevention options is crucial. LiquidJS provides 3 options for this purpose: `parseLimit`, `renderLimit`, and `memoryLimit`.
|
||||
|
||||
## TL;DR
|
||||
|
||||
Setting these options can largely ensure that your LiquidJS instance won't hang for extended periods or consume excessive memory. These limits are based on the available JavaScript APIs, so they are not precise hard limits but thresholds to help prevent your process from failing or hanging.
|
||||
|
||||
```typescript
|
||||
const liquid = new Liquid({
|
||||
parseLimit: 1e8, // typical size of your templates in each render
|
||||
renderLimit: 1000, // limit each render to be completed in 1s
|
||||
memoryLimit: 1e9, // memory available for LiquidJS (1e9 for 1GB)
|
||||
})
|
||||
```
|
||||
|
||||
When a `parse()` or `render()` cannot be completed within given resource, it throws.
|
||||
|
||||
## parseLimit
|
||||
|
||||
[parseLimit][parseLimit] restricts the size (character length) of templates parsed in each `.parse()` call, including referenced partials and layouts. Since LiquidJS parses template strings in near O(n) time, limiting total template length is usually sufficient.
|
||||
|
||||
A typical PC handles `1e8` (100M) characters without issues.
|
||||
|
||||
## renderLimit
|
||||
|
||||
Restricting template size alone is insufficient because dynamic loops with large counts can occur in render time. [renderLimit][renderLimit] mitigates this by limiting the time consumed by each `render()` call.
|
||||
|
||||
```liquid
|
||||
{%- for i in (1..10000000) -%}
|
||||
order: {{i}}
|
||||
{%- endfor -%}
|
||||
```
|
||||
|
||||
Render time is checked on a per-template basis (before rendering each template). In the above example, there are 2 templates in the loop: `order: ` and `{{i}}`, render time will be checked 10000000x2 times.
|
||||
|
||||
For time-consuming tags and filters within a single template, the process can still hang. For fully controlled rendering, consider using a process manager like [paralleljs][paralleljs].
|
||||
|
||||
## memoryLimit
|
||||
|
||||
Even with small number of templates and iterations, memory usage can grow exponentially. In the following example, memory doubles with each iteration:
|
||||
|
||||
```liquid
|
||||
{% assign array = "1,2,3" | split: "," %}
|
||||
{% for i in (1..32) %}
|
||||
{% assign array = array | concat: array %}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
[memoryLimit][memoryLimit] restricts memory-sensitive filters to prevent excessive memory allocation. As [JavaScript uses GC to manage memory](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management), `memoryLimit` limits only the total number of objects allocated by memory sensitive filters in LiquidJS thus may not reflect the actual memory footprint.
|
||||
|
||||
[paralleljs]: https://www.npmjs.com/package/paralleljs
|
||||
[parseLimit]: /api/interfaces/LiquidOptions.html#parseLimit
|
||||
[renderLimit]: /api/interfaces/LiquidOptions.html#renderLimit
|
||||
[memoryLimit]: /api/interfaces/LiquidOptions.html#memoryLimit
|
||||
@@ -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
|
||||
|
||||
@@ -45,26 +45,20 @@ It can be a string-typed path (see above example), or a list of root directories
|
||||
|
||||
```javascript
|
||||
var engine = new Liquid({
|
||||
root: ['views/', 'views/partials/'],
|
||||
root: ['views/'],
|
||||
partials: ['views/partials/'],
|
||||
layouts: ['views/layouts/'],
|
||||
extname: '.liquid'
|
||||
});
|
||||
```
|
||||
|
||||
{% note tip Relative Paths %}Relative paths in <code>root</code> will be resolved against <code>cwd()</code>.{% endnote %}
|
||||
|
||||
When `{% raw %}{% render "foo" %}{% endraw %}` is rendered or `liquid.renderFile('foo')` is called, the following files will be looked up and the first existing file will be used:
|
||||
- When `parse()`, `render()` functions are called, for example `liquid.renderFile('foo')`, templates under `root` will be looked up.
|
||||
- When a partial is requested, for example `{% raw %}{% render "foo" %}{% endraw %}`, templates under `partials` will be looked up.
|
||||
- When a layout is requested, for example `{% raw %}{% layout "foo" %}{% endraw %}`, templates under `layouts` will be looked up.
|
||||
|
||||
- `cwd()`/views/foo.liquid
|
||||
- `cwd()`/views/partials/foo.liquid
|
||||
|
||||
If none of the above files exists, an `ENOENT` error will be thrown. Here's a demo for Node.js: [demo/nodejs](https://github.com/harttle/liquidjs/tree/master/demo/nodejs).
|
||||
|
||||
When LiquidJS is used in browser, say current location is <https://example.com/bar/index.html>, only the first `root` will be used and the file to be fetched is:
|
||||
|
||||
- <https://example.com/bar/foo.liquid>
|
||||
|
||||
If fetch fails, a 404/500 error or network failures for example, an `ENOENT` error will be thrown.
|
||||
Here's a demo for browsers: [demo/browser](https://github.com/harttle/liquidjs/tree/master/demo/browser).
|
||||
When LiquidJS is used in browser, the paths will be resolved based on current location. Here's a demo for browsers: [demo/browser](https://github.com/harttle/liquidjs/tree/master/demo/browser).
|
||||
|
||||
## Abstract File System
|
||||
|
||||
@@ -98,7 +92,7 @@ var engine = new Liquid({
|
||||
});
|
||||
```
|
||||
|
||||
{% note warn Path Traversal Vulnerability %}The default value of <code>contains()</code> always returns true. That means when specifying an abstract file system, you'll need to provide a proper <code>contains()</code> to avoid expose such vulnerabilities.{% endnote %}
|
||||
{% note warn Path Traversal Vulnerability %}The built-in Node <code>fs</code> implements <code>contains()</code> with realpath so templates cannot escape the root via symlinks. The browser bundle omits <code>contains</code> (loader treats paths as allowed). For a custom abstract <code>fs</code>, implement <code>contains</code> unless every resolved path is trusted.{% endnote %}
|
||||
|
||||
## In-memory Template
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Security Model
|
||||
---
|
||||
|
||||
LiquidJS provides DoS-oriented limits (`parseLimit`, `renderLimit`, `memoryLimit`) to reduce risk. This page summarizes those limits, [`ownPropertyOnly`][ownPropertyOnly], custom [`Drop`][drop] usage, and the security boundary to assume in production.
|
||||
|
||||
## Security boundary
|
||||
|
||||
The built-in limits are cooperative safeguards, not strict runtime isolation.
|
||||
|
||||
- They do **not** equal process RSS/heap usage.
|
||||
- They do **not** sandbox JavaScript execution.
|
||||
- They should be combined with process/container limits and request timeouts for defense in depth.
|
||||
|
||||
## Limits at a glance
|
||||
|
||||
- [parseLimit][parseLimit]: limit total template size per `parse()` call.
|
||||
- [renderLimit][renderLimit]: limit total render time per `render()` call.
|
||||
- [memoryLimit][memoryLimit]: cooperatively limit memory-sensitive allocations counted by LiquidJS.
|
||||
|
||||
## Limit details
|
||||
|
||||
### parseLimit
|
||||
|
||||
[parseLimit][parseLimit] restricts the size (character length) of templates parsed in each `.parse()` call, including referenced partials and layouts. Since LiquidJS parses template strings in near O(n) time, limiting total template length is usually sufficient.
|
||||
|
||||
A typical PC handles `1e8` (100M) characters without issues.
|
||||
|
||||
### renderLimit
|
||||
|
||||
Restricting template size alone is insufficient because dynamic loops with large counts can occur in render time. [renderLimit][renderLimit] mitigates this by limiting the time consumed by each `render()` call.
|
||||
|
||||
```liquid
|
||||
{%- for i in (1..10000000) -%}
|
||||
order: {{i}}
|
||||
{%- endfor -%}
|
||||
```
|
||||
|
||||
Render time is checked on a per-template basis (before rendering each template). In the above example, there are 2 templates in the loop: `order: ` and `{{i}}`, render time will be checked 10000000x2 times.
|
||||
|
||||
`renderLimit` is not a hard CPU limiter. It is checked between template renders, so compute-intensive filters/tags/user-defined functions or deeply nested template execution between checks can still cause DoS.
|
||||
|
||||
### memoryLimit
|
||||
|
||||
`memoryLimit` only limits operations that LiquidJS explicitly counts.
|
||||
|
||||
- Counted: memory-sensitive LiquidJS operations that call internal memory accounting.
|
||||
- Not guaranteed counted: arbitrary user object behavior such as custom `toValue()`/`toString()` chains, or other host-side code that allocates outside LiquidJS accounting points.
|
||||
|
||||
In other words, `memoryLimit` limits what LiquidJS counts, not every byte your process may allocate.
|
||||
|
||||
Even with small number of templates and iterations, memory usage can grow exponentially. In the following example, memory doubles with each iteration:
|
||||
|
||||
```liquid
|
||||
{% assign array = "1,2,3" | split: "," %}
|
||||
{% for i in (1..32) %}
|
||||
{% assign array = array | concat: array %}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
As [JavaScript uses GC to manage memory](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management), `memoryLimit` may not reflect the actual memory footprint.
|
||||
|
||||
## `ownPropertyOnly` and scope data
|
||||
|
||||
With [`ownPropertyOnly`][ownPropertyOnly] `true`, plain scope objects only expose **own** properties (no inherited / `Object.prototype` keys). Default `false` follows normal JS property access. Use `true` for untrusted or polluted objects; add [`strictVariables`][strictVariables] if missing paths should error. Override per render via [`RenderOptions`][renderOwnPropertyOnly]. This is a read policy for scope data—not a sandbox for filters, tags, or your code.
|
||||
|
||||
## Custom `Drop` classes
|
||||
|
||||
[`Drop`][drop] values are not restricted the same way: LiquidJS still reads the prototype chain and may call [`liquidMethodMissing`][liquidMethodMissing]. **You** control what a drop exposes; narrow APIs and never feed unsafe data into drops unless the class is built for template access. `ownPropertyOnly` alone does not harden custom drops—audit them like any privileged code.
|
||||
|
||||
## Online service guidance
|
||||
|
||||
If you run an online service, avoid rendering fully user-defined templates whenever possible.
|
||||
|
||||
- Prefer curated templates or a restricted template subset.
|
||||
- If user-defined templates are required, isolate rendering (worker/process/container), enforce OS/container memory and CPU limits, and apply request rate limits.
|
||||
- Treat `parseLimit`/`renderLimit`/`memoryLimit` as one layer in a broader DoS defense strategy.
|
||||
|
||||
For heavy single-template operations, process-level isolation is still recommended (for example with [paralleljs][paralleljs]).
|
||||
|
||||
[paralleljs]: https://www.npmjs.com/package/paralleljs
|
||||
[parseLimit]: /api/interfaces/LiquidOptions.html#parseLimit
|
||||
[renderLimit]: /api/interfaces/LiquidOptions.html#renderLimit
|
||||
[memoryLimit]: /api/interfaces/LiquidOptions.html#memoryLimit
|
||||
[ownPropertyOnly]: /api/interfaces/LiquidOptions.html#ownPropertyOnly
|
||||
[renderOwnPropertyOnly]: /api/interfaces/RenderOptions.html#ownPropertyOnly
|
||||
[strictVariables]: /api/interfaces/LiquidOptions.html#strictVariables
|
||||
[drop]: /api/classes/Drop.html
|
||||
[liquidMethodMissing]: /api/classes/Drop.html#liquidMethodMissing
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
title: abs
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回数字的绝对值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ -17 | abs }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
17
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | abs }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4
|
||||
```
|
||||
|
||||
对于只包含数字的字符串也好使:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "-19.86" | abs }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
19.86
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: append
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
连接两个字符串并返回结果。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "/my/fancy/url" | append: ".html" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
/my/fancy/url.html
|
||||
```
|
||||
|
||||
也可以用于变量。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign filename = "/index.html" %}
|
||||
{{ "website.com" | append: filename }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
website.com/index.html
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: array_to_sentence_string
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把数组转化为句子,用于做标签列表。有一个可选的连接词参数。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "foo,bar,baz" | split: "," | array_to_sentence_string }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
foo, bar, and baz
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "foo,bar,baz" | split: "," | array_to_sentence_string: "or" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
foo, bar, or baz
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: at_least
|
||||
---
|
||||
|
||||
{% since %}v8.4.0{% endsince %}
|
||||
|
||||
限制数字到某个最小值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | at_least: 5 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
5
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | at_least: 3 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: at_most
|
||||
---
|
||||
|
||||
{% since %}v8.4.0{% endsince %}
|
||||
|
||||
限制数字到某个最大值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | at_most: 5 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | at_most: 3 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
3
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
title: capitalize
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串首字母改为大写。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "title" | capitalize }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Title
|
||||
```
|
||||
|
||||
`capitalize` 只会大写首字母,因此后续单词的不会受影响:
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "my great title" | capitalize }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
My great title
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: ceil
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
向上取整,取整前 LiquidJS 会首先把输入转换为数字。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 1.2 | ceil }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 2.0 | ceil }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | ceil }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
184
|
||||
```
|
||||
|
||||
下面的例子中输入是字符串:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "3.5" | ceil }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: cgi_escape
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把字符串 CGI 转义,用于 URL。用对应的 `%XX` 替换特殊字符,空格会被转义为 `+` 号。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "foo, bar; baz?" | cgi_escape }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
foo%2C+bar%3B+baz%3F
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: compact
|
||||
---
|
||||
|
||||
{% since %}v9.22.0{% endsince %}
|
||||
|
||||
从数组里移除任何 `null` 和 `undefined` 值。
|
||||
|
||||
假设 `site.pages` 是网页列表,有些网页包含 `category` 属性用来标明类别。如果把它们 `map` 到数组里,那么对于没有 `category` 属性的元素就会是 `undefined`。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign site_categories = site.pages | map: "category" %}
|
||||
|
||||
{% for category in site_categories %}
|
||||
- {{ category }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- business
|
||||
- celebrities
|
||||
-
|
||||
- lifestyle
|
||||
- sports
|
||||
-
|
||||
- technology
|
||||
```
|
||||
|
||||
使用 `compact` 创建 `site_categories` 数组,可以移除所有 `null` 和 `undefined` 值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign site_categories = site.pages | map: "category" | compact %}
|
||||
|
||||
{% for category in site_categories %}
|
||||
- {{ category }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- business
|
||||
- celebrities
|
||||
- lifestyle
|
||||
- sports
|
||||
- technology
|
||||
```
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
title: concat
|
||||
---
|
||||
|
||||
{% since %}v2.0.0{% endsince %}
|
||||
|
||||
连接多个数组,返回的数组包含所有传入数组的元素。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
{% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | concat: vegetables %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
- carrots
|
||||
- turnips
|
||||
- potatoes
|
||||
```
|
||||
|
||||
可以链式地使用 `concat` 过滤器来连接多个数组:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign furniture = "chairs, tables, shelves" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | concat: vegetables | concat: furniture %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
- carrots
|
||||
- turnips
|
||||
- potatoes
|
||||
- chairs
|
||||
- tables
|
||||
- shelves
|
||||
```
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
title: date
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把时间戳转换为字符串。LiquidJS 尝试跟 Shopify/Liquid 保持一致,它用的是 Ruby 核心的 [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime)。此外 LiquidJS 会先通过 [new Date()][newDate] 尝试把输入转换为 Date 对象。
|
||||
|
||||
但 LiquidJS 支持的格式与 [Ruby 的 flag](https://ruby-doc.org/core/strftime_formatting_rdoc.html) 有些不同:
|
||||
* `%Z`(自 v10.11.1 起支持)只有在传入了时区时才起作用(可以通过 `LiquidOption` 传入,也可以在创建日期时单独传入,见下文)。如果传入的时区是个数字,那么它的表现将会与 `%z` 相同。如果没有传入时区,将会返回 [运行时默认时区](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#timezone)。
|
||||
* LiquidJS 提供额外的 `%q` 用来处理序数:`{{ '2023/02/02' | date: '%d%q of %b'}}` => `02nd of Feb`
|
||||
* 日期字面量会通过 [new Date()][jsDate] 转化为 `Date` 对象,这意味着字面量默认使用运行时默认时区。
|
||||
* 格式字参数是可选的:
|
||||
* 如果不传,默认为 `%A, %B %-e, %Y at %-l:%M %P %z`。
|
||||
* 上述默认值可以通过 [`dateFormat`](/api/interfaces/LiquidOptions.html#dateFormat) 参数覆盖。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ article.published_at | date: "%a, %b %d, %y" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Fri, Jul 17, 15
|
||||
```
|
||||
|
||||
{% note info 时区 %}
|
||||
日期在输出时会转换为当地时区,设置 `timezoneOffset` LiquidJS 参数可以指定一个不同的时区。或者设置 `preserveTimezones` 为 `true` 来保持字面量时间戳的时区,数据中的日期对象不受此参数的影响。
|
||||
{% endnote %}
|
||||
|
||||
你也可以在使用 `date` 时再设置时区:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360}} // 等价于设置 `options.timezoneOffset` to `360`.
|
||||
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```liquid
|
||||
1990-12-31T17:00:00
|
||||
1991-01-01T04:30:00
|
||||
```
|
||||
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ article.published_at | date: "%Y" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2015
|
||||
```
|
||||
|
||||
输入也可以是符合 JavaScript `Date` 格式的字符串::
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "March 14, 2016" | date: "%b %d, %y" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Mar 14, 16
|
||||
```
|
||||
|
||||
{% note info 时间戳字符串 %}
|
||||
LiquidJS 使用 JavaScript [Date][newDate] 来解析输入字符串,意味着支持 [IETF-compliant RFC 2822 时间戳](https://datatracker.ietf.org/doc/html/rfc2822#page-14) 和 [特定版本的 ISO8601](https://www.ecma-international.org/ecma-262/11.0/#sec-date.parse)。
|
||||
{% endnote %}
|
||||
|
||||
可以用特殊值 `"now"`(或`"today"`)来获取当前时间:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
This page was last updated at {{ "now" | date: "%Y-%m-%d %H:%M" }}.
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
This page was last updated at 2020-03-25 15:57.
|
||||
```
|
||||
|
||||
{% note info 当前时间 %}注意得到的当前时间是模板渲染时的时间,如果你在用静态站点生成器或者模板有被缓存这一时间可能与用户看到的时间不同。{% endnote %}
|
||||
|
||||
[newDate]: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: date_to_long_string
|
||||
---
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把日期转换为长格式(只支持 US/UK 两种),与 Jekyll 的 `date_to_long_string` 过滤器一样。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_long_string }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
07 November 2008
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_long_string: "ordinal" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
7th November 2008
|
||||
```
|
||||
|
||||
|
||||
注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。
|
||||
|
||||
[date]: ./date.html
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: date_to_rfc822
|
||||
---
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把日期转换为 RFC-822 格式用于 RSS feed,与 Jekyll 的 `date_to_rfc822` 过滤器一样。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_rfc822 }}
|
||||
```
|
||||
|
||||
输入
|
||||
```text
|
||||
Mon, 07 Nov 2008 13:07:54 -0800
|
||||
```
|
||||
|
||||
注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。
|
||||
|
||||
[date]: ./date.html
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
title: date_to_string
|
||||
---
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把日期转换为短格式(只支持 US/UK 两种),与 Jekyll 的 `date_to_string` 过滤器一样。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_string }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
07 Nov 2008
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_string: "ordinal", "US" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Nov 7th, 2008
|
||||
```
|
||||
|
||||
注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。
|
||||
|
||||
[date]: ./date.html
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: date_to_xmlschema
|
||||
---
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把日期转换为 XML Schema (ISO 8601) 格式,与 Jekyll 的 `date_to_xmlschema` 过滤器一样。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ site.time | date_to_xmlschema }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2008-11-07T13:07:54-08:00
|
||||
```
|
||||
|
||||
注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。
|
||||
|
||||
[date]: ./date.html
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
title: default
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
在值不存在时给一个默认值,如果左侧是 [falsy][falsy] 或空(`string` 或 `Array`)就会使用这个默认值。下面的例子中 `product_price` 没有定义,因此使用了默认值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ product_price | default: 2.99 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2.99
|
||||
```
|
||||
|
||||
下面的例子中定义了 `product_price` 所以没有使用默认值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign product_price = 4.99 %}
|
||||
{{ product_price | default: 2.99 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4.99
|
||||
```
|
||||
|
||||
下面例子中 `product_price` 为空,所以使用了默认值。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign product_price = "" %}
|
||||
{{ product_price | default: 2.99 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2.99
|
||||
```
|
||||
|
||||
## 允许 `false`
|
||||
|
||||
{% since %}v9.32.0{% endsince %}
|
||||
|
||||
为了允许让 `false` 直接输出而不是用默认值,可以用 `allow_false` 参数。
|
||||
|
||||
输入
|
||||
|
||||
```liquid
|
||||
{% assign display_price = false %}
|
||||
{{ display_price | default: true, allow_false: true }}
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```text
|
||||
false
|
||||
```
|
||||
|
||||
[falsy]: ../tutorials/truthy-and-falsy.html
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: divided_by
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
两数相除返回商,返回结果数字在 JavaScript 中 `.toString()` 得到的字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 16 | divided_by: 4 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
4
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 5 | divided_by: 3 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1.6666666666666667
|
||||
```
|
||||
|
||||
在 JavaScript 里数字没有浮点和整数的区分,它们的类型都是 `number`:
|
||||
|
||||
```javascript
|
||||
// always true
|
||||
5.0 === 5
|
||||
```
|
||||
|
||||
因此如果需要做整数运算,需要传入额外的 `integerArithmetic` 参数:
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ 5 | divided_by: 3, true }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
[floor]: ./floor.html
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: downcase
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
字符串中每个字符都转为小写,对已经是小写的字符没有影响。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Parker Moore" | downcase }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
parker moore
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "apple" | downcase }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
apple
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: escape
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串中的 HTML 特殊字符转义,对不需要转义的字符串不会产生影响。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Have you read 'James & the Giant Peach'?" | escape }}
|
||||
```
|
||||
|
||||
输出
|
||||
<pre class="highlight">
|
||||
{{"Have you read 'James & the Giant Peach'?" | escape}}
|
||||
</pre>
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Tetsuro Takara" | escape }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Tetsuro Takara
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: escape_once
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串中的特殊字符转义得到可用在 URL 里的字符串,对已经转义过的字符串和不需要转义的字符串不会产生影响。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "1 < 2 & 3" | escape_once }}
|
||||
```
|
||||
|
||||
输出
|
||||
<pre class="highlight">
|
||||
{{"1 < 2 & 3" | escape}}
|
||||
</pre>
|
||||
|
||||
输入
|
||||
<pre class="highlight">
|
||||
{{ "{{"1 < 2 & 3" | escape}}" | escape_once }}
|
||||
</pre>
|
||||
|
||||
输出
|
||||
<pre class="highlight">
|
||||
{{"1 < 2 & 3" | escape}}
|
||||
</pre>
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
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"}
|
||||
```
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
title: find_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
找到数组中给定的表达式值为 `true` 的第一个元素,如果没有这样的元素则返回 `nil`。对于下面的 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2014, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
{"graduation_year":2014,"name":"John"}
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: first
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回数组的第一个元素。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Ground control to Major Tom." | split: " " | first }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Ground
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
|
||||
{{ my_array.first }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
zebra
|
||||
```
|
||||
|
||||
需要在标签中使用的时候,可以用点来计算 `first`:
|
||||
|
||||
```liquid
|
||||
{% if my_array.first == "zebra" %}
|
||||
Here comes a zebra!
|
||||
{% endif %}
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: floor
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
数字下取整,LiquidJS 会尝试把输入转换为数字再做下取整操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 1.2 | floor }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 2.0 | floor }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | floor }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
183
|
||||
```
|
||||
|
||||
下面的例子中输入是个数字:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "3.5" | floor }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
3
|
||||
```
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: group_by
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
把数组元素按照给定的属性的值分组。对于 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2003, name: 'Jay' },
|
||||
{ graduation_year: 2003, name: 'John' },
|
||||
{ graduation_year: 2004, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | group_by: "graduation_year" | json: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": 2003,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": 2004,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2004,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: group_by_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
把数组元素按照给定的 Liquid 表达式的值分组。对于 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2009, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": "201",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2013,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2014,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "200",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2009,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
title: inspect
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
类似于 `json`,但可以处理循环引用的情况。例如对于上下文:
|
||||
|
||||
```
|
||||
const foo = {
|
||||
bar: 'BAR'
|
||||
}
|
||||
foo.foo = foo
|
||||
const scope = { foo }
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% foo | inspect %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
{"bar":"BAR","foo":"[Circular]"}
|
||||
```
|
||||
|
||||
## 格式化
|
||||
|
||||
可以指定一个 `space` 参数来缩进长度。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ foo | inspect: 4 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
{
|
||||
"bar": "BAR",
|
||||
"foo": "[Circular]"
|
||||
}
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: join
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把数组中的元素连接成为一个字符串,以传入的参数作为分隔符。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
|
||||
{{ beatles | join: " and " }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
John and Paul and George and Ringo
|
||||
```
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
title: json
|
||||
---
|
||||
|
||||
{% since %}v9.10.0{% endsince %}
|
||||
|
||||
通过 `JSON.stringify()` 把值转换为字符串,多用于调试用途。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign arr = "foo bar coo" | split: " " %}
|
||||
{{ arr | json }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
["foo","bar","coo"]
|
||||
```
|
||||
|
||||
## 格式化
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
可以指定一个 `space` 参数来格式化 JSON。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign arr = "foo bar coo" | split: " " %}
|
||||
{{ arr | json: 4 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
[
|
||||
"foo",
|
||||
"bar",
|
||||
"coo"
|
||||
]
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: jsonify
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
见 [json][json]。
|
||||
|
||||
[json]: ./json.html
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: last
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回数组的最后一个元素。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Ground control to Major Tom." | split: " " | last }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Tom.
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
|
||||
{{ my_array.last }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
tiger
|
||||
```
|
||||
|
||||
需要在标签中使用的时候,可以用点来计算 `last`:
|
||||
|
||||
```liquid
|
||||
{% if my_array.last == "tiger" %}
|
||||
There goes a tiger!
|
||||
{% endif %}
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: lstrip
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
移除字符串左侧的空白字符(制表符、空格、换行),不影响词之间的空格。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
BEGIN{{ " So much room for activities! " | lstrip }}END
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
BEGINSo much room for activities! END
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: map
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
按照属性名提取对象的属性形成另一个数组并返回。
|
||||
|
||||
下面的例子中假设 `site.pages` 包含了站点的所有网页元信息。使用 `assign` 加 `map` 过滤器创建了一个 `site.pages` 中所有对象的 `category` 属性的值构成的数组。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign all_categories = site.pages | map: "category" %}
|
||||
|
||||
{% for item in all_categories %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- business
|
||||
- celebrities
|
||||
- lifestyle
|
||||
- sports
|
||||
- technology
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: minus
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
两数相减。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | minus: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 16 | minus: 4 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
12
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | minus: 12 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
171.357
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: modulo
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回两数相除的余数。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 3 | modulo: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 24 | modulo: 7 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
3
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | modulo: 12 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
3.3569999999999993
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
title: newline_to_br
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串里的所有换行符(`\n`)替换为 HTML 换行(`<br />`)。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% capture string_with_newlines %}
|
||||
Hello
|
||||
there
|
||||
{% endcapture %}
|
||||
|
||||
{{ string_with_newlines | newline_to_br }}
|
||||
```
|
||||
|
||||
输出
|
||||
```html
|
||||
|
||||
<br/>Hello<br/>there<br/>
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: normalize_whitespace
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
把连续的空白字符替换为单个空格。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "a \n b" | normalize_whitespace }}
|
||||
```
|
||||
|
||||
输出
|
||||
```html
|
||||
a b
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: number_of_words
|
||||
---
|
||||
|
||||
{% since %}v10.13.0{% endsince %}
|
||||
|
||||
计算文本中的单词数。此过滤器接受一个可选参数,用于控制输入字符串中汉字-日语-韩语(CJK)字符的处理方式:
|
||||
- `'cjk'`:将每个检测到的 CJK 字符计为一个单词,无论是否由空格分隔。
|
||||
- `'auto'`:与 `'cjk'` 类似,但如果过滤器用于可能包含或不包含 CJK 字符的字符串,则性能更好。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Hello world!" | number_of_words }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "你好hello世界world" | number_of_words }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "你好hello世界world" | number_of_words: "cjk" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
6
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "你好hello世界world" | number_of_words: "auto" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
6
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: 过滤器
|
||||
description: 每个 Liquid 过滤器的描述和示例
|
||||
---
|
||||
|
||||
LiquidJS 支持 Liquid 语法中具体业务无关的过滤器,基本上 [shopify/liquid 核心][shopify/liquid] 支持的 LiquidJS 都支持。这部分包含了所有 LiquidJS 支持的过滤器的文档和使用示例。
|
||||
|
||||
LiquidJS 共支持 40+ 个过滤器,可以分为如下几类:
|
||||
|
||||
类别 | 过滤器
|
||||
--- | ---
|
||||
数学 | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
|
||||
字符串 | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last, remove, remove_first, remove_last, truncate, truncatewords, normalize_whitespace, number_of_words, array_to_sentence_string
|
||||
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br, xml_escape, cgi_escape, uri_escape, slugify
|
||||
数组 | 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_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string
|
||||
其他 | default, json, jsonify, inspect, raw, to_integer
|
||||
|
||||
[shopify/liquid]: https://github.com/Shopify/liquid
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: plus
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
两数相加。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 4 | plus: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
6
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 16 | plus: 4 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
20
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | plus: 12 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
195.357
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: pop
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
从数组末尾弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | pop %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: prepend
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
在字符串开头添加另一个字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Some fruit: apples, oranges, and bananas
|
||||
```
|
||||
|
||||
`prepend` 也可以用于变量。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign url = "example.com" %}
|
||||
{{ "/index.html" | prepend: url }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
example.com/index.html
|
||||
```
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
title: push
|
||||
---
|
||||
|
||||
{% since %}v10.8.0{% endsince %}
|
||||
|
||||
在数组中添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | push: "peaches" %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
title: raw
|
||||
---
|
||||
|
||||
{% since %}v9.37.0{% endsince %}
|
||||
|
||||
直接返回变量的值。配合 [outputEscape](/api/interfaces/LiquidOptions.html#outputEscape) 参数使用。
|
||||
|
||||
{% note info 自动转义 %}
|
||||
默认情况下 `outputEscape` 为 `undefined`,这意味着 LiquidJS 输出不会默认转义,因此这时使用 `raw` 没有意义。
|
||||
{% endnote %}
|
||||
|
||||
输入(未设置 `outputEscape`)
|
||||
```liquid
|
||||
{{ "<" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
<
|
||||
```
|
||||
|
||||
输入(`outputEscape="escape"`)
|
||||
```liquid
|
||||
{{ "<" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
<
|
||||
```
|
||||
|
||||
输入(`outputEscape="json"`)
|
||||
```liquid
|
||||
{{ "<" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
"<"
|
||||
```
|
||||
|
||||
输入(`outputEscape="escape"`)
|
||||
```liquid
|
||||
{{ "<" | raw }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
<
|
||||
```
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: remove
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
移除字符串中出现的所有指定子字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "I strained to see the train through the rain" | remove: "rain" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
I strained to see the t through the
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: remove_first
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
移除字符串中出现的第一个指定子字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "I strained to see the train through the rain" | remove_first: "rain" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
I strained to see the t through the rain
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: remove_last
|
||||
---
|
||||
|
||||
{% since %}v10.2.0{% endsince %}
|
||||
|
||||
移除字符串中出现的最后一个指定子字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "I strained to see the train through the rain" | remove_last: "rain" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
I strained to see the train through the
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: replace
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串中出现的每一个指定子字符串替换为另一个字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Take your protein pills and put your helmet on
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: replace_first
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
把字符串中出现的第一个指定子字符串替换为另一个字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Take your protein pills and put my helmet on
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: replace_last
|
||||
---
|
||||
|
||||
{% since %}v10.2.0{% endsince %}
|
||||
|
||||
把字符串中出现的最后一个指定子字符串替换为另一个字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Take my protein pills and put my helmet on" | replace_last: "my", "your" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
Take my protein pills and put your helmet on
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
title: reverse
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
反转数组的所有元素,不可用于字符串。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
|
||||
|
||||
{{ my_array | reverse | join: ", " }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
|
||||
plums, peaches, oranges, apples
|
||||
```
|
||||
|
||||
尽管 `reverse` 不能直接用于字符串,可以把字符串分割成数组,反转后再连接成字符串:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
.moT rojaM ot lortnoc dnuorG
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: round
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
数字四舍五入取整,如果传入小数位数作为参数。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 1.2 | round }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 2.7 | round }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
3
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ 183.357 | round: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
183.36
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: rstrip
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
移除字符串右侧的空白字符(制表符、空格、换行),不影响词之间的空格。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
BEGIN{{ " So much room for activities! " | rstrip }}END
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
BEGIN So much room for activities!END
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: shift
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
从数组头部弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | shift %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
title: size
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回字符串的字符个数或者数组的元素个数。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Ground control to Major Tom." | size }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
28
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
|
||||
|
||||
{{ my_array.size }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
|
||||
4
|
||||
```
|
||||
|
||||
在标签里可以用点来计算 `size`:
|
||||
|
||||
```liquid
|
||||
{% if site.pages.size > 10 %}
|
||||
This is a big website!
|
||||
{% endif %}
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: slice
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
返回第一个参数为下标位置的一个字符,如果指定了第二个参数会被解释为子字符串的长度。字符串下标从零开始。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Liquid" | slice: 0 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
L
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Liquid" | slice: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
q
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Liquid" | slice: 2, 5 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
quid
|
||||
```
|
||||
|
||||
If the first argument is a negative number, the indices are counted from the end of the string:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "Liquid" | slice: -3, 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
ui
|
||||
```
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
title: slugify
|
||||
---
|
||||
|
||||
将字符串转换为小写的 URL “slug”。`slugify` 过滤器接受两个选项:
|
||||
|
||||
1. `mode: string`。默认为`"default"`,它可选的值如下:
|
||||
- `"none"`:没有字符
|
||||
- `"raw"`:空格
|
||||
- `"default"`:空格和非字母数字字符
|
||||
- `"pretty"`:空格和非字母数字字符,但排除 `._~!$&'()+,;=@`
|
||||
- `"ascii"`:空格、非字母数字和非 ASCII 字符
|
||||
- `"latin"`:与默认相同,但拉丁字符首先进行音译(例如,àèïòü 转换为 aeiou)。
|
||||
2. `case: boolean`。默认为 `false`。如果为 `true`,则保留 `slug` 原本的大小写。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "The _config.yml file" | slugify }}
|
||||
```
|
||||
输出
|
||||
```
|
||||
the-config-yml-file
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "The _config.yml file" | slugify: "pretty" }}
|
||||
```
|
||||
输出
|
||||
```
|
||||
the-_config.yml-file
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "The _cönfig.yml file" | slugify: "ascii" }}
|
||||
```
|
||||
输出
|
||||
```
|
||||
the-c-nfig-yml-file
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "The cönfig.yml file" | slugify: "latin" }}
|
||||
```
|
||||
输出
|
||||
```
|
||||
the-config-yml-file
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "The cönfig.yml file" | slugify: "latin", true }}
|
||||
```
|
||||
输出
|
||||
```
|
||||
The-config-yml-file
|
||||
```
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
title: sort
|
||||
---
|
||||
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
对数组中的元素排序,排序方式为 JavaScript `Array.prototype.sort()`。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
|
||||
|
||||
{{ my_array | sort | join: ", " }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
|
||||
Sally Snake, giraffe, octopus, zebra
|
||||
```
|
||||
|
||||
有一个参数来指定用元素的哪个属性排序。
|
||||
|
||||
```liquid
|
||||
{% assign products_by_price = collection.products | sort: "price" %}
|
||||
{% for product in products_by_price %}
|
||||
<h4>{{ product.title }}</h4>
|
||||
{% endfor %}
|
||||
```
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
title: sort_natural
|
||||
---
|
||||
|
||||
{% since %}v8.4.0{% endsince %}
|
||||
|
||||
大小写不敏感地对数组元素排序。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
|
||||
|
||||
{{ my_array | sort_natural | join: ", " }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
|
||||
|
||||
giraffe, octopus, Sally Snake, zebra
|
||||
```
|
||||
|
||||
有一个参数来指定用元素的哪个属性排序。
|
||||
|
||||
```liquid
|
||||
{% assign products_by_company = collection.products | sort_natural: "company" %}
|
||||
{% for product in products_by_company %}
|
||||
<h4>{{ product.title }}</h4>
|
||||
{% endfor %}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user