mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-14 20:00:39 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9347586cb5 | ||
|
|
629d958b86 | ||
|
|
fe978c8cd9 | ||
|
|
8ff9aea8ce | ||
|
|
dd7616acb9 | ||
|
|
d75ac93390 | ||
|
|
2caeeda1c1 | ||
|
|
6ebf2ee427 | ||
|
|
3bd825afb0 | ||
|
|
67a5b229ca | ||
|
|
c1c72b53d5 | ||
|
|
30e364c7d0 | ||
|
|
8af682d2ca | ||
|
|
88aa63fd58 | ||
|
|
fce9fcfa68 | ||
|
|
45adbd7008 | ||
|
|
942944eae0 | ||
|
|
660d9be55f | ||
|
|
dc6a301387 | ||
|
|
803a0907fc | ||
|
|
4427ae1fb9 | ||
|
|
7009a054f3 | ||
|
|
b8e7e2d946 | ||
|
|
4c30c660ae | ||
|
|
d916981dad | ||
|
|
fc2731376f | ||
|
|
3bd8d2e557 | ||
|
|
5d91e3d2ef | ||
|
|
5facd99a27 | ||
|
|
3f4f328572 | ||
|
|
a7c94eeeb5 | ||
|
|
2222a1be87 | ||
|
|
bf425c3adb | ||
|
|
c0bc2de76e | ||
|
|
4759371102 | ||
|
|
e03e053362 | ||
|
|
d8691421b4 | ||
|
|
0b4e2a9979 | ||
|
|
e401a30972 |
+74
-1
@@ -577,8 +577,81 @@
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jgarber623",
|
||||
"name": "Jason Garber",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73866?v=4",
|
||||
"profile": "https://sixtwothree.org",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Checkout-Blocks",
|
||||
"name": "Checkout Blocks",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/114603307?v=4",
|
||||
"profile": "https://www.checkoutblocks.com/",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "akdarrah",
|
||||
"name": "Adam Darrah",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/69064?v=4",
|
||||
"profile": "https://www.dropkiq.com/",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "11ty",
|
||||
"name": "Eleventy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/35147177?v=4",
|
||||
"profile": "https://www.11ty.dev/",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NReilingh",
|
||||
"name": "Nick Reilingh",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2458645?v=4",
|
||||
"profile": "http://nickreilingh.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ebobby",
|
||||
"name": "Francisco Soto",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/170356?v=4",
|
||||
"profile": "http://ebobby.org",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davidlj95",
|
||||
"name": "David LJ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8050648?v=4",
|
||||
"profile": "https://www.davidlj95.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RasmusWL",
|
||||
"name": "Rasmus Wriedt Larsen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1054041?v=4",
|
||||
"profile": "https://github.com/RasmusWL",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
"skipCi": true,
|
||||
"commitType": "docs"
|
||||
}
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
name: Release
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
test:
|
||||
needs: build
|
||||
uses: ./.github/workflows/test.yml
|
||||
release:
|
||||
name: Release
|
||||
needs: [build, test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -26,11 +15,6 @@ jobs:
|
||||
node-version: '18'
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist-ubuntu-latest
|
||||
path: dist
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -13,7 +13,6 @@ node_modules/
|
||||
# tmp
|
||||
docs/public/js/liquid.browser.min.js
|
||||
dist/
|
||||
demo/*/package-lock.json
|
||||
demo/*/yarn.json
|
||||
|
||||
# editors
|
||||
|
||||
@@ -1,3 +1,67 @@
|
||||
# [10.10.0](https://github.com/harttle/liquidjs/compare/v10.9.4...v10.10.0) (2023-12-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Array sum filter ([#661](https://github.com/harttle/liquidjs/issues/661)) ([629d958](https://github.com/harttle/liquidjs/commit/629d958b86a97ddf2921d2285b7c9ea83430004e))
|
||||
|
||||
## [10.9.4](https://github.com/harttle/liquidjs/compare/v10.9.3...v10.9.4) (2023-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow unicode to be identifiers, fixes [#655](https://github.com/harttle/liquidjs/issues/655) ([dd7616a](https://github.com/harttle/liquidjs/commit/dd7616acb9a71b77f39d2fa24b6f68a7caef87f1))
|
||||
|
||||
## [10.9.3](https://github.com/harttle/liquidjs/compare/v10.9.2...v10.9.3) (2023-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* package version in released files ([67a5b22](https://github.com/harttle/liquidjs/commit/67a5b229ca9ccabb4aee42bbb5c9f03d4076786a))
|
||||
|
||||
## [10.9.2](https://github.com/harttle/liquidjs/compare/v10.9.1...v10.9.2) (2023-08-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle windows newlines on `newline_to_br` and `strip_newlines` ([88aa63f](https://github.com/harttle/liquidjs/commit/88aa63fd58b5a5824c031acc6f3e4072bedd262f))
|
||||
* sort and where bug when using `strictVariables` ([8af682d](https://github.com/harttle/liquidjs/commit/8af682d2ca68de99bafd4a7055e4912eeb318f57))
|
||||
|
||||
## [10.9.1](https://github.com/harttle/liquidjs/compare/v10.9.0...v10.9.1) (2023-08-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* map filter allow nil results in strict mode, fixes [#647](https://github.com/harttle/liquidjs/issues/647) ([45adbd7](https://github.com/harttle/liquidjs/commit/45adbd7008296a94da04d21a35917f744a0f4109))
|
||||
|
||||
# [10.9.0](https://github.com/harttle/liquidjs/compare/v10.8.4...v10.9.0) (2023-08-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* case should allow multiple values separated by or ([b8e7e2d](https://github.com/harttle/liquidjs/commit/b8e7e2d9467b17ca786e6fb422e9579dd178de76))
|
||||
* for throws undefined var with a null value with strictVariables ([dc6a301](https://github.com/harttle/liquidjs/commit/dc6a3013874872ac85f1fbe5184c74631122d851))
|
||||
* remove_last was eating an extra character ([fc27313](https://github.com/harttle/liquidjs/commit/fc2731376f8ef59ac7160f97cef1fb5d94f053db))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* more flexible squared property read expression, fixes [#643](https://github.com/harttle/liquidjs/issues/643) ([#646](https://github.com/harttle/liquidjs/issues/646)) ([660d9be](https://github.com/harttle/liquidjs/commit/660d9be55f8eac16ca5ac77fd0b38b0d7f94961e))
|
||||
|
||||
## [10.8.4](https://github.com/harttle/liquidjs/compare/v10.8.3...v10.8.4) (2023-07-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow quotes in inline comment tag, fixes [#628](https://github.com/harttle/liquidjs/issues/628) ([bf425c3](https://github.com/harttle/liquidjs/commit/bf425c3adb929e68fa234bee8397560a436595bb))
|
||||
|
||||
## [10.8.3](https://github.com/harttle/liquidjs/compare/v10.8.2...v10.8.3) (2023-06-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* strftime getSuffix works for all dates ([0b4e2a9](https://github.com/harttle/liquidjs/commit/0b4e2a99790347bea0ab5f7d651f2330e3054601))
|
||||
|
||||
## [10.8.2](https://github.com/harttle/liquidjs/compare/v10.8.1...v10.8.2) (2023-06-04)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ A simple, expressive and safe [Shopify][shopify/liquid] / Github Pages compatibl
|
||||
|
||||
* [Documentation][doc]
|
||||
* Please star [LiquidJS on GitHub][github]!
|
||||
* Support [LiquidJS on Open Collective][oc] or [Patreon][patreon]
|
||||
* Financial support via [LiquidJS on Open Collective][oc], or [Github Sponsors](https://github.com/sponsors/harttle)
|
||||
|
||||
<p align="center"><a href="https://liquidjs.com"><img height="155px" width="155px" src="https://liquidjs.com/icon/mstile-310x310.png" alt="logo"></a></p>
|
||||
|
||||
@@ -50,19 +50,27 @@ npx liquidjs --template 'Hello, {{ name }}!' --context '{"name": "Snake"}'
|
||||
|
||||
For more details, refer to the [Setup Guide][setup].
|
||||
|
||||
## Related Projects
|
||||
## Who's Using LiquidJS?
|
||||
|
||||
* [@11ty/eleventy](https://www.npmjs.com/package/@11ty/eleventy): A simpler static site generator. An alternative to Jekyll. Written in JavaScript. Transforms a directory of templates (of varying types) into HTML.
|
||||
* [gulp-liquidjs](https://www.npmjs.com/package/@tuanpham-dev/gulp-liquidjs): A shopify compatible Liquid template engine for Gulp using liquidjs.
|
||||
* [grunt-liquify](https://www.npmjs.com/package/grunt-liquify): A Grunt task to process Liquid using liquidjs. Use it to add Liquid magic to your scripts and css assets.
|
||||
* [react-liquid](https://github.com/aquibm/react-liquid#readme): Liquid templating language component for React
|
||||
- [Eleventy](https://www.11ty.dev/): Eleventy, a simpler static site generator.
|
||||
- [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.
|
||||
- [Builder.io](https://www.builder.io/m/developers): the first and only headless CMS with a visual editor that lets you drag and drop with your components, directly within your current site or app. Completely API-driven, for cleaner code and simpler workflows.
|
||||
- [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.
|
||||
|
||||
Feel free to create a PR or contact me to add your use case into this list!
|
||||
|
||||
## Financial Support
|
||||
|
||||
If you love LiquidJS or your company is using LiquidJS? Please consider [support us on Open Collective or Patreon][financial-support].
|
||||
If you love LiquidJS or your company is using LiquidJS, please consider [financial support][financial-support].
|
||||
|
||||
### Backers
|
||||
If you personally like LiquidJS and find it useful to you, you can buy me a coffee!
|
||||
If you personally like LiquidJS and find it useful to you, buy me a coffee!💖
|
||||
|
||||
<img src="https://opencollective.com/liquidjs/backers.svg?avatarHeight=72">
|
||||
|
||||
@@ -159,6 +167,16 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bangank36"><img src="https://avatars.githubusercontent.com/u/10071857?v=4?s=100" width="100px;" alt="BaNgan"/><br /><sub><b>BaNgan</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=bangank36" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mahyar-pasarzangene"><img src="https://avatars.githubusercontent.com/u/16485039?v=4?s=100" width="100px;" alt="Mahyar Pasarzangene"/><br /><sub><b>Mahyar Pasarzangene</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=mahyar-pasarzangene" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://hubelbauer.net/"><img src="https://avatars.githubusercontent.com/u/6831144?v=4?s=100" width="100px;" alt="Tomáš Hübelbauer"/><br /><sub><b>Tomáš Hübelbauer</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=TomasHubelbauer" title="Code">💻</a> <a href="https://github.com/harttle/liquidjs/commits?author=TomasHubelbauer" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://sixtwothree.org"><img src="https://avatars.githubusercontent.com/u/73866?v=4?s=100" width="100px;" alt="Jason Garber"/><br /><sub><b>Jason Garber</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=jgarber623" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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><br /><a href="#financial-Checkout-Blocks" title="Financial">💵</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.dropkiq.com/"><img src="https://avatars.githubusercontent.com/u/69064?v=4?s=100" width="100px;" alt="Adam Darrah"/><br /><sub><b>Adam Darrah</b></sub></a><br /><a href="#financial-akdarrah" title="Financial">💵</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><br /><a href="#financial-11ty" title="Financial">💵</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://nickreilingh.com/"><img src="https://avatars.githubusercontent.com/u/2458645?v=4?s=100" width="100px;" alt="Nick Reilingh"/><br /><sub><b>Nick Reilingh</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=NReilingh" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://ebobby.org"><img src="https://avatars.githubusercontent.com/u/170356?v=4?s=100" width="100px;" alt="Francisco Soto"/><br /><sub><b>Francisco Soto</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=ebobby" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.davidlj95.com"><img src="https://avatars.githubusercontent.com/u/8050648?v=4?s=100" width="100px;" alt="David LJ"/><br /><sub><b>David LJ</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=davidlj95" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RasmusWL"><img src="https://avatars.githubusercontent.com/u/1054041?v=4?s=100" width="100px;" alt="Rasmus Wriedt Larsen"/><br /><sub><b>Rasmus Wriedt Larsen</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=RasmusWL" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -173,7 +191,6 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
[setup]: https://liquidjs.com/tutorials/setup.html
|
||||
[doc]: https://liquidjs.com
|
||||
[github]: https://github.com/harttle/liquidjs
|
||||
[patreon]: https://www.patreon.com/harttle
|
||||
[oc]: https://opencollective.com/liquidjs/
|
||||
[contribution]: https://liquidjs.com/tutorials/contribution-guidelines.html
|
||||
[financial-support]: https://liquidjs.com/tutorials/contribution-guidelines.html#Financial-Support
|
||||
|
||||
Regular → Executable
+12
-4
@@ -4,18 +4,19 @@ const isQuote = c => c === '"' || c === "'"
|
||||
const isOperator = c => '!=<>'.includes(c)
|
||||
const isNumber = c => c >= '0' && c <= '9'
|
||||
const isCharacter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
const isIdentifier = c => '_-?'.includes(c) || isCharacter(c) || isNumber(c)
|
||||
const isBlank = c => c === '\n' || c === '\t' || c === ' ' || c === '\r' || c === '\v' || c === '\f'
|
||||
const isWord = c => '_-?'.includes(c) || isCharacter(c) || isNumber(c)
|
||||
const isBlank = c => '\n\t \r\v\f'.includes(c)
|
||||
const isInlineBlank = c => c === '\t' || c === ' ' || c === '\r'
|
||||
const isSign = c => c === '-' || c === '+'
|
||||
// See https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
|
||||
const unicodeBlanks = '\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000'
|
||||
const unicodePunctuations = '“”'
|
||||
|
||||
const types = []
|
||||
for (let i = 0; i < 128; i++) {
|
||||
const c = String.fromCharCode(i)
|
||||
let n = 0
|
||||
if (isIdentifier(c)) n |= 1
|
||||
if (isWord(c)) n |= 1
|
||||
if (isOperator(c)) n |= 2
|
||||
if (isBlank(c)) n |= 4
|
||||
if (isQuote(c)) n |= 8
|
||||
@@ -31,13 +32,20 @@ console.log(`
|
||||
// This file is generated by bin/character-gen.js
|
||||
// bitmask character types to boost performance
|
||||
export const TYPES = [${types.join(', ')}]
|
||||
export const IDENTIFIER = 1
|
||||
export const WORD = 1
|
||||
export const OPERATOR = 2
|
||||
export const BLANK = 4
|
||||
export const QUOTE = 8
|
||||
export const INLINE_BLANK = 16
|
||||
export const NUMBER = 32
|
||||
export const SIGN = 64
|
||||
export const PUNCTUATION = 128
|
||||
|
||||
export function isWord (char: string): boolean {
|
||||
const code = char.charCodeAt(0)
|
||||
return code >= 128 ? !TYPES[code] : !!(TYPES[code] & WORD)
|
||||
}
|
||||
`.trim())
|
||||
|
||||
console.log([...unicodeBlanks].map(char => `TYPES[${char.charCodeAt(0)}]`).join(' = ') + ' = BLANK')
|
||||
console.log([...unicodePunctuations].map(char => `TYPES[${char.charCodeAt(0)}]`).join(' = ') + ' = PUNCTUATION')
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION_LATEST=$(cat package.json | grep '"version":' | awk -F'"' '{print $4}')
|
||||
VERSION_LATEST=$(cat package.json | grep '"version":' | head -1 | awk -F'"' '{print $4}')
|
||||
FILE_LOCAL=dist/liquid.node.cjs.js
|
||||
FILE_LATEST=dist/liquid.node.cjs.$VERSION_LATEST.js
|
||||
URL_LATEST=https://unpkg.com/liquidjs@$VERSION_LATEST/dist/liquid.node.cjs.js
|
||||
|
||||
if [ ! -f $FILE_LATEST ]; then
|
||||
if [ ! -f "$FILE_LATEST" ]; then
|
||||
curl $URL_LATEST > $FILE_LATEST
|
||||
fi
|
||||
|
||||
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "liquidjs-demo-nodejs",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"hello-liquid": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hello-liquid/-/hello-liquid-1.0.0.tgz",
|
||||
"integrity": "sha512-WNmZp5s647BN2WHPq4FHVH85CcqNVet5DXIVw1UtVMaV+MJHFA+ylge0hqHSw0iOI/Ojrx8KEKZurYZix5PekQ=="
|
||||
},
|
||||
"liquidjs": {
|
||||
"version": "9.42.0",
|
||||
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-9.42.0.tgz",
|
||||
"integrity": "sha512-krvhwGFrMCMGhybGkxJIvlWVVnoCSpYCn7NhEN43+uvlg2vOkYWpq8be+L3NMlOfwe4ZrKQ7hCh1EmS4yhLKow=="
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "liquidjs-demo-nodejs",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"liquidjs": {
|
||||
"version": "9.32.1",
|
||||
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-9.32.1.tgz",
|
||||
"integrity": "sha512-U/ymmm4M3sNJszHlF0Gzneky17qk+vWmu3nFkWKdniQhqaZNk6eZQdbcKkFGFPN4I9ijrBRjDsLBLysu292j8w=="
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+16737
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"html-react-parser": "^0.4.7",
|
||||
"liquidjs": "^9.1.0",
|
||||
"liquidjs": "^10.8.3",
|
||||
"react": "^16.5.2",
|
||||
"react-dom": "^16.5.2",
|
||||
"react-promise": "^2.0.3",
|
||||
|
||||
Generated
+82
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "liquidjs-demo-typescript",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.18.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.34.tgz",
|
||||
"integrity": "sha512-hcU9AIQVHmPnmjRK+XUUYlILlr9pQrsqSrwov/JK1pnf3GTQowVBhx54FbvM0AU/VXGH4i3+vgXS5EguR7fysA==",
|
||||
"dev": true
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
|
||||
},
|
||||
"liquidjs": {
|
||||
"version": "10.8.3",
|
||||
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.8.3.tgz",
|
||||
"integrity": "sha512-LqHLYtH3vrkT3LyfOhPU0FJX5KPO4aB6SzGa4HRI29yz8pS0ZxqIe/fWtic8qiust1+qrHI92J67tdt92V4WOA==",
|
||||
"requires": {
|
||||
"commander": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
|
||||
"integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.17",
|
||||
"yn": "3.1.1"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg=="
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"start": "ts-node index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"liquidjs": "*",
|
||||
"liquidjs": "^10.8.3",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^4.2.4"
|
||||
},
|
||||
|
||||
Generated
+979
@@ -0,0 +1,979 @@
|
||||
{
|
||||
"name": "liquidjs-demo-nodejs",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
|
||||
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "8.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz",
|
||||
"integrity": "sha512-ehM7cCt2RSFs42mb+lcmhFT9ouIlV92PuaeRGn8N8c98oMjG4Z5pJHA9b1QiCcuqnbPSHcyfiD3mlhqMaHsQIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"@types/eslint-scope": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
|
||||
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"@types/estree": {
|
||||
"version": "0.0.51",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
|
||||
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
|
||||
"integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
|
||||
"integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-buffer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
|
||||
"integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
|
||||
"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
|
||||
"integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
|
||||
"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ieee754": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
|
||||
"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/leb128": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
|
||||
"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/utf8": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
|
||||
"integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/wasm-edit": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
|
||||
"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-opt": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"@webassemblyjs/wast-printer": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-gen": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
|
||||
"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-opt": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
|
||||
"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wast-printer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
|
||||
"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webpack-cli/configtest": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
|
||||
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
|
||||
"dev": true
|
||||
},
|
||||
"@webpack-cli/info": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
|
||||
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"envinfo": "^7.7.3"
|
||||
}
|
||||
},
|
||||
"@webpack-cli/serve": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
|
||||
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true
|
||||
},
|
||||
"@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.21.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
|
||||
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001400",
|
||||
"electron-to-chromium": "^1.4.251",
|
||||
"node-releases": "^2.0.6",
|
||||
"update-browserslist-db": "^1.0.9"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001423",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001423.tgz",
|
||||
"integrity": "sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
|
||||
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
|
||||
"dev": true
|
||||
},
|
||||
"clone-deep": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
|
||||
"integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-plain-object": "^2.0.4",
|
||||
"kind-of": "^6.0.2",
|
||||
"shallow-clone": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"colorette": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
|
||||
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.284",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
|
||||
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
|
||||
"integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"envinfo": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
|
||||
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
|
||||
"dev": true
|
||||
},
|
||||
"es-module-lexer": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"esrecurse": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"estraverse": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"dev": true
|
||||
},
|
||||
"events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true
|
||||
},
|
||||
"fastest-levenshtein": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
|
||||
"integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
|
||||
"dev": true
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.10",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
|
||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"hello-liquid": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hello-liquid/-/hello-liquid-1.0.0.tgz",
|
||||
"integrity": "sha512-WNmZp5s647BN2WHPq4FHVH85CcqNVet5DXIVw1UtVMaV+MJHFA+ylge0hqHSw0iOI/Ojrx8KEKZurYZix5PekQ=="
|
||||
},
|
||||
"import-local": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
|
||||
"integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pkg-dir": "^4.2.0",
|
||||
"resolve-cwd": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"interpret": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
|
||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
|
||||
"dev": true
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isobject": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-worker": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
"supports-color": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"dev": true
|
||||
},
|
||||
"liquidjs": {
|
||||
"version": "9.42.0",
|
||||
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-9.42.0.tgz",
|
||||
"integrity": "sha512-krvhwGFrMCMGhybGkxJIvlWVVnoCSpYCn7NhEN43+uvlg2vOkYWpq8be+L3NMlOfwe4ZrKQ7hCh1EmS4yhLKow=="
|
||||
},
|
||||
"loader-runner": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
|
||||
"dev": true
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
},
|
||||
"neo-async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
|
||||
"dev": true
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"rechoir": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
||||
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"resolve": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"resolve-cwd": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
|
||||
"integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"resolve-from": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
|
||||
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
|
||||
"dev": true
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
|
||||
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||
"integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
|
||||
"integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "^0.3.14",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"terser": "^5.14.1"
|
||||
}
|
||||
},
|
||||
"update-browserslist-db": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.74.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
|
||||
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.10.0",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.1.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
}
|
||||
},
|
||||
"webpack-cli": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
|
||||
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@discoveryjs/json-ext": "^0.5.0",
|
||||
"@webpack-cli/configtest": "^1.2.0",
|
||||
"@webpack-cli/info": "^1.5.0",
|
||||
"@webpack-cli/serve": "^1.7.0",
|
||||
"colorette": "^2.0.14",
|
||||
"commander": "^7.0.0",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"fastest-levenshtein": "^1.0.12",
|
||||
"import-local": "^3.0.2",
|
||||
"interpret": "^2.2.0",
|
||||
"rechoir": "^0.7.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-merge": {
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
|
||||
"integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone-deep": "^4.0.1",
|
||||
"wildcard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"wildcard": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,5 +34,4 @@ algolia:
|
||||
indexName: liquidjs
|
||||
twitter: harttleharttle
|
||||
github: harttle/liquidjs
|
||||
patreon: harttle
|
||||
oc: liquidjs
|
||||
|
||||
@@ -12,6 +12,7 @@ tutorials:
|
||||
access_scope_in_filters: access-scope-in-filters.html
|
||||
parse_parameters: parse-parameters.html
|
||||
render_tag_content: render-tag-content.html
|
||||
drops: drops.html
|
||||
sync_and_async: sync-and-async.html
|
||||
whitespace: whitespace-control.html
|
||||
plugins: plugins.html
|
||||
@@ -69,6 +70,7 @@ filters:
|
||||
strip: strip.html
|
||||
strip_html: strip_html.html
|
||||
strip_newlines: strip_newlines.html
|
||||
sum: sum.html
|
||||
times: times.html
|
||||
truncate: truncate.html
|
||||
truncatewords: truncatewords.html
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: sum
|
||||
---
|
||||
|
||||
{% since %}v10.10.0{% endsince %}
|
||||
|
||||
Computes the sum of all the numbers in an array.
|
||||
An optional argument specifies which property of the array's items to sum up.
|
||||
|
||||
In this example, assume the object `cart.products` contains an array of all products in the cart of a website.
|
||||
Assume each cart product has a `qty` property that gives the count of that product instance in the cart.
|
||||
Using the `sum` filter we can calculate the total number of products in the cart.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
The cart has {{ order.products | sum: "qty" }} products.
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
The cart has 7 products.
|
||||
```
|
||||
@@ -20,7 +20,7 @@ Getting started and building is described in [CONTRIBUTING.md](https://github.co
|
||||
|
||||
## Financial Support
|
||||
|
||||
LiquidJS is Open Source and Free. To help it live and thrive, please consider contribute on [Open Collective][oc]. To acknowledge your contribution, your name and avatar will be listed here, on LiquidJS [homepage](https://liquidjs.com/) and on [Github README][liquidjs]. Please provide your Github ID by openning an issue or contacting me through Twitter (harttleharttle), I can help add you into [the contributors table](https://github.com/harttle/liquidjs#contributors-).
|
||||
LiquidJS is Open Source and Free. To help it live and thrive, you can contribute on [Open Collective][oc] or [Github Sponsors](https://github.com/sponsors/harttle). Please find me via Twitter (harttleharttle) or email (harttleharttle at gmail), to add you into [the contributors table](https://github.com/harttle/liquidjs#contributors-).
|
||||
|
||||
### Backers
|
||||
If you personally like LiquidJS and find it's useful to you, you can become a backer!
|
||||
|
||||
@@ -4,9 +4,9 @@ title: Differences with Shopify/liquid
|
||||
|
||||
## Compatibility
|
||||
|
||||
Being compatible the Ruby version is one of our priorities. Liquid language is originally [implemented in Ruby][ruby-liquid] and used by Shopify, Jekyll and thus Github Pages, as you can see it's one of the most popular template engines in Ruby. There're lots of people using LiquidJS to serve their templates originally written for Shopify themes and Jekyll sites.
|
||||
Being compatible with the Ruby version is one of our priorities. Liquid language is originally [implemented in Ruby][ruby-liquid] and used by Shopify and Jekyll (and thus Github Pages). As you can see it's one of the most popular template engines in Ruby. There're lots of people using LiquidJS to serve their templates originally written for Shopify themes and Jekyll sites.
|
||||
|
||||
So "being compatible" means serve developers from Shopify and Jekyll well:
|
||||
So "being compatible" means serving developers from Shopify and Jekyll well:
|
||||
|
||||
- **Well-formed Liquid template should work just fine in LiquidJS**. For example, `forloop.index` should be 1-indexed, `nil` should be rendered as empty string rather than `undefined`, etc. Although some features (e.g. [#236][#236]) are not feasible in JavaScript, at least we're trying to implement all the semantics of Liquid language.
|
||||
- **All filters and tags in [shopify/liquid][ruby-liquid] are supposed to be built in LiquidJS**. But not those business-logic specific tags/filters typically defined by Shopify platform. Those features should be maintained as [plugins][plugins]. For filters/tags that are not business-logic specific, like `{% layout %}`, and extremely useful, feel free to file an issue.
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: Liquid Drops
|
||||
---
|
||||
|
||||
LiquidJS also provides a mechanism similar to [Shopify Drops][shopify-drops], allowing template authors to incorporate custom functionality in resolving variable values.
|
||||
|
||||
{% note info Drop for JavaScript %}
|
||||
<em>Drop</em> interface is implemented differently in LiquidJS compared to built-in filters and other template functionalities. Since LiquidJS runs in JavaScript, custom Drops need to be reimplemented in JavaScript anyway. There's no compatibility between JavaScript classes and Ruby classes.
|
||||
{% endnote %}
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class SettingsDrop extends Drop {
|
||||
constructor() {
|
||||
super()
|
||||
this.foo = 'FOO'
|
||||
}
|
||||
bar() {
|
||||
return 'BAR'
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
|
||||
const context = { settings: new SettingsDrop() }
|
||||
// Outputs: "foo: FOO, bar: BAR"
|
||||
engine.parseAndRender(template, context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
[Runkit link](https://runkit.com/embed/2is7di4mc7kk)
|
||||
|
||||
As shown above, besides reading properties from context scopes, you can also call methods. You only need to create a custom class inherited from `Drop`.
|
||||
|
||||
{% note tip Async Methods %}
|
||||
LiquidJS is fully async-friendly. You can safely return a Promise in your Drop methods or define your methods in Drop as `async`.
|
||||
{% endnote %}
|
||||
|
||||
## liquidMethodMissing
|
||||
|
||||
For cases when there isn't a fixed set of properties, you can leverage `liquidMethodMissing` to dynamically resolve the value of a variable name.
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class SettingsDrop extends Drop {
|
||||
liquidMethodMissing(key) {
|
||||
return key.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// Outputs: "COO"
|
||||
engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
|
||||
.then(html => console.log(html))
|
||||
```
|
||||
|
||||
`liquidMethodMissing` supports Promise, meaning you can make async calls within it. A more useful case can be fetching the value dynamically from the database. By using Drops, you can avoid hardcoding each property into the context. For example:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class DBDrop extends Drop {
|
||||
async liquidMethodMissing(key) {
|
||||
const record = await db.getRecordByKey(key)
|
||||
return record.value
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const context = { db: new DBDrop() }
|
||||
engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
## valueOf
|
||||
|
||||
Drops can implement a `valueOf()` method, the return value of which can be used to replace itself in the output. For example:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class ColorDrop extends Drop {
|
||||
valueOf() {
|
||||
return 'red'
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const context = { color: new ColorDrop() }
|
||||
// Outputs: "red"
|
||||
engine.parseAndRender("{{color}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
## toLiquid
|
||||
|
||||
`toLiquid()` is not a method of `Drop`, but it can be used to return a `Drop`. In cases where you have a fixed structure in the `context` that cannot change its values, you can implement `toLiquid()` to let LiquidJS use the returned value instead of itself to render the templates.
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
const context = {
|
||||
person: {
|
||||
firstName: "Jun",
|
||||
lastName: "Yang",
|
||||
name: "Jun Yang",
|
||||
toLiquid: () => ({
|
||||
firstName: this.firstName,
|
||||
lastName: this.lastName,
|
||||
// use a different `name`
|
||||
name: "Yang, Jun"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// Outputs: "Yang, Jun"
|
||||
engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
Of course, you can also return a `PersonDrop` instance in the `toLiquid()` method and implement this functionality within `PersonDrop`:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class PersonDrop extends Drop {
|
||||
constructor(person) {
|
||||
super()
|
||||
this.person = person
|
||||
}
|
||||
name() {
|
||||
return this.person.lastName + ", " + this.person.firstName
|
||||
}
|
||||
}
|
||||
|
||||
const context = {
|
||||
person: {
|
||||
firstName: "Jun",
|
||||
lastName: "Yang",
|
||||
name: "Jun Yang",
|
||||
toLiquid: function () { return new PersonDrop(this) }
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// Outputs: "Yang, Jun"
|
||||
engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
{% note info <code>toLiquid()</code> vs. <code>valueOf()</code> Difference %}
|
||||
<ul>
|
||||
<li><code>valueOf()</code> is typically used to define how the current variable should be rendered, while <code>toLiquid()</code> is often used to convert an object into a Drop or another scope provided to the template.</li>
|
||||
<li><code>valueOf()</code> is a method exclusive to Drops; whereas <code>toLiquid()</code> can be used on any scope object.</li>
|
||||
<li><code>valueOf()</code> is called when the variable itself is about to be rendered, replacing itself; whereas <code>toLiquid()</code> is called when its properties are about to be read.</li>
|
||||
</ul>
|
||||
{% endnote %}
|
||||
|
||||
## Special Drops
|
||||
|
||||
LiquidJS itself implements several built-in drops to facilitate template writing. This part is compatible with Shopify Liquid, as we need templates to be portable.
|
||||
|
||||
### blank
|
||||
|
||||
Useful to check whether a string variable is `false`, `null`, `undefined`, an empty string, or a string containing only blank characters.
|
||||
|
||||
```liquid
|
||||
{% unless author == blank %}
|
||||
{{author}}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### empty
|
||||
|
||||
Useful to check if an array, string, or object is empty.
|
||||
|
||||
```liquid
|
||||
{% if authors == empty %}
|
||||
Author list is empty
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
{% note info <code>empty</code> implementation %}
|
||||
For arrays and strings, LiquidJS checks their `.length` property. For objects, LiquidJS calls `Object.keys()` to check whether they have keys.
|
||||
{% endnote %}
|
||||
|
||||
### nil
|
||||
|
||||
`nil` Drop is used to check whether a variable is not defined or defined as `null` or `undefined`, essentially equivalent to JavaScript `== null` check.
|
||||
|
||||
```liquid
|
||||
{% if notexist == nil %}
|
||||
null variable
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### Other Drops
|
||||
|
||||
There are still several Drops for specific tags, like `forloop`, `tablerowloop`, `block`, which are covered by respective tag documents.
|
||||
|
||||
[shopify-drops]: https://github.com/Shopify/liquid/wiki/Introduction-to-Drops
|
||||
@@ -60,7 +60,7 @@ engine.registerTag('random', {
|
||||
|
||||
Calling this tag in scope `{ bar: "bar", obj: { coo: "coo" } }` yields exactly the same result as the first example. See this JSFiddle: <http://jsfiddle.net/ctj364up/3/>
|
||||
|
||||
{% note info Async ans Promises %}
|
||||
{% note info Async and Promises %}
|
||||
Async calls in LiquidJS are implemented by generators directly, for we can call generators in synchronous manner so this tag implementation is also valid for `renderSync()`, `parseAndRenderSync()`, `renderFileSync()`. If you need to await a promise in tag implementation, simply replace `await somePromise` with `yield somePromise` and keep `* render()` instead of `async render()` will do the trick. See <a href="/tutorials/sync-and-async.html">Sync and Async</a> for more details.
|
||||
{% endnote %}
|
||||
|
||||
|
||||
@@ -100,4 +100,4 @@ Most of the [options available through the JavaScript API][options] are also ava
|
||||
A ReactJS demo is also added by [@stevenanthonyrevo](https://github.com/stevenanthonyrevo), see [liquidjs/demo/reactjs/](https://github.com/harttle/liquidjs/blob/master/demo/reactjs/).
|
||||
|
||||
[intro]: ./intro-to-liquid.html
|
||||
[options]: ./options.md
|
||||
[options]: ./options.html
|
||||
|
||||
@@ -20,7 +20,7 @@ Star 是支持 LiquidJS 最重要的方式,也是最简单的方式:通过
|
||||
|
||||
## 资金支持
|
||||
|
||||
LiquidJS 是开源和免费的,但支持 [Open Collective][oc] 赞助,作为感谢你的名字和头像(或 Logo)会展示在这里,[LiquidJS 首页](https://liquidjs.com/)和 [Github README][liquidjs]。
|
||||
LiquidJS 是开源和免费的,但支持 [Open Collective][oc] 和 [Github Sponsors](https://github.com/sponsors/harttle) 赞助,请通过 Twitter (harttleharttle) 或邮件 (harttleharttle at gmail) 联系我,把您加到 [贡献者列表](https://github.com/harttle/liquidjs#contributors-) 中。
|
||||
|
||||
### 支持者
|
||||
如果你喜欢 LiquidJS,希望支持我们,你可以成为 Open Collective Backer!
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: Liquid Drop
|
||||
---
|
||||
|
||||
LiquidJS 还提供了一种类似于 [Shopify Drop][shopify-drops] 的机制,用于为模板作者提供在自定义解析变量值的功能。
|
||||
|
||||
{% note info JavaScript 中的 Drop %}
|
||||
<em>Drop</em> 接口在 LiquidJS 中实现方式与内置过滤器和其他模板功能不同。由于 LiquidJS 在 JavaScript 中运行,自定义 Drop 在 JavaScript 中一定需要重新实现。JavaScript 类与 Ruby 类之间没有兼容性可言。
|
||||
{% endnote %}
|
||||
|
||||
## 基本用法
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class SettingsDrop extends Drop {
|
||||
constructor() {
|
||||
super()
|
||||
this.foo = 'FOO'
|
||||
}
|
||||
bar() {
|
||||
return 'BAR'
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const template = `foo: {{settings.foo}}, bar: {{settings.bar}}`
|
||||
const context = { settings: new SettingsDrop() }
|
||||
// 输出: "foo: FOO, bar: BAR"
|
||||
engine.parseAndRender(template, context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
[Runkit 链接](https://runkit.com/embed/2is7di4mc7kk)
|
||||
|
||||
如上所示,除了从上下文作用域中读取属性外,还可以调用方法。您只需创建一个继承自 `Drop` 的自定义类。
|
||||
|
||||
{% note tip 异步方法 %}
|
||||
LiquidJS 完全支持异步,您可以在 Drop 的方法中安全地返回 Promise,或将 Drop 的方法定义为 `async`。
|
||||
{% endnote %}
|
||||
|
||||
## liquidMethodMissing
|
||||
|
||||
如果属性名不能静态地确定的情况下,可以利用 `liquidMethodMissing` 来动态解析变量的值。
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class SettingsDrop extends Drop {
|
||||
liquidMethodMissing(key) {
|
||||
return key.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// 输出: "COO"
|
||||
engine.parseAndRender("{{settings.coo}}", { settings: new SettingsDrop() })
|
||||
.then(html => console.log(html))
|
||||
```
|
||||
|
||||
`liquidMethodMissing` 支持 Promise,这意味着您可以在其中进行异步调用。一个更有用的例子是通过使用 Drop 动态地从数据库获取值。通过使用 Drop,您可以避免将每个属性都硬编码到上下文中。例如:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class DBDrop extends Drop {
|
||||
async liquidMethodMissing(key) {
|
||||
const record = await db.getRecordByKey(key)
|
||||
return record.value
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const context = { db: new DBDrop() }
|
||||
engine.parseAndRender("{{db.coo}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
## valueOf
|
||||
|
||||
Drop 可以实现一个 `valueOf()` 方法,用于在输出中替换自身。例如:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class ColorDrop extends Drop {
|
||||
valueOf() {
|
||||
return 'red'
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
const context = { color: new ColorDrop() }
|
||||
// 输出: "red"
|
||||
engine.parseAndRender("{{color}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
## toLiquid
|
||||
|
||||
`toLiquid()` 不是 `Drop` 的方法,但它可以用于返回一个 `Drop`。在您有一个上下文中固定结构且不能更改其值的情况下,您可以实现 `toLiquid()`,以便让 LiquidJS 使用返回的值而不是自身来渲染模板。
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
const context = {
|
||||
person: {
|
||||
firstName: "Jun",
|
||||
lastName: "Yang",
|
||||
name: "Jun Yang",
|
||||
toLiquid: () => ({
|
||||
firstName: this.firstName,
|
||||
lastName: this.lastName,
|
||||
// 使用不同的 `name`
|
||||
name: "Yang, Jun"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// 输出: "Yang, Jun"
|
||||
engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
当然,您还可以在 `toLiquid()` 方法中返回一个 `PersonDrop` 实例,并在 `PersonDrop` 中实现此功能:
|
||||
|
||||
```javascript
|
||||
import { Liquid, Drop } from 'liquidjs'
|
||||
|
||||
class PersonDrop extends Drop {
|
||||
constructor(person) {
|
||||
super()
|
||||
this.person = person
|
||||
}
|
||||
name() {
|
||||
return this.person.lastName + ", " + this.person.firstName
|
||||
}
|
||||
}
|
||||
|
||||
const context = {
|
||||
person: {
|
||||
firstName: "Jun",
|
||||
lastName: "Yang",
|
||||
name: "Jun Yang",
|
||||
toLiquid: function () { return new PersonDrop(this) }
|
||||
}
|
||||
}
|
||||
|
||||
const engine = new Liquid()
|
||||
// 输出: "Yang, Jun"
|
||||
engine.parseAndRender("{{person.name}}", context).then(html => console.log(html))
|
||||
```
|
||||
|
||||
{% note info <code>toLiquid()</code> 和 <code>valueOf()</code> 的区别 %}
|
||||
<ul>
|
||||
<li><code>valueOf()</code> 通常用来定义当前变量如何渲染,<code>toLiquid()</code> 通常用来把一个对象转换为 Drop 或另一个提供给模板的 scope。</li>
|
||||
<li><code>valueOf()</code> 是 Drop 才有的方法;而 <code>toLiquid()</code> 可以用在任何 scope 对象上。</li>
|
||||
<li><code>valueOf()</code> 是在自己即将被渲染时,用来替代自己;而 <code>toLiquid()</code> 在即将读取它的属性时才会被调用。</li>
|
||||
</ul>
|
||||
{% endnote %}
|
||||
|
||||
## 特殊 Drop
|
||||
|
||||
LiquidJS 本身实现了几个内置 Drop,以促进模板编写。此部分与 Shopify Liquid 兼容,因为我们需要模板具有可移植性。
|
||||
|
||||
### blank
|
||||
|
||||
用于检查字符串变量是否为 `false`、`null`、`undefined`、空字符串或字符串仅包含空白字符。
|
||||
|
||||
```liquid
|
||||
{% unless author == blank %}
|
||||
{{author}}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### empty
|
||||
|
||||
用于检查数组、字符串或对象是否为空。
|
||||
|
||||
```liquid
|
||||
{% if authors == empty %}
|
||||
作者列表为空
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
{% note info <code>empty</code> 的实现 %}
|
||||
对于数组和字符串,LiquidJS 检查它们的 `.length` 属性。对于对象,LiquidJS 调用 `Object.keys()` 来检查它是否有键。
|
||||
{% endnote %}
|
||||
|
||||
### nil
|
||||
|
||||
`nil` Drop 用于检查变量是否未定义或定义为 `null` 或 `undefined`,本质上等同于 JavaScript 的 `== null` 检查。
|
||||
|
||||
```liquid
|
||||
{% if notexist == nil %}
|
||||
空变量
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 其他 Drop
|
||||
|
||||
仍然有一些特定标签的 Drop,例如 `forloop`、`tablerowloop`、`block`,这些在各自的标签文档中有详细介绍。
|
||||
|
||||
[shopify-drops]: https://github.com/Shopify/liquid/wiki/Introduction-to-Drops
|
||||
Vendored
+1
@@ -47,6 +47,7 @@ sidebar:
|
||||
access_scope_in_filters: Access Scope in Filters
|
||||
parse_parameters: Parse Parameters
|
||||
render_tag_content: Render Tag Content
|
||||
drops: Liquid Drops
|
||||
sync_and_async: Sync and Async
|
||||
whitespace: Whitespace Control
|
||||
plugins: Plugins
|
||||
|
||||
Vendored
+1
@@ -44,6 +44,7 @@ sidebar:
|
||||
access_scope_in_filters: 过滤器里访问上下文
|
||||
parse_parameters: 参数解析
|
||||
render_tag_content: 渲染标签内容
|
||||
drops: Liquid Drop
|
||||
sync_and_async: 同步和异步
|
||||
whitespace: 换行和缩进
|
||||
plugins: 插件
|
||||
|
||||
@@ -67,6 +67,14 @@
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/bangank36"><img src="https://avatars.githubusercontent.com/u/10071857?v=4?s=100" width="100px;" alt="BaNgan"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/mahyar-pasarzangene"><img src="https://avatars.githubusercontent.com/u/16485039?v=4?s=100" width="100px;" alt="Mahyar Pasarzangene"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://hubelbauer.net/"><img src="https://avatars.githubusercontent.com/u/6831144?v=4?s=100" width="100px;" alt="Tomáš Hübelbauer"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://sixtwothree.org"><img src="https://avatars.githubusercontent.com/u/73866?v=4?s=100" width="100px;" alt="Jason Garber"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.checkoutblocks.com/"><img src="https://avatars.githubusercontent.com/u/114603307?v=4?s=100" width="100px;" alt="Checkout Blocks"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.dropkiq.com/"><img src="https://avatars.githubusercontent.com/u/69064?v=4?s=100" width="100px;" alt="Adam Darrah"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.11ty.dev/"><img src="https://avatars.githubusercontent.com/u/35147177?v=4?s=100" width="100px;" alt="Eleventy"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://nickreilingh.com/"><img src="https://avatars.githubusercontent.com/u/2458645?v=4?s=100" width="100px;" alt="Nick Reilingh"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://ebobby.org"><img src="https://avatars.githubusercontent.com/u/170356?v=4?s=100" width="100px;" alt="Francisco Soto"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.davidlj95.com"><img src="https://avatars.githubusercontent.com/u/8050648?v=4?s=100" width="100px;" alt="David LJ"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/RasmusWL"><img src="https://avatars.githubusercontent.com/u/1054041?v=4?s=100" width="100px;" alt="Rasmus Wriedt Larsen"/></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
-1
@@ -6,7 +6,6 @@
|
||||
</div>
|
||||
<div id="footer-links">
|
||||
<a href="https://twitter.com/{{ config.twitter }}" class="footer-link" target="_blank"><i class="icon-twitter"></i></a>
|
||||
<a href="https://www.patreon.com/{{ config.patreon }}" class="footer-link" target="_blank"><i class="icon-patreon"></i></a>
|
||||
<a href="https://opencollective.com/{{ config.oc }}" class="footer-link" target="_blank"><i class="icon-opencollective"></i></a>
|
||||
<a href="https://github.com/{{ config.github }}" class="footer-link" target="_blank"><i class="icon-github"></i></a>
|
||||
</div>
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
color: #fff
|
||||
.icon-twitter:hover
|
||||
color: #1da1f2
|
||||
.icon-patreon:hover
|
||||
color: #f96854
|
||||
.icon-opencollective:hover
|
||||
color: #3384ff
|
||||
|
||||
|
||||
@@ -34,9 +34,6 @@ i {
|
||||
.icon-opencollective:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.icon-patreon:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\f002";
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
note-tip = hsl(40, 100%, 50%)
|
||||
note-tip = #0fff00
|
||||
note-info = hsl(200, 100%, 50%)
|
||||
note-warn = hsl(0, 100%, 50%)
|
||||
|
||||
|
||||
-1
@@ -11,7 +11,6 @@
|
||||
<glyph unicode="" glyph-name="chat-bubble-dots" d="M512 182.857l-204.8-204.8v204.8h-204.8c-56.554 0-102.4 45.846-102.4 102.4v0 512c0 56.32 46.080 102.4 102.4 102.4h819.2c56.554 0 102.4-45.846 102.4-102.4v0-512c0-56.554-45.846-102.4-102.4-102.4v0h-409.6zM256 592.457v-102.4h102.4v102.4h-102.4zM460.8 592.457v-102.4h102.4v102.4h-102.4zM665.6 592.457v-102.4h102.4v102.4h-102.4z" />
|
||||
<glyph unicode="" glyph-name="network" d="M512-73.143c-282.77 0-512 229.23-512 512s229.23 512 512 512v0c282.77 0 512-229.23 512-512s-229.23-512-512-512v0zM908.8 336.457c8.251 30.73 12.99 66.012 12.99 102.4s-4.739 71.67-13.634 105.261l0.644-2.861h-195.584c2.267-30.637 3.559-66.368 3.559-102.4s-1.292-71.763-3.832-107.148l0.273 4.748h195.584zM866.816 234.057h-164.864c-9.503-67.086-26.518-127.646-50.456-184.579l1.816 4.867c91.262 34.305 165.167 97.283 212.455 177.781l1.049 1.931zM413.696 336.457h196.608c2.654 30.668 4.168 66.357 4.168 102.4s-1.513 71.734-4.48 107.010l0.312-4.61h-196.608c-2.654-30.668-4.168-66.357-4.168-102.4s1.513-71.734 4.48-107.010l-0.312 4.61zM426.496 234.057c20.992-122.88 57.856-204.8 85.504-204.8s64.512 81.92 85.504 204.8h-171.008zM115.2 336.457h195.584c-2.267 30.637-3.559 66.368-3.559 102.4s1.292 71.763 3.832 107.148l-0.273-4.748h-195.584c-8.251-30.73-12.99-66.012-12.99-102.4s4.739-71.67 13.634-105.261l-0.644 2.861zM157.184 234.057c48.337-82.429 122.242-145.407 210.673-178.775l2.831-0.937c-21.504 49.152-37.888 110.592-48.64 179.712h-164.864zM866.816 643.657c-48.337 82.429-122.242 145.407-210.673 178.775l-2.831 0.937c21.504-49.152 37.888-110.592 48.64-179.712h164.864zM426.496 643.657h171.008c-20.992 122.88-57.856 204.8-85.504 204.8s-64.512-81.92-85.504-204.8zM157.184 643.657h164.864c10.24 69.12 27.136 130.56 48.64 179.712-91.262-34.305-165.167-97.283-212.455-177.781l-1.049-1.931z" />
|
||||
<glyph unicode="" glyph-name="opencollective" d="M932.693 730.27c57.298-81.265 91.58-182.336 91.58-291.413s-34.281-210.149-92.657-293.026l1.077 1.613-132.267 132.267c25.95 45.863 41.242 100.719 41.242 159.147s-15.292 113.284-42.092 160.785l0.85-1.638zM803.413 859.55l-132.267-132.267c-45.751 25.644-100.402 40.747-158.58 40.747-181.68 0-328.96-147.28-328.96-328.96s147.28-328.96 328.96-328.96c58.179 0 112.83 15.103 160.247 41.602l-1.666-0.855 132.267-132.693c-81.392-57.006-182.473-91.093-291.516-91.093-282.77 0-512 229.23-512 512s229.23 512 512 512c109.043 0 210.124-34.088 293.168-92.188l-1.652 1.095zM932.693 730.27c57.298-81.265 91.58-182.336 91.58-291.413s-34.281-210.149-92.657-293.026l1.077 1.613-132.267 132.267c25.95 45.863 41.242 100.719 41.242 159.147s-15.292 113.284-42.092 160.785l0.85-1.638z" />
|
||||
<glyph unicode="" glyph-name="patreon" d="M656.469 928.5c-203.264 0-368.64-165.376-368.64-368.64 0-202.667 165.376-367.488 368.64-367.488 202.667 0 367.531 164.864 367.531 367.488 0 203.264-164.864 368.64-367.531 368.64zM0.128-53.388h180.053v981.888h-180.053z" />
|
||||
<glyph unicode="" glyph-name="shopify" d="M654.379-72.247l307.883 66.603c0 0-111.104 751.488-112 756.48-0.768 4.949-4.864 8.192-9.003 8.192s-82.304 5.803-82.304 5.803-54.4 54.357-61.397 60.203c-1.92 1.579-3.2 2.432-5.163 3.157l-38.997-900.437zM499.627 468.51c0 0-34.56 18.091-75.691 18.091-61.739 0-64.171-38.656-64.171-48.683 0-52.565 138.24-73.173 138.24-197.504 0-97.92-61.44-160.427-145.323-160.427-100.437 0-151.040 62.507-151.040 62.507l27.563 89.003c0 0 53.12-45.483 97.28-45.483 28.8 0 41.6 23.253 41.6 39.765 0 69.077-113.237 72.277-113.237 185.984-1.451 95.445 67.029 188.416 205.952 188.416 53.632 0 80-15.403 80-15.403l-40.32-115.84zM476.587 915.444c5.803 0 11.563-1.621 17.28-5.76-41.984-19.84-88.064-69.931-107.008-170.325-27.989-9.088-55.168-17.28-80.597-24.661 22.144 76.16 75.648 200.32 170.325 200.32zM529.28 789.62v-5.76c-32.171-9.899-67.541-20.651-102.144-31.403 19.883 75.819 56.875 112.853 88.96 126.763 8.235-21.376 13.184-50.176 13.184-89.6zM552.277 884.937c29.611-3.157 48.683-36.992 60.971-74.88-14.891-4.864-31.36-9.856-49.408-15.616v10.752c0 32.085-4.096 58.496-11.563 79.829zM679.936 829.94c-0.853 0-2.56-0.896-3.328-0.896s-12.331-3.2-30.464-8.96c-18.048 52.608-50.176 101.12-107.008 101.12h-4.907c-16.469 20.736-36.352 29.653-53.589 29.653-132.523 0-195.84-165.419-215.68-249.429-50.944-15.573-88.021-27.136-92.16-28.757-28.8-9.088-29.611-9.899-32.939-37.12-3.2-19.712-78.080-600.021-78.080-600.021l578.603-108.672z" />
|
||||
<glyph unicode="" glyph-name="rocket" d="M704 886.857l-320-320h-192l-192-256c0 0 203.416 56.652 322.066 30.084l-322.066-414.084 421.902 328.144c58.838-134.654-37.902-328.144-37.902-328.144l256 192v192l320 320 64 320-320-64z" />
|
||||
<glyph unicode="" glyph-name="search" horiz-adv-x="951" d="M658.286 475.428c0 141.143-114.857 256-256 256s-256-114.857-256-256 114.857-256 256-256 256 114.857 256 256zM950.857 0c0-40-33.143-73.143-73.143-73.143-19.429 0-38.286 8-51.429 21.714l-196 195.429c-66.857-46.286-146.857-70.857-228-70.857-222.286 0-402.286 180-402.286 402.286s180 402.286 402.286 402.286 402.286-180 402.286-402.286c0-81.143-24.571-161.143-70.857-228l196-196c13.143-13.143 21.143-32 21.143-51.429z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.8.2",
|
||||
"version": "10.10.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.8.2",
|
||||
"version": "10.10.0",
|
||||
"description": "A simple, expressive and safe Shopify / Github Pages compatible template engine in pure JavaScript.",
|
||||
"main": "dist/liquid.node.cjs.js",
|
||||
"module": "dist/liquid.node.esm.js",
|
||||
@@ -21,10 +21,12 @@
|
||||
"test:e2e": "jest test/e2e",
|
||||
"perf:diff": "bin/perf-diff.sh",
|
||||
"perf:engines": "cd benchmark && npm run engines",
|
||||
"version": "npm run build && npm test",
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"build:cjs": "BUNDLES=cjs rollup -c rollup.config.mjs",
|
||||
"build:min": "BUNDLES=min rollup -c rollup.config.mjs",
|
||||
"build:umd": "BUNDLES=umd rollup -c rollup.config.mjs",
|
||||
"build:charmap": "./bin/character-gen.js > src/util/character.ts",
|
||||
"build:docs": "bin/build-docs.sh"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Drop } from '../drop/drop'
|
||||
import { __assign } from 'tslib'
|
||||
import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options'
|
||||
import { Scope } from './scope'
|
||||
import { isArray, isNil, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync } from '../util'
|
||||
import { isArray, isNil, isUndefined, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync } from '../util'
|
||||
|
||||
type PropertyKey = string | number;
|
||||
|
||||
@@ -76,11 +76,11 @@ export class Context {
|
||||
public getFromScope (scope: unknown, paths: PropertyKey[] | string): IterableIterator<unknown> {
|
||||
return toValueSync(this._getFromScope(scope, paths))
|
||||
}
|
||||
public * _getFromScope (scope: unknown, paths: PropertyKey[] | string): IterableIterator<unknown> {
|
||||
public * _getFromScope (scope: unknown, paths: PropertyKey[] | string, strictVariables = this.strictVariables): IterableIterator<unknown> {
|
||||
if (isString(paths)) paths = paths.split('.')
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
scope = yield readProperty(scope as object, paths[i], this.ownPropertyOnly)
|
||||
if (isNil(scope) && this.strictVariables) {
|
||||
if (strictVariables && isUndefined(scope)) {
|
||||
throw new InternalUndefinedVariableError((paths as string[]).slice(0, i + 1).join!('.'))
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -14,7 +14,7 @@ export function * sort<T> (this: FilterImpl, arr: T[], property?: string): Itera
|
||||
for (const item of toArray(toValue(arr))) {
|
||||
values.push([
|
||||
item,
|
||||
property ? yield this.context._getFromScope(item, stringify(property).split('.')) : item
|
||||
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
||||
])
|
||||
}
|
||||
return values.sort((lhs, rhs) => {
|
||||
@@ -38,11 +38,20 @@ export const size = (v: string | any[]) => (v && v.length) || 0
|
||||
export function * map (this: FilterImpl, arr: Scope[], property: string): IterableIterator<unknown> {
|
||||
const results = []
|
||||
for (const item of toArray(toValue(arr))) {
|
||||
results.push(yield this.context._getFromScope(item, stringify(property).split('.')))
|
||||
results.push(yield this.context._getFromScope(item, stringify(property), false))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
export function * sum (this: FilterImpl, arr: Scope[], property?: string): IterableIterator<unknown> {
|
||||
let sum = 0
|
||||
for (const item of toArray(toValue(arr))) {
|
||||
const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item)
|
||||
sum += Number.isNaN(data) ? 0 : data
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
export function compact<T> (this: FilterImpl, arr: T[]) {
|
||||
arr = toValue(arr)
|
||||
return toArray(arr).filter(x => !isNil(toValue(x)))
|
||||
@@ -70,7 +79,7 @@ export function * where<T extends object> (this: FilterImpl, arr: T[], property:
|
||||
const values: unknown[] = []
|
||||
arr = toArray(toValue(arr))
|
||||
for (const item of arr) {
|
||||
values.push(yield this.context._getFromScope(item, stringify(property).split('.')))
|
||||
values.push(yield this.context._getFromScope(item, stringify(property).split('.'), false))
|
||||
}
|
||||
return arr.filter((_, i) => {
|
||||
if (expected === undefined) return isTruthy(values[i], this.context)
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export function escape_once (str: string) {
|
||||
}
|
||||
|
||||
export function newline_to_br (v: string) {
|
||||
return stringify(v).replace(/\n/g, '<br />\n')
|
||||
return stringify(v).replace(/\r?\n/gm, '<br />\n')
|
||||
}
|
||||
|
||||
export function strip_html (v: string) {
|
||||
|
||||
@@ -44,7 +44,7 @@ export function remove_last (v: string, l: string) {
|
||||
const pattern = String(l)
|
||||
const index = str.lastIndexOf(pattern)
|
||||
if (index === -1) return str
|
||||
return str.substring(0, index) + str.substring(index + pattern.length + 1)
|
||||
return str.substring(0, index) + str.substring(index + pattern.length)
|
||||
}
|
||||
|
||||
export function rstrip (str: string, chars?: string) {
|
||||
@@ -74,7 +74,7 @@ export function strip (v: string, chars?: string) {
|
||||
}
|
||||
|
||||
export function strip_newlines (v: string) {
|
||||
return stringify(v).replace(/\n/g, '')
|
||||
return stringify(v).replace(/\r?\n/gm, '')
|
||||
}
|
||||
|
||||
export function capitalize (str: string) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './tokenizer'
|
||||
export * from './parser'
|
||||
export * from './parse-stream'
|
||||
export * from './parse-string-literal'
|
||||
export * from './token-kind'
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { matchOperator } from './match-operator'
|
||||
import { defaultOperators } from '..'
|
||||
import { createTrie } from '../util/operator-trie'
|
||||
|
||||
describe('parser/matchOperator()', function () {
|
||||
const trie = createTrie(defaultOperators)
|
||||
it('should match contains', () => {
|
||||
expect(matchOperator('contains', 0, trie)).toBe(8)
|
||||
})
|
||||
it('should match comparision', () => {
|
||||
expect(matchOperator('>', 0, trie)).toBe(1)
|
||||
expect(matchOperator('>=', 0, trie)).toBe(2)
|
||||
expect(matchOperator('<', 0, trie)).toBe(1)
|
||||
expect(matchOperator('<=', 0, trie)).toBe(2)
|
||||
})
|
||||
it('should match binary logic', () => {
|
||||
expect(matchOperator('and', 0, trie)).toBe(3)
|
||||
expect(matchOperator('or', 0, trie)).toBe(2)
|
||||
})
|
||||
it('should not match if word not terminate', () => {
|
||||
expect(matchOperator('true1', 0, trie)).toBe(-1)
|
||||
expect(matchOperator('containsa', 0, trie)).toBe(-1)
|
||||
})
|
||||
it('should match if word boundary found', () => {
|
||||
expect(matchOperator('>=1', 0, trie)).toBe(2)
|
||||
expect(matchOperator('contains b', 0, trie)).toBe(8)
|
||||
})
|
||||
})
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Trie, TrieNode, IDENTIFIER, TYPES } from '../util'
|
||||
|
||||
export function matchOperator (str: string, begin: number, trie: Trie, end = str.length) {
|
||||
let node: TrieNode = trie
|
||||
let i = begin
|
||||
let info
|
||||
while (node[str[i]] && i < end) {
|
||||
node = node[str[i++]]
|
||||
if (node['end']) info = node
|
||||
}
|
||||
if (!info) return -1
|
||||
if (info['needBoundary'] && (TYPES[str.charCodeAt(i)] & IDENTIFIER)) return -1
|
||||
return i
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export class Parser {
|
||||
}
|
||||
return new HTML(token)
|
||||
} catch (e) {
|
||||
if (e instanceof LiquidError) throw e
|
||||
if (LiquidError.is(e)) throw e
|
||||
throw new ParseError(e as Error, token)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { LiquidTagToken, HTMLToken, QuotedToken, OutputToken, TagToken, OperatorToken, RangeToken, PropertyAccessToken, NumberToken, IdentifierToken } from '../tokens'
|
||||
import { Tokenizer } from './tokenizer'
|
||||
import { defaultOperators } from '../render/operator'
|
||||
import { createTrie } from '../util/operator-trie'
|
||||
|
||||
describe('Tokenizer', function () {
|
||||
it('should read quoted', () => {
|
||||
@@ -15,12 +17,31 @@ describe('Tokenizer', function () {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
expect(new Tokenizer('foo bar').readWord()).toHaveProperty('content', 'foo')
|
||||
})
|
||||
it('should read number value', () => {
|
||||
const token: NumberToken = new Tokenizer('2.33.2').readValueOrThrow() as any
|
||||
it('should read integer number', () => {
|
||||
const token: NumberToken = new Tokenizer('123').readValueOrThrow() as any
|
||||
expect(token).toBeInstanceOf(NumberToken)
|
||||
expect(token.whole.getText()).toBe('2')
|
||||
expect(token.decimal!.getText()).toBe('33')
|
||||
expect(token.getText()).toBe('2.33')
|
||||
expect(token.getText()).toBe('123')
|
||||
expect(token.content).toBe(123)
|
||||
})
|
||||
it('should read negative number', () => {
|
||||
const token: NumberToken = new Tokenizer('-123').readValueOrThrow() as any
|
||||
expect(token).toBeInstanceOf(NumberToken)
|
||||
expect(token.getText()).toBe('-123')
|
||||
expect(token.content).toBe(-123)
|
||||
})
|
||||
it('should read float number', () => {
|
||||
const token: NumberToken = new Tokenizer('1.23').readValueOrThrow() as any
|
||||
expect(token).toBeInstanceOf(NumberToken)
|
||||
expect(token.getText()).toBe('1.23')
|
||||
expect(token.content).toBe(1.23)
|
||||
})
|
||||
it('should treat 1.2.3 as property read', () => {
|
||||
const token: PropertyAccessToken = new Tokenizer('1.2.3').readValueOrThrow() as any
|
||||
expect(token).toBeInstanceOf(PropertyAccessToken)
|
||||
expect(token.props).toHaveLength(3)
|
||||
expect(token.props[0].getText()).toBe('1')
|
||||
expect(token.props[1].getText()).toBe('2')
|
||||
expect(token.props[2].getText()).toBe('3')
|
||||
})
|
||||
it('should read quoted value', () => {
|
||||
const value = new Tokenizer('"foo"a').readValue()
|
||||
@@ -33,11 +54,7 @@ describe('Tokenizer', function () {
|
||||
it('should read quoted property access value', () => {
|
||||
const value = new Tokenizer('["a prop"]').readValue()
|
||||
expect(value).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((value as PropertyAccessToken).variable.getText()).toBe('"a prop"')
|
||||
})
|
||||
it('should throw for broken quoted property access', () => {
|
||||
const tokenizer = new Tokenizer('[5]')
|
||||
expect(() => tokenizer.readValueOrThrow()).toThrow()
|
||||
expect((value as QuotedToken).getText()).toBe('["a prop"]')
|
||||
})
|
||||
it('should throw for incomplete quoted property access', () => {
|
||||
const tokenizer = new Tokenizer('["a prop"')
|
||||
@@ -199,15 +216,6 @@ describe('Tokenizer', function () {
|
||||
})
|
||||
})
|
||||
describe('#readTagToken()', () => {
|
||||
it('should skip quoted delimiters', function () {
|
||||
const html = '{% assign a = "%} {% }} {{" %}'
|
||||
const tokenizer = new Tokenizer(html)
|
||||
const token = tokenizer.readTagToken()
|
||||
|
||||
expect(token).toBeInstanceOf(TagToken)
|
||||
expect(token.name).toBe('assign')
|
||||
expect(token.args).toBe('a = "%} {% }} {{"')
|
||||
})
|
||||
})
|
||||
describe('#readOutputToken()', () => {
|
||||
it('should skip quoted delimiters', function () {
|
||||
@@ -286,10 +294,10 @@ describe('Tokenizer', function () {
|
||||
|
||||
const pa: PropertyAccessToken = token!.args[0] as any
|
||||
expect(token!.args[0]).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((pa.variable as any).content).toBe('arr')
|
||||
expect(pa.props).toHaveLength(1)
|
||||
expect(pa.props[0]).toBeInstanceOf(NumberToken)
|
||||
expect(pa.props[0].getText()).toBe('0')
|
||||
expect(pa.props).toHaveLength(2)
|
||||
expect((pa.props[0] as any).content).toBe('arr')
|
||||
expect(pa.props[1]).toBeInstanceOf(NumberToken)
|
||||
expect(pa.props[1].getText()).toBe('0')
|
||||
})
|
||||
it('should read a filter with obj.foo argument', function () {
|
||||
const tokenizer = new Tokenizer('| plus: obj.foo')
|
||||
@@ -299,10 +307,10 @@ describe('Tokenizer', function () {
|
||||
|
||||
const pa: PropertyAccessToken = token!.args[0] as any
|
||||
expect(token!.args[0]).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((pa.variable as any).content).toBe('obj')
|
||||
expect(pa.props).toHaveLength(1)
|
||||
expect(pa.props[0]).toBeInstanceOf(IdentifierToken)
|
||||
expect(pa.props[0].getText()).toBe('foo')
|
||||
expect(pa.props).toHaveLength(2)
|
||||
expect((pa.props[0] as any).content).toBe('obj')
|
||||
expect(pa.props[1]).toBeInstanceOf(IdentifierToken)
|
||||
expect(pa.props[1].getText()).toBe('foo')
|
||||
})
|
||||
it('should read a filter with obj["foo"] argument', function () {
|
||||
const tokenizer = new Tokenizer('| plus: obj["good luck"]')
|
||||
@@ -313,8 +321,8 @@ describe('Tokenizer', function () {
|
||||
const pa: PropertyAccessToken = token!.args[0] as any
|
||||
expect(token!.args[0]).toBeInstanceOf(PropertyAccessToken)
|
||||
expect(pa.getText()).toBe('obj["good luck"]')
|
||||
expect((pa.variable as any).content).toBe('obj')
|
||||
expect(pa.props[0].getText()).toBe('"good luck"')
|
||||
expect((pa.props[0] as any).content).toBe('obj')
|
||||
expect(pa.props[1].getText()).toBe('"good luck"')
|
||||
})
|
||||
})
|
||||
describe('#readFilters()', () => {
|
||||
@@ -350,7 +358,7 @@ describe('Tokenizer', function () {
|
||||
expect(tokens[2].args).toHaveLength(1)
|
||||
expect(tokens[2].args[0]).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((tokens[2].args[0] as any).getText()).toBe('foo[a.b["c d"]]')
|
||||
expect((tokens[2].args[0] as any).props[0].getText()).toBe('a.b["c d"]')
|
||||
expect((tokens[2].args[0] as any).props[1].getText()).toBe('a.b["c d"]')
|
||||
})
|
||||
})
|
||||
describe('#readExpression()', () => {
|
||||
@@ -367,10 +375,10 @@ describe('Tokenizer', function () {
|
||||
expect(exp).toHaveLength(1)
|
||||
const pa = exp[0] as PropertyAccessToken
|
||||
expect(pa).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((pa.variable as any).content).toEqual('a')
|
||||
expect(pa.props).toHaveLength(2)
|
||||
expect(pa.props).toHaveLength(3)
|
||||
expect((pa.props[0] as any).content).toEqual('a')
|
||||
|
||||
const [p1, p2] = pa.props
|
||||
const [, p1, p2] = pa.props
|
||||
expect(p1).toBeInstanceOf(IdentifierToken)
|
||||
expect(p1.getText()).toBe('')
|
||||
expect(p2).toBeInstanceOf(PropertyAccessToken)
|
||||
@@ -382,8 +390,8 @@ describe('Tokenizer', function () {
|
||||
expect(exp).toHaveLength(1)
|
||||
const pa = exp[0] as PropertyAccessToken
|
||||
expect(pa).toBeInstanceOf(PropertyAccessToken)
|
||||
expect((pa.variable as any).content).toEqual('a')
|
||||
expect(pa.props).toHaveLength(0)
|
||||
expect(pa.props).toHaveLength(1)
|
||||
expect((pa.props[0] as any).content).toEqual('a')
|
||||
})
|
||||
it('should read expression `a ==`', () => {
|
||||
const exp = [...new Tokenizer('a ==').readExpressionTokens()]
|
||||
@@ -490,6 +498,30 @@ describe('Tokenizer', function () {
|
||||
expect(rhs.getText()).toEqual('"\\""')
|
||||
})
|
||||
})
|
||||
describe('#matchTrie()', function () {
|
||||
const opTrie = createTrie(defaultOperators)
|
||||
it('should match contains', () => {
|
||||
expect(new Tokenizer('contains').matchTrie(opTrie)).toBe(8)
|
||||
})
|
||||
it('should match comparision', () => {
|
||||
expect(new Tokenizer('>').matchTrie(opTrie)).toBe(1)
|
||||
expect(new Tokenizer('>=').matchTrie(opTrie)).toBe(2)
|
||||
expect(new Tokenizer('<').matchTrie(opTrie)).toBe(1)
|
||||
expect(new Tokenizer('<=').matchTrie(opTrie)).toBe(2)
|
||||
})
|
||||
it('should match binary logic', () => {
|
||||
expect(new Tokenizer('and').matchTrie(opTrie)).toBe(3)
|
||||
expect(new Tokenizer('or').matchTrie(opTrie)).toBe(2)
|
||||
})
|
||||
it('should not match if word not terminate', () => {
|
||||
expect(new Tokenizer('true1').matchTrie(opTrie)).toBe(-1)
|
||||
expect(new Tokenizer('containsa').matchTrie(opTrie)).toBe(-1)
|
||||
})
|
||||
it('should match if word boundary found', () => {
|
||||
expect(new Tokenizer('>=1').matchTrie(opTrie)).toBe(2)
|
||||
expect(new Tokenizer('contains b').matchTrie(opTrie)).toBe(8)
|
||||
})
|
||||
})
|
||||
describe('#readLiquidTagTokens', () => {
|
||||
it('should read newline terminated tokens', () => {
|
||||
const tokenizer = new Tokenizer('echo \'hello\'')
|
||||
|
||||
+76
-36
@@ -1,16 +1,17 @@
|
||||
import { FilteredValueToken, TagToken, HTMLToken, HashToken, QuotedToken, LiquidTagToken, OutputToken, ValueToken, Token, RangeToken, FilterToken, TopLevelToken, PropertyAccessToken, OperatorToken, LiteralToken, IdentifierToken, NumberToken } from '../tokens'
|
||||
import { Trie, createTrie, ellipsis, literalValues, TokenizationError, TYPES, QUOTE, BLANK, IDENTIFIER } from '../util'
|
||||
import { OperatorHandler } from '../render/operator'
|
||||
import { TrieNode, LiteralValue, Trie, createTrie, ellipsis, literalValues, TokenizationError, TYPES, QUOTE, BLANK, NUMBER, SIGN, isWord } from '../util'
|
||||
import { Operators, Expression } from '../render'
|
||||
import { NormalizedFullOptions, defaultOptions } from '../liquid-options'
|
||||
import { FilterArg } from './filter-arg'
|
||||
import { matchOperator } from './match-operator'
|
||||
import { whiteSpaceCtrl } from './whitespace-ctrl'
|
||||
|
||||
export class Tokenizer {
|
||||
p: number
|
||||
N: number
|
||||
private rawBeginAt = -1
|
||||
private opTrie: Trie
|
||||
private opTrie: Trie<OperatorHandler>
|
||||
private literalTrie: Trie<LiteralValue>
|
||||
|
||||
constructor (
|
||||
public input: string,
|
||||
@@ -21,6 +22,7 @@ export class Tokenizer {
|
||||
this.p = range ? range[0] : 0
|
||||
this.N = range ? range[1] : input.length
|
||||
this.opTrie = createTrie(operators)
|
||||
this.literalTrie = createTrie(literalValues)
|
||||
}
|
||||
|
||||
readExpression () {
|
||||
@@ -44,10 +46,22 @@ export class Tokenizer {
|
||||
}
|
||||
readOperator (): OperatorToken | undefined {
|
||||
this.skipBlank()
|
||||
const end = matchOperator(this.input, this.p, this.opTrie)
|
||||
const end = this.matchTrie(this.opTrie)
|
||||
if (end === -1) return
|
||||
return new OperatorToken(this.input, this.p, (this.p = end), this.file)
|
||||
}
|
||||
matchTrie<T> (trie: Trie<T>) {
|
||||
let node: TrieNode<T> = trie
|
||||
let i = this.p
|
||||
let info
|
||||
while (node[this.input[i]] && i < this.N) {
|
||||
node = node[this.input[i++]]
|
||||
if (node['end']) info = node
|
||||
}
|
||||
if (!info) return -1
|
||||
if (info['needBoundary'] && isWord(this.peek(i - this.p))) return -1
|
||||
return i
|
||||
}
|
||||
readFilteredValue (): FilteredValueToken {
|
||||
const begin = this.p
|
||||
const initial = this.readExpression()
|
||||
@@ -140,9 +154,10 @@ export class Tokenizer {
|
||||
return token
|
||||
}
|
||||
|
||||
readToDelimiter (delimiter: string) {
|
||||
readToDelimiter (delimiter: string, respectQuoted = false) {
|
||||
this.skipBlank()
|
||||
while (this.p < this.N) {
|
||||
if ((this.peekType() & QUOTE)) {
|
||||
if (respectQuoted && (this.peekType() & QUOTE)) {
|
||||
this.readQuoted()
|
||||
continue
|
||||
}
|
||||
@@ -156,7 +171,7 @@ export class Tokenizer {
|
||||
const { file, input } = this
|
||||
const { outputDelimiterRight } = options
|
||||
const begin = this.p
|
||||
if (this.readToDelimiter(outputDelimiterRight) === -1) {
|
||||
if (this.readToDelimiter(outputDelimiterRight, true) === -1) {
|
||||
throw this.error(`output ${this.snapshot(begin)} not closed`, begin)
|
||||
}
|
||||
return new OutputToken(input, begin, this.p, options, file)
|
||||
@@ -230,7 +245,7 @@ export class Tokenizer {
|
||||
readIdentifier (): IdentifierToken {
|
||||
this.skipBlank()
|
||||
const begin = this.p
|
||||
while (!this.end() && this.peekType() & IDENTIFIER) ++this.p
|
||||
while (!this.end() && isWord(this.peek())) ++this.p
|
||||
return new IdentifierToken(this.input, begin, this.p, this.file)
|
||||
}
|
||||
|
||||
@@ -271,8 +286,8 @@ export class Tokenizer {
|
||||
return this.input.slice(this.p, this.N)
|
||||
}
|
||||
|
||||
advance (i = 1) {
|
||||
this.p += i
|
||||
advance (step = 1) {
|
||||
this.p += step
|
||||
}
|
||||
|
||||
end () {
|
||||
@@ -288,43 +303,68 @@ export class Tokenizer {
|
||||
}
|
||||
|
||||
readValue (): ValueToken | undefined {
|
||||
const value = this.readQuoted() || this.readRange()
|
||||
if (value) return value
|
||||
|
||||
if (this.peek() === '[') {
|
||||
this.p++
|
||||
const prop = this.readQuoted()
|
||||
if (!prop) return
|
||||
if (this.peek() !== ']') return
|
||||
this.p++
|
||||
return new PropertyAccessToken(prop, [], this.p)
|
||||
}
|
||||
|
||||
const variable = this.readIdentifier()
|
||||
if (!variable.size()) return
|
||||
|
||||
let isNumber = variable.isNumber(true)
|
||||
const props: (QuotedToken | IdentifierToken)[] = []
|
||||
this.skipBlank()
|
||||
const begin = this.p
|
||||
const variable = this.readLiteral() || this.readQuoted() || this.readRange() || this.readNumber()
|
||||
const props: (ValueToken | IdentifierToken)[] = []
|
||||
while (true) {
|
||||
if (this.peek() === '[') {
|
||||
isNumber = false
|
||||
this.p++
|
||||
const prop = this.readValue() || new IdentifierToken(this.input, this.p, this.p, this.file)
|
||||
this.readTo(']')
|
||||
this.assert(this.readTo(']') !== -1, '[ not closed')
|
||||
props.push(prop)
|
||||
} else if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
|
||||
continue
|
||||
}
|
||||
if (!variable && !props.length) {
|
||||
const prop = this.readIdentifier()
|
||||
if (prop.size()) {
|
||||
props.push(prop)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
|
||||
this.p++
|
||||
const prop = this.readIdentifier()
|
||||
if (!prop.size()) break
|
||||
if (!prop.isNumber()) isNumber = false
|
||||
props.push(prop)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if (!props.length) return variable
|
||||
return new PropertyAccessToken(variable, props, this.input, begin, this.p)
|
||||
}
|
||||
|
||||
readNumber (): NumberToken | undefined {
|
||||
this.skipBlank()
|
||||
let decimalFound = false
|
||||
let digitFound = false
|
||||
let n = 0
|
||||
if (this.peekType() & SIGN) n++
|
||||
while (this.p + n <= this.N) {
|
||||
if (this.peekType(n) & NUMBER) {
|
||||
digitFound = true
|
||||
n++
|
||||
} else if (this.peek(n) === '.' && this.peek(n + 1) !== '.') {
|
||||
if (decimalFound || !digitFound) return
|
||||
decimalFound = true
|
||||
n++
|
||||
} else break
|
||||
}
|
||||
if (!props.length && literalValues.hasOwnProperty(variable.content)) {
|
||||
return new LiteralToken(this.input, variable.begin, variable.end, this.file)
|
||||
if (digitFound && !isWord(this.peek(n))) {
|
||||
const num = new NumberToken(this.input, this.p, this.p + n, this.file)
|
||||
this.advance(n)
|
||||
return num
|
||||
}
|
||||
if (isNumber) return new NumberToken(variable, props[0] as IdentifierToken)
|
||||
return new PropertyAccessToken(variable, props, this.p)
|
||||
}
|
||||
|
||||
readLiteral (): LiteralToken | undefined {
|
||||
this.skipBlank()
|
||||
const end = this.matchTrie(this.literalTrie)
|
||||
if (end === -1) return
|
||||
const literal = new LiteralToken(this.input, this.p, end, this.file)
|
||||
this.p = end
|
||||
return literal
|
||||
}
|
||||
|
||||
readRange (): RangeToken | undefined {
|
||||
@@ -387,7 +427,7 @@ export class Tokenizer {
|
||||
}
|
||||
|
||||
peekType (n = 0) {
|
||||
return TYPES[this.input.charCodeAt(this.p + n)]
|
||||
return this.p + n >= this.N ? 0 : TYPES[this.input.charCodeAt(this.p + n)]
|
||||
}
|
||||
|
||||
peek (n = 0): string {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Tokenizer } from '../parser'
|
||||
import { Drop } from '../drop'
|
||||
import { QuotedToken } from '../tokens'
|
||||
import { Context } from '../context'
|
||||
import { toPromise, toValueSync } from '../util'
|
||||
import { evalQuotedToken } from './expression'
|
||||
|
||||
describe('Expression', function () {
|
||||
const ctx = new Context({})
|
||||
@@ -32,7 +34,9 @@ describe('Expression', function () {
|
||||
expect(await toPromise(create('"foo"').evaluate(ctx, false))).toBe('foo')
|
||||
expect(await toPromise(create('false').evaluate(ctx, false))).toBe(false)
|
||||
})
|
||||
|
||||
it('should support evalQuotedToken()', async function () {
|
||||
expect(evalQuotedToken(new QuotedToken('"foo"', 0, 5))).toBe('foo')
|
||||
})
|
||||
it('should eval property access', async function () {
|
||||
const ctx = new Context({
|
||||
foo: { bar: 'BAR' },
|
||||
@@ -162,6 +166,10 @@ describe('Expression', function () {
|
||||
const ctx = new Context({ obj: { foo: 'FOO' }, keys: { "what's this": 'foo' } })
|
||||
expect(await toPromise(create('obj[keys["what\'s this"]]').evaluate(ctx, false))).toBe('FOO')
|
||||
})
|
||||
it('should allow bracket quoted property access', async function () {
|
||||
const ctx = new Context({ 'foo bar': { coo: 'FOO BAR' } })
|
||||
expect(await toPromise(create('["foo bar"].coo').evaluate(ctx, false))).toBe('FOO BAR')
|
||||
})
|
||||
it('should support not', async function () {
|
||||
expect(await toPromise(create('not 1 < 2').evaluate(ctx))).toBe(false)
|
||||
})
|
||||
@@ -169,6 +177,24 @@ describe('Expression', function () {
|
||||
expect(await toPromise(create('not 1 < 2 or not 1 > 2').evaluate(ctx))).toBe(true)
|
||||
expect(await toPromise(create('not 1 < 2 and not 1 > 2').evaluate(ctx))).toBe(false)
|
||||
})
|
||||
it('should allow variable as squared sub property key', async function () {
|
||||
const ctx = new Context({ 'foo': { bar: 'BAR' }, 'key': 'bar' })
|
||||
expect(await toPromise(create('foo[key]').evaluate(ctx))).toBe('BAR')
|
||||
})
|
||||
it('should allow propertyAccessToken as squared sub property key', async function () {
|
||||
const ctx = new Context({ 'foo': { bar: 'BAR', key: 'bar' } })
|
||||
expect(await toPromise(create('foo[foo.key]').evaluate(ctx))).toBe('BAR')
|
||||
})
|
||||
it('should allow nested squared property read', async function () {
|
||||
const ctx = new Context({ 'foo': { bar: 'BAR', key: 'bar' } })
|
||||
expect(await toPromise(create('foo[foo["key"]]').evaluate(ctx))).toBe('BAR')
|
||||
})
|
||||
it('should allow string as property read variable', async function () {
|
||||
expect(await toPromise(create('"foo"[2]').evaluate(ctx))).toBe('o')
|
||||
})
|
||||
it('should allow range as property read variable', async function () {
|
||||
expect(await toPromise(create('(3..5).size').evaluate(ctx))).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sync', function () {
|
||||
|
||||
+11
-18
@@ -1,6 +1,5 @@
|
||||
import { RangeToken, OperatorToken, Token, LiteralToken, NumberToken, PropertyAccessToken, QuotedToken, OperatorType, operatorTypes } from '../tokens'
|
||||
import { isQuotedToken, isWordToken, isNumberToken, isLiteralToken, isRangeToken, isPropertyAccessToken, UndefinedVariableError, range, isOperatorToken, literalValues, assert } from '../util'
|
||||
import { parseStringLiteral } from '../parser'
|
||||
import { QuotedToken, RangeToken, OperatorToken, Token, PropertyAccessToken, OperatorType, operatorTypes } from '../tokens'
|
||||
import { isRangeToken, isPropertyAccessToken, UndefinedVariableError, range, isOperatorToken, assert } from '../util'
|
||||
import type { Context } from '../context'
|
||||
import type { UnaryOperatorHandler } from '../render'
|
||||
|
||||
@@ -36,38 +35,32 @@ export class Expression {
|
||||
}
|
||||
|
||||
export function * evalToken (token: Token | undefined, ctx: Context, lenient = false): IterableIterator<unknown> {
|
||||
if (!token) return
|
||||
if ('content' in token) return token.content
|
||||
if (isPropertyAccessToken(token)) return yield evalPropertyAccessToken(token, ctx, lenient)
|
||||
if (isRangeToken(token)) return yield evalRangeToken(token, ctx)
|
||||
if (isLiteralToken(token)) return evalLiteralToken(token)
|
||||
if (isNumberToken(token)) return evalNumberToken(token)
|
||||
if (isWordToken(token)) return token.getText()
|
||||
if (isQuotedToken(token)) return evalQuotedToken(token)
|
||||
}
|
||||
|
||||
function * evalPropertyAccessToken (token: PropertyAccessToken, ctx: Context, lenient: boolean): IterableIterator<unknown> {
|
||||
const props: string[] = []
|
||||
const variable = yield evalToken(token.variable, ctx, lenient)
|
||||
for (const prop of token.props) {
|
||||
props.push((yield evalToken(prop, ctx, false)) as unknown as string)
|
||||
}
|
||||
try {
|
||||
return yield ctx._get([token.propertyName, ...props])
|
||||
if (token.variable) {
|
||||
return yield ctx._getFromScope(variable, props)
|
||||
} else {
|
||||
return yield ctx._get(props)
|
||||
}
|
||||
} catch (e) {
|
||||
if (lenient && (e as Error).name === 'InternalUndefinedVariableError') return null
|
||||
throw (new UndefinedVariableError(e as Error, token))
|
||||
}
|
||||
}
|
||||
|
||||
function evalNumberToken (token: NumberToken) {
|
||||
const str = token.whole.content + '.' + (token.decimal ? token.decimal.content : '')
|
||||
return Number(str)
|
||||
}
|
||||
|
||||
export function evalQuotedToken (token: QuotedToken) {
|
||||
return parseStringLiteral(token.getText())
|
||||
}
|
||||
|
||||
function evalLiteralToken (token: LiteralToken) {
|
||||
return literalValues[token.literal]
|
||||
return token.content
|
||||
}
|
||||
|
||||
function * evalRangeToken (token: RangeToken, ctx: Context) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseStringLiteral } from './parse-string-literal'
|
||||
import { parseStringLiteral } from './string'
|
||||
|
||||
describe('parseStringLiteral()', function () {
|
||||
it('should parse octal escape', () => {
|
||||
+6
-1
@@ -17,7 +17,12 @@ export default class extends Tag {
|
||||
const values: ValueToken[] = []
|
||||
while (!token.tokenizer.end()) {
|
||||
values.push(token.tokenizer.readValueOrThrow())
|
||||
token.tokenizer.readTo(',')
|
||||
token.tokenizer.skipBlank()
|
||||
if (token.tokenizer.peek() === ',') {
|
||||
token.tokenizer.readTo(',')
|
||||
} else {
|
||||
token.tokenizer.readTo('or')
|
||||
}
|
||||
}
|
||||
this.branches.push({
|
||||
values,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Context } from '../context'
|
||||
import { toPromise } from '../util'
|
||||
import { IdentifierToken, NumberToken, QuotedToken } from '../tokens'
|
||||
import { NumberToken, QuotedToken } from '../tokens'
|
||||
import { Filter } from './filter'
|
||||
|
||||
describe('filter', function () {
|
||||
@@ -13,7 +13,7 @@ describe('filter', function () {
|
||||
|
||||
it('should call filter impl with correct arguments', async function () {
|
||||
const spy = jest.fn()
|
||||
const thirty = new NumberToken(new IdentifierToken('30', 0, 2), undefined)
|
||||
const thirty = new NumberToken('30', 0, 2, undefined)
|
||||
const filter = new Filter('foo', spy, [thirty], liquid)
|
||||
await toPromise(filter.render('foo', ctx))
|
||||
expect(spy).toHaveBeenCalledWith('foo', 30)
|
||||
@@ -23,7 +23,7 @@ describe('filter', function () {
|
||||
const val = yield this.context._get([valStr])
|
||||
return `${this.liquid.testVersion}: ${val + diff}`
|
||||
})
|
||||
const ten = new NumberToken(new IdentifierToken('10', 0, 2), undefined)
|
||||
const ten = new NumberToken('10', 0, 2, undefined)
|
||||
const filter = new Filter('add', spy, [ten], liquid)
|
||||
const val = await toPromise(filter.render('thirty', ctx))
|
||||
expect(val).toEqual('1.0: 40')
|
||||
@@ -31,14 +31,19 @@ describe('filter', function () {
|
||||
it('should render a simple filter', async function () {
|
||||
expect(await toPromise(new Filter('upcase', (x: string) => x.toUpperCase(), [], liquid).render('foo', ctx))).toBe('FOO')
|
||||
})
|
||||
|
||||
it('should reject promise when filter throws', async function () {
|
||||
const filter = new Filter('foo', function * () { throw new Error('intended') }, [], liquid)
|
||||
expect(toPromise(filter.render('foo', ctx))).rejects.toMatchObject({
|
||||
message: 'intended'
|
||||
})
|
||||
})
|
||||
it('should render filters with argument', async function () {
|
||||
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
|
||||
const two = new NumberToken('2', 0, 1, undefined)
|
||||
expect(await toPromise(new Filter('add', (a: number, b: number) => a + b, [two], liquid).render(3, ctx))).toBe(5)
|
||||
})
|
||||
|
||||
it('should render filters with multiple arguments', async function () {
|
||||
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
|
||||
const two = new NumberToken('2', 0, 1, undefined)
|
||||
const c = new QuotedToken('"c"', 0, 3)
|
||||
expect(await toPromise(new Filter('add', (a: number, b: number, c: number) => a + b + c, [two, c], liquid).render(3, ctx))).toBe('5c')
|
||||
})
|
||||
@@ -49,7 +54,7 @@ describe('filter', function () {
|
||||
})
|
||||
|
||||
it('should support key value pairs', async function () {
|
||||
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
|
||||
const two = new NumberToken('2', 0, 1, undefined)
|
||||
expect(await toPromise(new Filter('add', (a: number, b: number[]) => b[0] + ':' + (a + b[1]), [['num', two]], liquid).render(3, ctx))).toBe('num:5')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,6 +27,6 @@ export class Filter {
|
||||
if (isKeyValuePair(arg)) argv.push([arg[0], yield evalToken(arg[1], context)])
|
||||
else argv.push(yield evalToken(arg, context))
|
||||
}
|
||||
return this.handler.apply({ context, liquid: this.liquid }, [value, ...argv])
|
||||
return yield this.handler.apply({ context, liquid: this.liquid }, [value, ...argv])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Token } from './token'
|
||||
import { TokenKind } from '../parser'
|
||||
import { literalValues, LiteralValue } from '../util'
|
||||
|
||||
export class LiteralToken extends Token {
|
||||
public content: LiteralValue
|
||||
public literal: string
|
||||
public constructor (
|
||||
public input: string,
|
||||
@@ -11,5 +13,6 @@ export class LiteralToken extends Token {
|
||||
) {
|
||||
super(TokenKind.Literal, input, begin, end, file)
|
||||
this.literal = this.getText()
|
||||
this.content = literalValues[this.literal]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Token } from './token'
|
||||
import { IdentifierToken } from './identifier-token'
|
||||
import { TokenKind } from '../parser'
|
||||
|
||||
export class NumberToken extends Token {
|
||||
public content: number
|
||||
constructor (
|
||||
public whole: IdentifierToken,
|
||||
public decimal?: IdentifierToken
|
||||
public input: string,
|
||||
public begin: number,
|
||||
public end: number,
|
||||
public file?: string
|
||||
) {
|
||||
super(TokenKind.Number, whole.input, whole.begin, decimal ? decimal.end : whole.end, whole.file)
|
||||
super(TokenKind.Number, input, begin, end, file)
|
||||
this.content = Number(this.getText())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { QuotedToken, PropertyAccessToken, IdentifierToken } from '.'
|
||||
|
||||
describe('PropertyAccessToken', function () {
|
||||
describe('#propertyName', function () {
|
||||
it('should return correct value for IdentifierToken', function () {
|
||||
const token = new PropertyAccessToken(new IdentifierToken('foo', 0, 3), [], 3)
|
||||
expect(token.propertyName).toBe('foo')
|
||||
})
|
||||
it('should return correct value for QuotedToken', function () {
|
||||
const token = new PropertyAccessToken(new QuotedToken('"foo bar"', 0, 9), [], 9)
|
||||
expect(token.propertyName).toBe('foo bar')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,18 +1,21 @@
|
||||
import { Token } from './token'
|
||||
import { LiteralToken } from './literal-token'
|
||||
import { ValueToken } from './value-token'
|
||||
import { IdentifierToken } from './identifier-token'
|
||||
import { NumberToken } from './number-token'
|
||||
import { RangeToken } from './range-token'
|
||||
import { QuotedToken } from './quoted-token'
|
||||
import { TokenKind, parseStringLiteral } from '../parser'
|
||||
import { TokenKind } from '../parser'
|
||||
|
||||
export class PropertyAccessToken extends Token {
|
||||
public propertyName: string
|
||||
constructor (
|
||||
public variable: IdentifierToken | QuotedToken,
|
||||
public props: (IdentifierToken | QuotedToken | PropertyAccessToken)[],
|
||||
end: number
|
||||
public variable: QuotedToken | RangeToken | LiteralToken | NumberToken | undefined,
|
||||
public props: (ValueToken | IdentifierToken)[],
|
||||
input: string,
|
||||
begin: number,
|
||||
end: number,
|
||||
file?: string
|
||||
) {
|
||||
super(TokenKind.PropertyAccess, variable.input, variable.begin, end, variable.file)
|
||||
this.propertyName = this.variable instanceof IdentifierToken
|
||||
? this.variable.getText()
|
||||
: parseStringLiteral(this.variable.getText())
|
||||
super(TokenKind.PropertyAccess, input, begin, end, file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Token } from './token'
|
||||
import { TokenKind } from '../parser'
|
||||
import { parseStringLiteral } from '../render/string'
|
||||
|
||||
export class QuotedToken extends Token {
|
||||
public readonly content: string
|
||||
constructor (
|
||||
public input: string,
|
||||
public begin: number,
|
||||
@@ -9,5 +11,6 @@ export class QuotedToken extends Token {
|
||||
public file?: string
|
||||
) {
|
||||
super(TokenKind.Quoted, input, begin, end, file)
|
||||
this.content = parseStringLiteral(this.getText())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { RangeToken } from './range-token'
|
||||
import { LiteralToken } from './literal-token'
|
||||
import { NumberToken } from './number-token'
|
||||
import { QuotedToken } from './quoted-token'
|
||||
import { PropertyAccessToken } from './property-access-token'
|
||||
|
||||
export type ValueToken = RangeToken | LiteralToken | QuotedToken | PropertyAccessToken
|
||||
export type ValueToken = RangeToken | LiteralToken | QuotedToken | PropertyAccessToken | NumberToken
|
||||
|
||||
@@ -3,11 +3,18 @@
|
||||
// This file is generated by bin/character-gen.js
|
||||
// bitmask character types to boost performance
|
||||
export const TYPES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 4, 4, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 2, 8, 0, 0, 0, 0, 8, 0, 0, 0, 64, 0, 65, 0, 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 2, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
|
||||
export const IDENTIFIER = 1
|
||||
export const WORD = 1
|
||||
export const OPERATOR = 2
|
||||
export const BLANK = 4
|
||||
export const QUOTE = 8
|
||||
export const INLINE_BLANK = 16
|
||||
export const NUMBER = 32
|
||||
export const SIGN = 64
|
||||
export const PUNCTUATION = 128
|
||||
|
||||
export function isWord (char: string): boolean {
|
||||
const code = char.charCodeAt(0)
|
||||
return code >= 128 ? !TYPES[code] : !!(TYPES[code] & WORD)
|
||||
}
|
||||
TYPES[160] = TYPES[5760] = TYPES[6158] = TYPES[8192] = TYPES[8193] = TYPES[8194] = TYPES[8195] = TYPES[8196] = TYPES[8197] = TYPES[8198] = TYPES[8199] = TYPES[8200] = TYPES[8201] = TYPES[8202] = TYPES[8232] = TYPES[8233] = TYPES[8239] = TYPES[8287] = TYPES[12288] = BLANK
|
||||
TYPES[8220] = TYPES[8221] = PUNCTUATION
|
||||
|
||||
@@ -2,6 +2,11 @@ import * as _ from './underscore'
|
||||
import { Token } from '../tokens/token'
|
||||
import { Template } from '../template/template'
|
||||
|
||||
/**
|
||||
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to kee track of classes
|
||||
*/
|
||||
const TRAIT = '__liquidClass__'
|
||||
|
||||
export abstract class LiquidError extends Error {
|
||||
private token!: Token
|
||||
public context = ''
|
||||
@@ -14,6 +19,7 @@ export abstract class LiquidError extends Error {
|
||||
super(typeof err === 'string' ? err : err.message)
|
||||
if (typeof err !== 'string') Object.defineProperty(this, 'originalError', { value: err, enumerable: false })
|
||||
Object.defineProperty(this, 'token', { value: token, enumerable: false })
|
||||
Object.defineProperty(this, TRAIT, { value: 'LiquidError', enumerable: false })
|
||||
}
|
||||
protected update () {
|
||||
Object.defineProperty(this, 'context', { value: mkContext(this.token), enumerable: false })
|
||||
@@ -22,6 +28,9 @@ export abstract class LiquidError extends Error {
|
||||
'\n' + this.stack
|
||||
if (this.originalError) this.stack += '\nFrom ' + this.originalError.stack
|
||||
}
|
||||
static is (obj: unknown): obj is LiquidError {
|
||||
return obj?.[TRAIT] === 'LiquidError'
|
||||
}
|
||||
}
|
||||
|
||||
export class TokenizationError extends LiquidError {
|
||||
|
||||
@@ -9,3 +9,6 @@ export const literalValues = {
|
||||
'empty': new EmptyDrop(),
|
||||
'blank': new BlankDrop()
|
||||
}
|
||||
|
||||
export type LiteralKey = keyof typeof literalValues
|
||||
export type LiteralValue = typeof literalValues[LiteralKey]
|
||||
|
||||
+16
-13
@@ -1,35 +1,38 @@
|
||||
import { Operators, OperatorHandler } from '../render/operator'
|
||||
import { IDENTIFIER, TYPES } from '../util/character'
|
||||
import { isWord } from '../util/character'
|
||||
|
||||
interface TrieLeafNode {
|
||||
handler: OperatorHandler;
|
||||
interface TrieInput<T> {
|
||||
[key: string]: T
|
||||
}
|
||||
|
||||
interface TrieLeafNode<T> {
|
||||
data: T;
|
||||
end: true;
|
||||
needBoundary?: true;
|
||||
}
|
||||
|
||||
export interface Trie {
|
||||
[key: string]: Trie | TrieLeafNode;
|
||||
export interface Trie<T> {
|
||||
[key: string]: Trie<T> | TrieLeafNode<T>;
|
||||
}
|
||||
|
||||
export type TrieNode = Trie | TrieLeafNode
|
||||
export type TrieNode<T> = Trie<T> | TrieLeafNode<T>
|
||||
|
||||
export function createTrie (operators: Operators): Trie {
|
||||
const trie: Trie = {}
|
||||
for (const [name, handler] of Object.entries(operators)) {
|
||||
let node: Trie | TrieLeafNode = trie
|
||||
export function createTrie<T = any> (input: TrieInput<T>): Trie<T> {
|
||||
const trie: Trie<T> = {}
|
||||
for (const [name, data] of Object.entries(input)) {
|
||||
let node: Trie<T> | TrieLeafNode<T> = trie
|
||||
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
const c = name[i]
|
||||
node[c] = node[c] || {}
|
||||
|
||||
if (i === name.length - 1 && (TYPES[name.charCodeAt(i)] & IDENTIFIER)) {
|
||||
if (i === name.length - 1 && isWord(name[i])) {
|
||||
node[c].needBoundary = true
|
||||
}
|
||||
|
||||
node = node[c]
|
||||
}
|
||||
|
||||
node.handler = handler
|
||||
node.data = data
|
||||
node.end = true
|
||||
}
|
||||
return trie
|
||||
|
||||
@@ -32,13 +32,30 @@ describe('util/strftime', function () {
|
||||
})
|
||||
})
|
||||
it('should format %q as date suffix', function () {
|
||||
const st = new Date('2016-03-01 03:05:03')
|
||||
const nd = new Date('2016-03-02 03:05:03')
|
||||
const rd = new Date('2016-03-03 03:05:03')
|
||||
expect(t(st, '%q')).toBe('st')
|
||||
expect(t(nd, '%q')).toBe('nd')
|
||||
expect(t(rd, '%q')).toBe('rd')
|
||||
const first = new Date('2016-03-01 03:05:03')
|
||||
const second = new Date('2016-03-02 03:05:03')
|
||||
const third = new Date('2016-03-03 03:05:03')
|
||||
|
||||
const eleventh = new Date('2016-03-11 03:05:03')
|
||||
const twelfth = new Date('2016-03-12 03:05:03')
|
||||
const thirteenth = new Date('2016-03-13 03:05:03')
|
||||
|
||||
const twentyfirst = new Date('2016-03-21 03:05:03')
|
||||
const twentysecond = new Date('2016-03-22 03:05:03')
|
||||
const twentythird = new Date('2016-03-23 03:05:03')
|
||||
|
||||
expect(t(first, '%q')).toBe('st')
|
||||
expect(t(second, '%q')).toBe('nd')
|
||||
expect(t(third, '%q')).toBe('rd')
|
||||
expect(t(now, '%q')).toBe('th')
|
||||
|
||||
expect(t(eleventh, '%q')).toBe('th')
|
||||
expect(t(twelfth, '%q')).toBe('th')
|
||||
expect(t(thirteenth, '%q')).toBe('th')
|
||||
|
||||
expect(t(twentyfirst, '%q')).toBe('st')
|
||||
expect(t(twentysecond, '%q')).toBe('nd')
|
||||
expect(t(twentythird, '%q')).toBe('rd')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
+24
-9
@@ -11,12 +11,6 @@ const dayNames = [
|
||||
]
|
||||
const monthNamesShort = monthNames.map(abbr)
|
||||
const dayNamesShort = dayNames.map(abbr)
|
||||
const suffixes = {
|
||||
1: 'st',
|
||||
2: 'nd',
|
||||
3: 'rd',
|
||||
'default': 'th'
|
||||
}
|
||||
interface FormatOptions {
|
||||
flags: object;
|
||||
width?: string;
|
||||
@@ -52,9 +46,30 @@ function isLeapYear (d: LiquidDate) {
|
||||
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)))
|
||||
}
|
||||
function getSuffix (d: LiquidDate) {
|
||||
const str = d.getDate().toString()
|
||||
const index = parseInt(str.slice(-1))
|
||||
return suffixes[index] || suffixes['default']
|
||||
const date = d.getDate()
|
||||
|
||||
let suffix = 'th'
|
||||
|
||||
switch (date) {
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
break
|
||||
default:
|
||||
switch (date % 10) {
|
||||
case 1:
|
||||
suffix = 'st'
|
||||
break
|
||||
case 2:
|
||||
suffix = 'nd'
|
||||
break
|
||||
case 3:
|
||||
suffix = 'rd'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return suffix
|
||||
}
|
||||
function century (d: LiquidDate) {
|
||||
return parseInt(d.getFullYear().toString().substring(0, 2), 10)
|
||||
|
||||
@@ -63,6 +63,10 @@ export function isNil (value: any): boolean {
|
||||
return value == null
|
||||
}
|
||||
|
||||
export function isUndefined (value: any): boolean {
|
||||
return value === undefined
|
||||
}
|
||||
|
||||
export function isArray (value: any): value is any[] {
|
||||
// be compatible with IE 8
|
||||
return toString.call(value) === '[object Array]'
|
||||
|
||||
@@ -7,4 +7,18 @@ describe('browser', function () {
|
||||
message: 'output "{{huh" not closed, line:1, col:1'
|
||||
})
|
||||
})
|
||||
it('should throw tokenization error for invalid filter syntax', async () => {
|
||||
const engine = new LiquidUMD()
|
||||
const message = 'expected filter name, line:1, col:10'
|
||||
const stack = [
|
||||
'>> 1| {{ foo | ^ }}',
|
||||
' ^',
|
||||
`TokenizationError: ${message}`
|
||||
].join('\n')
|
||||
await expect(engine.parseAndRender('{{ foo | ^ }}')).rejects.toMatchObject({
|
||||
message,
|
||||
stack: expect.stringContaining(stack),
|
||||
name: 'TokenizationError'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -431,4 +431,42 @@ describe('Issues', function () {
|
||||
const fn = () => engine.parseAndRenderSync("{%- assign module = '' | split '' -%}")
|
||||
expect(fn).toThrow(/expected ":" after filter name/)
|
||||
})
|
||||
it('#628 Single or double quote breaks comments', () => {
|
||||
const template = `{%- liquid
|
||||
# Show a message that's customized to the product type
|
||||
|
||||
assign product_type = product.type | downcase
|
||||
assign message = ''
|
||||
|
||||
case product_type
|
||||
when 'health'
|
||||
assign message = 'This is a health potion!'
|
||||
when 'love'
|
||||
assign message = 'This is a love potion!'
|
||||
else
|
||||
assign message = 'This is a potion!'
|
||||
endcase
|
||||
|
||||
echo message
|
||||
-%}`
|
||||
const engine = new Liquid()
|
||||
const product = { type: 'love' }
|
||||
const result = engine.parseAndRenderSync(template, { product })
|
||||
expect(result).toEqual('This is a love potion!')
|
||||
})
|
||||
it('#643 Error When Accessing Subproperty of Bracketed Reference', () => {
|
||||
const engine = new Liquid()
|
||||
const tpl = '{{ ["Key String with Spaces"].subpropertyKey }}'
|
||||
const ctx = {
|
||||
'Key String with Spaces': {
|
||||
subpropertyKey: 'FOO'
|
||||
}
|
||||
}
|
||||
expect(engine.parseAndRenderSync(tpl, ctx)).toEqual('FOO')
|
||||
})
|
||||
it('#655 Error in the tokenization process due to an invalid value expression', () => {
|
||||
const engine = new Liquid()
|
||||
const result = engine.parseAndRenderSync('{{ÜLKE}}', { ÜLKE: 'Türkiye' })
|
||||
expect(result).toEqual('Türkiye')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,6 +58,14 @@ describe('filters/array', function () {
|
||||
const post = { category: 'foo' }
|
||||
return test('{{post | map: "category"}}', { post }, 'foo')
|
||||
})
|
||||
it('should allow nil results in strictVariables mode', function () {
|
||||
const engine = new Liquid({ strictVariables: true })
|
||||
const ctx = {
|
||||
posts: [{ category: 'foo' }, { title: 'bar' }]
|
||||
}
|
||||
const result = engine.parseAndRenderSync('{{posts | map: "category" | json}}', ctx)
|
||||
expect(result).toEqual('["foo",null]')
|
||||
})
|
||||
it('should support nested property', function () {
|
||||
const tpl = '{{ arr | map: "name.first" | join }}'
|
||||
const a = { name: { first: 'Alice' } }
|
||||
@@ -66,6 +74,44 @@ describe('filters/array', function () {
|
||||
return test(tpl, { arr: [a, b, c] }, 'Alice Bob Carol')
|
||||
})
|
||||
})
|
||||
describe('sum', () => {
|
||||
it('should support sum with no args', function () {
|
||||
const ages = [21, null, -4, '4.5', 13.25, undefined, 0]
|
||||
return test('{{ages | sum}}', { ages }, '34.75')
|
||||
})
|
||||
it('should support sum with property', function () {
|
||||
const ages = [21, null, -4, '4.5', 13.25, undefined, 0].map(x => ({ age: x }))
|
||||
return test('{{ages | sum: "age"}}', { ages }, '34.75')
|
||||
})
|
||||
it('should support sum with nested property', function () {
|
||||
const ages = [21, null, -4, '4.5', 13.25, undefined, 0].map(x => ({ age: { first: x } }))
|
||||
return test('{{ages | sum: "age.first"}}', { ages }, '34.75')
|
||||
})
|
||||
it('should support non-array input', function () {
|
||||
const age = 21.5
|
||||
return test('{{age | sum}}', { age }, '21.5')
|
||||
})
|
||||
it('should coerce missing property to zero', function () {
|
||||
const ages = [{ qty: 1 }, { qty: 2, cnt: 3 }, { cnt: 4 }]
|
||||
return test('{{ages | sum}} {{ages | sum: "cnt"}} {{ages | sum: "other"}}', { ages }, '0 7 0')
|
||||
})
|
||||
it('should coerce indexable non-map values to zero', function () {
|
||||
const input = [1, 'foo', { quantity: 3 }]
|
||||
return test('{{input | sum}}', { input }, '1')
|
||||
})
|
||||
it('should coerce unindexable values to zero', function () {
|
||||
const input = [1, null, { quantity: 2 }]
|
||||
return test('{{input | sum}}', { input }, '1')
|
||||
})
|
||||
it('should coerce true to 1', function () {
|
||||
const input = [1, true, null, { quantity: 2 }]
|
||||
return test('{{input | sum}}', { input }, '2')
|
||||
})
|
||||
it('should not support nested arrays', function () {
|
||||
const ages = [1, [2, [3, 4]]]
|
||||
return test('{{ages | sum}}', { ages }, '1')
|
||||
})
|
||||
})
|
||||
describe('compact', () => {
|
||||
it('should compact array', function () {
|
||||
const posts = [{ category: 'foo' }, { category: 'bar' }, { foo: 'bar' }]
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('filters/html', function () {
|
||||
it('should support string_with_newlines', function () {
|
||||
const src = '{% capture string_with_newlines %}\n' +
|
||||
'Hello\n' +
|
||||
'there\n' +
|
||||
'there\r\n' +
|
||||
'{% endcapture %}' +
|
||||
'{{ string_with_newlines | newline_to_br }}'
|
||||
const dst = '<br />\n' +
|
||||
|
||||
@@ -137,6 +137,12 @@ describe('filters/string', function () {
|
||||
'{{ string_with_newlines | strip_newlines }}',
|
||||
'Hellothere')
|
||||
})
|
||||
it('should support strip_newlines on Windows newlines ', function () {
|
||||
return test('{% capture string_with_newlines %}\n' +
|
||||
'Hello\r\nthere\n{% endcapture %}' +
|
||||
'{{ string_with_newlines | strip_newlines }}',
|
||||
'Hellothere')
|
||||
})
|
||||
describe('truncate', function () {
|
||||
it('should truncate when string too long', function () {
|
||||
return test('{{ "Ground control to Major Tom." | truncate: 20 }}',
|
||||
@@ -203,6 +209,10 @@ describe('filters/string', function () {
|
||||
return test('{{ "I strained to see the train through the rain" | remove_last: "rain" }}',
|
||||
'I strained to see the train through the ')
|
||||
})
|
||||
it('should remove the last occurrence of substring in the middle of a string', function () {
|
||||
return test('{{ "I strained to see the train through the rain and fog" | remove_last: "rain" }}',
|
||||
'I strained to see the train through the and fog')
|
||||
})
|
||||
it('should handle substring not found', function () {
|
||||
return test('{{ "I strained to see the train through the rain" | remove_last: "no such thing" }}',
|
||||
'I strained to see the train through the rain')
|
||||
|
||||
@@ -75,4 +75,20 @@ describe('tags/case', function () {
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('firstsecond')
|
||||
})
|
||||
it('should support case with multiple values separated by or', async function () {
|
||||
const src = '{% case 3 %}' +
|
||||
'{% when 1 or 2 or 3 %}1 or 2 or 3' +
|
||||
'{% else %}not 1 or 2 or 3' +
|
||||
'{%endcase%}'
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('1 or 2 or 3')
|
||||
})
|
||||
it('should support case with multiple strings separated by or', async function () {
|
||||
const src = '{% case "or" %}' +
|
||||
'{% when "and" or "or" %}and or or' +
|
||||
'{% else %}not and or or' +
|
||||
'{%endcase%}'
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('and or or')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -59,6 +59,12 @@ describe('tags/for', function () {
|
||||
const html = await liquid.parseAndRender(src, scope)
|
||||
return expect(html).toBe('str-"string"-string')
|
||||
})
|
||||
it('should not report undefined variable on null value', async function () {
|
||||
const engine = new Liquid({ strictVariables: true })
|
||||
const src = '{% assign hello = "hello,world" | split: "," | concat: null %}{% for i in hello %}{{ i }},{% endfor %}'
|
||||
const html = await engine.parseAndRender(src, scope)
|
||||
return expect(html).toBe('hello,world,,')
|
||||
})
|
||||
describe('illegal', function () {
|
||||
it('should reject when for not closed', function () {
|
||||
const src = '{%for c in alpha%}{{c}}'
|
||||
|
||||
@@ -17,6 +17,16 @@ describe('tags/inline-comment', function () {
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('foo')
|
||||
})
|
||||
it('should allow single quotes', async function () {
|
||||
const src = "B{% # that's %}A"
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('BA')
|
||||
})
|
||||
it('should allow double quotes', async function () {
|
||||
const src = 'B{% # that"s %}A'
|
||||
const html = await liquid.parseAndRender(src)
|
||||
return expect(html).toBe('BA')
|
||||
})
|
||||
it('should handle hash without trailing whitespace', async function () {
|
||||
const src = '{% #some comment %}'
|
||||
const html = await liquid.parseAndRender(src)
|
||||
|
||||
Reference in New Issue
Block a user