mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-13 19:30:39 -07:00
Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7ae3deb16 | ||
|
|
d48ac56824 | ||
|
|
2b713b721d | ||
|
|
258780e9a8 | ||
|
|
18d55929e5 | ||
|
|
ccd83bc6bd | ||
|
|
306b070507 | ||
|
|
05223c4378 | ||
|
|
01029aba87 | ||
|
|
902a8e4c22 | ||
|
|
08421ea70c | ||
|
|
f816955704 | ||
|
|
1937aa1f1d | ||
|
|
4518a19588 | ||
|
|
dafdfbc849 | ||
|
|
3632623456 | ||
|
|
85f5b327fa | ||
|
|
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 |
+75
-3
@@ -321,8 +321,7 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/2703309?v=4",
|
||||
"profile": "https://github.com/amit777",
|
||||
"contributions": [
|
||||
"code",
|
||||
"financial"
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -577,8 +576,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": "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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "brunodccarvalho",
|
||||
"name": "Bruno Carvalho",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24962950?v=4",
|
||||
"profile": "https://github.com/brunodccarvalho",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fupengl",
|
||||
"name": "傅鹏",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20211964?v=4",
|
||||
"profile": "https://github.com/fupengl",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joel-hamilton",
|
||||
"name": "Joel Hamilton",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12899024?v=4",
|
||||
"profile": "https://github.com/joel-hamilton",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
"skipCi": true,
|
||||
"commitType": "docs"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Build
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
bundles:
|
||||
required: false
|
||||
type: string
|
||||
env:
|
||||
BUNDLES: ${{ inputs.bundles }}
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist-${{ inputs.os }}
|
||||
path: dist
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Check
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
- name: Test
|
||||
run: |
|
||||
npm run lint
|
||||
TZ=Etc/GMT npm run test
|
||||
TZ=Asia/Shanghai npm run test
|
||||
TZ=America/New_York npm run test
|
||||
- name: Coverage
|
||||
uses: coverallsapp/[email protected]
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Performance
|
||||
run: |
|
||||
npm run perf:diff
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
@@ -0,0 +1,28 @@
|
||||
name: CI Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'test'
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
lint:
|
||||
uses: ./.github/workflows/lint.yml
|
||||
test:
|
||||
needs: build
|
||||
uses: ./.github/workflows/test.yml
|
||||
coverage:
|
||||
uses: ./.github/workflows/coverage.yml
|
||||
performance:
|
||||
needs: build
|
||||
uses: ./.github/workflows/performance.yml
|
||||
with:
|
||||
os: ubuntu-latest
|
||||
docs:
|
||||
uses: ./.github/workflows/docs.yml
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Test Coverage
|
||||
on: [workflow_dispatch, workflow_call]
|
||||
jobs:
|
||||
coverage:
|
||||
name: Test Coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Test
|
||||
run: npm run test:coverage
|
||||
- name: Coverage
|
||||
uses: coverallsapp/[email protected]
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
@@ -1,32 +1,30 @@
|
||||
name: Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
on: [workflow_dispatch, workflow_call]
|
||||
jobs:
|
||||
docs:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npm run build:docs
|
||||
- name: Publish
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: JamesIves/[email protected]
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: docs/public
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Lint
|
||||
on: [workflow_dispatch, workflow_call]
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Performance Check
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
performance:
|
||||
name: Performance
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist-${{ inputs.os }}
|
||||
path: dist
|
||||
- name: Check Performance
|
||||
run: npm run perf:diff
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
@@ -0,0 +1,22 @@
|
||||
name: PR Build
|
||||
on: pull_request
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
lint:
|
||||
uses: ./.github/workflows/lint.yml
|
||||
test:
|
||||
needs: build
|
||||
uses: ./.github/workflows/test.yml
|
||||
coverage:
|
||||
uses: ./.github/workflows/coverage.yml
|
||||
performance:
|
||||
needs: build
|
||||
uses: ./.github/workflows/performance.yml
|
||||
with:
|
||||
os: ubuntu-latest
|
||||
@@ -1,15 +1,12 @@
|
||||
name: "PR Title Checker"
|
||||
|
||||
name: PR Check
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Validate PR title
|
||||
title:
|
||||
name: Check PR title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
@@ -6,32 +6,27 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:dist
|
||||
- name: Test
|
||||
run: |
|
||||
npm run lint
|
||||
npm run test
|
||||
- name: Coverage
|
||||
uses: coverallsapp/[email protected]
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
node-version: '18'
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npx semantic-release
|
||||
run: |
|
||||
if [ ${{ github.ref == 'refs/heads/master' }} ]; then
|
||||
npx semantic-release
|
||||
else
|
||||
npx semantic-release --dry-run
|
||||
fi
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Test
|
||||
on: [workflow_dispatch, workflow_call]
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
timezone: [Etc/GMT, Asia/Shanghai, America/New_York]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist-${{ matrix.os }}
|
||||
path: dist
|
||||
- name: Run Test
|
||||
run: TZ=${{ matrix.timezone }} npm test
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: npm-logs
|
||||
path: ~/.npm/_logs
|
||||
+4
-1
@@ -9,8 +9,11 @@ coverage/
|
||||
|
||||
# modules
|
||||
node_modules/
|
||||
|
||||
# tmp
|
||||
docs/public/js/liquid.browser.min.js
|
||||
docs/themes/navy/layout/partial/all-contributors.swig
|
||||
dist/
|
||||
demo/*/package-lock.json
|
||||
demo/*/yarn.json
|
||||
|
||||
# editors
|
||||
|
||||
@@ -1,3 +1,90 @@
|
||||
# [10.11.0](https://github.com/harttle/liquidjs/compare/v10.10.2...v10.11.0) (2024-04-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* group_by/group_by_exp/find/find_exp from Jekyll, [#443](https://github.com/harttle/liquidjs/issues/443) ([2b713b7](https://github.com/harttle/liquidjs/commit/2b713b721d1f355309a70ebb5846169c6c03c523))
|
||||
* pop/shift/unshift filters from Jekyll ([258780e](https://github.com/harttle/liquidjs/commit/258780e9a87ce87534a6bb4336725cb1d38a2998))
|
||||
|
||||
## [10.10.2](https://github.com/harttle/liquidjs/compare/v10.10.1...v10.10.2) (2024-03-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* contains regression ([#677](https://github.com/harttle/liquidjs/issues/677)) ([05223c4](https://github.com/harttle/liquidjs/commit/05223c4378f9474f4e658af36cb8272e161d681f)), closes [#675](https://github.com/harttle/liquidjs/issues/675)
|
||||
|
||||
## [10.10.1](https://github.com/harttle/liquidjs/compare/v10.10.0...v10.10.1) (2024-02-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* in conditionals, don't render anything after an else branch ([#671](https://github.com/harttle/liquidjs/issues/671)) ([f816955](https://github.com/harttle/liquidjs/commit/f81695570491ede77975de2c26a07612a2d62c28))
|
||||
* Rely on equal for computing contains ([#668](https://github.com/harttle/liquidjs/issues/668)) ([1937aa1](https://github.com/harttle/liquidjs/commit/1937aa1f1dce01ee6332f39a6e658e85cbe4f30b))
|
||||
|
||||
# [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)
|
||||
|
||||
|
||||
|
||||
+17
-25
@@ -16,14 +16,14 @@
|
||||
- Ensure the build runs because the Husky pre-commit hook checks it
|
||||
- `npm run check` checks runs the build, tests, lint and perf tests
|
||||
- `commitlint` checks the commit message format
|
||||
|
||||
|
||||
If there is a problem you will see it in the pre-commit hook output.
|
||||
In VS Code, this output will be shown in a new file in a new tab if the
|
||||
pre-commit hook fails.
|
||||
If you want to check the commit message without using the VS Code Source
|
||||
Control UI, you can run `echo "feat: my commit message" > npx commitlint`
|
||||
directly.
|
||||
|
||||
|
||||
- `git switch -c your_branch_name` (do this in your fork not the main repo)
|
||||
- `git add .`
|
||||
- `git commit -m "feat: Adding my change"`
|
||||
@@ -33,32 +33,24 @@
|
||||
## Playground
|
||||
|
||||
The Playground runs off the `docs` directory.
|
||||
`npm run:docs` is used to build it and that's included in `npm run build`.
|
||||
`npm run build:docs` is used to build it.
|
||||
|
||||
To start the site locally, go to `docs` and run `npm start`, then visit
|
||||
Then, to start the site locally, go to `docs` and run `npm start`, then visit
|
||||
http://localhost:4000/playground.html.
|
||||
|
||||
At the moment, the Playground uses the latest NPM version of the LiquidJS
|
||||
library instead of using the built artifact produced by `npm run build:dist`
|
||||
(also included in `npm run build`).
|
||||
The Playground uses a local built LiquidJS, which is created during `npm run build:docs`.
|
||||
To update that, you'll need to run `./bin/build-docs-liquid.sh` each time after making changes.
|
||||
Then refresh the Playground site for the changes to take effect.
|
||||
|
||||
To use the Playground with the local build of the library, make these changes:
|
||||
## Performance
|
||||
|
||||
- Copy `dist/liquid.browser.min.js` to `docs/public/js/liquid.browser.min.js`
|
||||
- Open `docs/themes/navy/layout/partial/after_footer.swig`
|
||||
- Remove `https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js` line
|
||||
- Add `{{ js('liquid.browser.min.js') }}` line before `{{ js('js/main') }}` line
|
||||
- Refresh the Playground site for the changes to take effect
|
||||
- Replace `liquid.browser.min.js` each time after making changes and building
|
||||
If your change can have a performance impact, you can update and run performance cases under `benchmark/`.
|
||||
|
||||
```diff
|
||||
{% if page.layout === 'playground' %}
|
||||
-<script src="https://cdn.jsdelivr.net/npm/liquidjs/dist/liquid.browser.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src-min/ace.js"></script>
|
||||
{% endif %}
|
||||
|
||||
+{{ js('js/liquid.browser.min.js') }}
|
||||
{{ js('js/main') }}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
|
||||
```
|
||||
1. `npm run build:cjs` to build a CommonJS bundle for the perf test.
|
||||
2. `npm run perf:diff` to check whether there's a regression compared against `liquidjs@latest`
|
||||
|
||||
Further more, `benchmark/` contains different cases to check its ops/sec. Useful when debugging perf regressions, to use it:
|
||||
|
||||
- `cd benchmark` go into benchmark project
|
||||
- `npm ci` install dependencies
|
||||
- `npm start` run the cases
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[](https://www.npmjs.org/package/liquidjs)
|
||||
[](https://www.npmjs.org/package/liquidjs)
|
||||
[](https://coveralls.io/github/harttle/liquidjs?branch=master)
|
||||
[](https://github.com/harttle/liquidjs/actions/workflows/check.yml?query=branch%3Amaster)
|
||||
[](https://github.com/harttle/liquidjs/actions/workflows/ci-build.yml?query=branch%3Amaster)
|
||||
[](https://github.com/harttle/liquidjs/blob/master/LICENSE)
|
||||
[](https://github.com/harttle/liquidjs)
|
||||
|
||||
@@ -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 [Github Sponsors](https://github.com/sponsors/harttle), or [LiquidJS on Open Collective][oc]
|
||||
|
||||
<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,26 +50,51 @@ 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 personally love LiquidJS or it's benefiting your business, please consider financially support us:
|
||||
|
||||
### Backers
|
||||
If you personally like LiquidJS and find it useful to you, you can buy me a coffee!
|
||||
- via [Github Sponsors](https://github.com/sponsors/harttle), or
|
||||
- via [LiquidJS on Open Collective][oc]
|
||||
|
||||
<img src="https://opencollective.com/liquidjs/backers.svg?avatarHeight=72">
|
||||
Special thanks to the following sponsors!
|
||||
|
||||
### Sponsors
|
||||
If LiquidJS is benefiting your business/company, please sponsor us to contribute to our effort!
|
||||
|
||||
<img src="https://opencollective.com/liquidjs/sponsors.svg?avatarHeight=72">
|
||||
<!-- FINANCIAL-CONTRIBUTORS-BEGIN -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.opensense.com/"><img src="https://images.opencollective.com/opensense-inc/bf840ae/logo/256.png?height=100" width="100px;" alt="Opensense Inc."/><br /><sub><b>Opensense</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.11ty.dev/"><img src="https://avatars.githubusercontent.com/u/35147177?v=4?s=100" width="100px;" alt="Eleventy"/><br /><sub><b>Eleventy</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://about.me/peterdehaan"><img src="https://avatars2.githubusercontent.com/u/557895?v=4?s=100" width="100px;" alt="Peter deHaan"/><br /><sub><b>Peter deHaan</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://opencollective.com/touchless"><img src="https://images.opencollective.com/touchless/273bc74/logo/256.png?height=100" width="100px;" alt="Touchless"/><br /><sub><b>Touchless</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.dropkiq.com/"><img src="https://images.opencollective.com/1bertlol/43a8ea8/logo/256.png?height=100" width="100px;" alt="Adam Darrah"/><br /><sub><b>Dropkiq</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://dailycontributors.com/"><img src="https://images.opencollective.com/dailycontributors/3c2e057/logo/256.png?height=100&width=100" width="100px;" alt="Dailycontributors"/><br /><sub><b>Dailycontributors</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/coni2k"><img src="https://avatars0.githubusercontent.com/u/1284601?v=4?s=100" width="100px;" alt="coni2k"/><br /><sub><b>Serkan Holat</b></sub></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4?s=100" width="100px;" alt="amit777"/><br /><sub><b>amit777</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://opencollective.com/khaled-salem"><img src="https://images.opencollective.com/khaled-salem/avatar/256.png?height=256" width="100px;" alt="Khaled Salem"/><br /><sub><b>Khaled Salem</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://sentry.io/"><img src="https://avatars.githubusercontent.com/u/1396951?v=4?s=100" width="100px;" alt="Sentry"/><br /><sub><b>Sentry</b></sub></a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.checkoutblocks.com/"><img src="https://avatars.githubusercontent.com/u/114603307?v=4?s=100" width="100px;" alt="Checkout Blocks"/><br /><sub><b>Checkout Blocks</b></sub></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- FINANCIAL-CONTRIBUTORS-END -->
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
@@ -122,7 +147,7 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/IAmTrySound"><img src="https://avatars0.githubusercontent.com/u/5635476?v=4?s=100" width="100px;" alt="Bogdan Chadkin"/><br /><sub><b>Bogdan Chadkin</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=TrySound" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://hightouch.io"><img src="https://avatars0.githubusercontent.com/u/5959235?v=4?s=100" width="100px;" alt="Tejas Manohar"/><br /><sub><b>Tejas Manohar</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=tejasmanohar" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://about.me/peterdehaan"><img src="https://avatars2.githubusercontent.com/u/557895?v=4?s=100" width="100px;" alt="Peter deHaan"/><br /><sub><b>Peter deHaan</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=pdehaan" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4?s=100" width="100px;" alt="amit777"/><br /><sub><b>amit777</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=amit777" title="Code">💻</a> <a href="#financial-amit777" title="Financial">💵</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4?s=100" width="100px;" alt="amit777"/><br /><sub><b>amit777</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=amit777" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.ifi.uzh.ch/en/ce/people/schuldenzucker.html"><img src="https://avatars3.githubusercontent.com/u/1100776?v=4?s=100" width="100px;" alt="Steffen Schuldenzucker"/><br /><sub><b>Steffen Schuldenzucker</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=sschuldenzucker" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -159,6 +184,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="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>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/brunodccarvalho"><img src="https://avatars.githubusercontent.com/u/24962950?v=4?s=100" width="100px;" alt="Bruno Carvalho"/><br /><sub><b>Bruno Carvalho</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=brunodccarvalho" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fupengl"><img src="https://avatars.githubusercontent.com/u/20211964?v=4?s=100" width="100px;" alt="傅鹏"/><br /><sub><b>傅鹏</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=fupengl" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/joel-hamilton"><img src="https://avatars.githubusercontent.com/u/12899024?v=4?s=100" width="100px;" alt="Joel Hamilton"/><br /><sub><b>Joel Hamilton</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=joel-hamilton" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -173,7 +208,5 @@ 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
|
||||
|
||||
+3
-12
@@ -1,17 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run `sed` in a way that's compatible with both macOS (BSD) and Linux (GNU)
|
||||
sedi() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cd docs
|
||||
cp ../CHANGELOG.md source/tutorials/changelog.md
|
||||
sedi \
|
||||
sed -i \
|
||||
-e 's/{%/{% raw %}{%{% endraw %}/g' \
|
||||
-e 's/{{/{% raw %}{{{% endraw %}/g' \
|
||||
-e '1 s/"/\"/g' \
|
||||
@@ -20,7 +11,7 @@ sedi \
|
||||
source/tutorials/changelog.md
|
||||
cp source/tutorials/changelog.md source/zh-cn/tutorials/changelog.md
|
||||
|
||||
sedi -e '1i\
|
||||
sed -i -e '1i\
|
||||
---\ntitle: Changelog\nauto: true\n---\n' source/tutorials/changelog.md
|
||||
sedi -e '1i\
|
||||
sed -i -e '1i\
|
||||
---\ntitle: 更新日志\nauto: true\n---\n' source/zh-cn/tutorials/changelog.md
|
||||
|
||||
@@ -3,17 +3,27 @@
|
||||
# Run `sed` in a way that's compatible with both macOS (BSD) and Linux (GNU)
|
||||
sedi() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
/usr/bin/sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# create docs/themes/navy/layout/partial/all-contributors.swig
|
||||
cp .all-contributorsrc docs/.all-contributorsrc
|
||||
sedi \
|
||||
-e 's/README.md/docs\/themes\/navy\/layout\/partial\/all-contributors.swig/g' \
|
||||
-e 's/"contributorsPerLine": 7/"contributorsPerLine": 65535/g' \
|
||||
docs/.all-contributorsrc
|
||||
|
||||
touch docs/themes/navy/layout/partial/all-contributors.swig
|
||||
all-contributors --config docs/.all-contributorsrc generate
|
||||
sedi 's/<br \/>.*<\/td>/<\/a><\/td>/g' docs/themes/navy/layout/partial/all-contributors.swig
|
||||
|
||||
# create docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
awk '/FINANCIAL-CONTRIBUTORS-BEGIN/{flag=1;next}/FINANCIAL-CONTRIBUTORS-END/{flag=0}flag' README.md | \
|
||||
sed 's/<br \/>.*<\/td>/<\/a><\/td>/g' | \
|
||||
sed 's/width="[^"]*"//g' | \
|
||||
tr -d '\n' | \
|
||||
sed 's/<\/tr>\s*<tr>//g' \
|
||||
> docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BUNDLES=min npm run build
|
||||
mkdir -p docs/public/js/
|
||||
cp dist/liquid.browser.min.js docs/public/js/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
set -ex
|
||||
|
||||
./bin/build-docs-liquid.sh
|
||||
./bin/build-contributors.sh
|
||||
./bin/build-apidoc.sh
|
||||
./bin/build-changelog.sh
|
||||
|
||||
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
-6
@@ -1,16 +1,12 @@
|
||||
#!/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
|
||||
|
||||
# if [ ! -f $FILE_LOCAL ]; then
|
||||
BUNDLES=cjs npm run build:dist
|
||||
# fi
|
||||
|
||||
exec node benchmark/diff.js $FILE_LOCAL $FILE_LATEST
|
||||
|
||||
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
|
||||
|
||||
@@ -8,10 +8,12 @@ tutorials:
|
||||
express: use-in-expressjs.html
|
||||
advanced:
|
||||
caching: caching.html
|
||||
escaping: escaping.html
|
||||
registeration: register-filters-tags.html
|
||||
access_scope_in_filters: access-scope-in-filters.html
|
||||
parse_parameters: parse-parameters.html
|
||||
render_tag_content: render-tag-content.html
|
||||
drops: drops.html
|
||||
sync_and_async: sync-and-async.html
|
||||
whitespace: whitespace-control.html
|
||||
plugins: plugins.html
|
||||
@@ -39,8 +41,12 @@ filters:
|
||||
downcase: downcase.html
|
||||
escape: escape.html
|
||||
escape_once: escape_once.html
|
||||
find: find.html
|
||||
find_exp: find_exp.html
|
||||
first: first.html
|
||||
floor: floor.html
|
||||
group_by: group_by.html
|
||||
group_by_exp: group_by_exp.html
|
||||
join: join.html
|
||||
json: json.html
|
||||
last: last.html
|
||||
@@ -50,6 +56,8 @@ filters:
|
||||
modulo: modulo.html
|
||||
newline_to_br: newline_to_br.html
|
||||
plus: plus.html
|
||||
pop: pop.html
|
||||
push: push.html
|
||||
prepend: prepend.html
|
||||
raw: raw.html
|
||||
remove: remove.html
|
||||
@@ -61,6 +69,7 @@ filters:
|
||||
reverse: reverse.html
|
||||
round: round.html
|
||||
rstrip: rstrip.html
|
||||
shift: shift.html
|
||||
size: size.html
|
||||
slice: slice.html
|
||||
sort: sort.html
|
||||
@@ -69,10 +78,12 @@ 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
|
||||
uniq: uniq.html
|
||||
unshift: unshift.html
|
||||
upcase: upcase.html
|
||||
url_decode: url_decode.html
|
||||
url_encode: url_encode.html
|
||||
|
||||
+14
-17
@@ -3,15 +3,15 @@ title: date
|
||||
---
|
||||
{% since %}v1.9.1{% endsince %}
|
||||
|
||||
# Format
|
||||
* Converts a timestamp into another date format
|
||||
* LiquidJS tries to be conformant with Shopify/Liquid which is using Ruby's core [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime)
|
||||
* Refer [format flags](https://ruby-doc.org/core/strftime_formatting_rdoc.html)
|
||||
* Not all options are supported though - refer [differences here](/tutorials/differences.html#Differences)
|
||||
* The input is firstly converted to `Date` object via [new Date()][jsDate]
|
||||
* Date format can be provided individually as a filter option
|
||||
* If not provided, then `%A, %B %-e, %Y at %-l:%M %P %z` format will be used as default format
|
||||
* Override this using [`dateFormat`](/api/interfaces/LiquidOptions.html#dateFormat) LiquidJS option, to set your preferred default format for all date filters
|
||||
Date filter is used to convert a timestamp into the specified format.
|
||||
|
||||
* LiquidJS tries to conform to Shopify/Liquid, which uses Ruby's core [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime). There're differences with [Ruby's format flags](https://ruby-doc.org/core/strftime_formatting_rdoc.html):
|
||||
* `%Z` (since v10.11.1) works when there's a passed-in timezone name from `LiquidOption` or in-place value (see TimeZone below). If passed-in timezone is an offset number instead of string, it'll behave like `%z`. If there's none passed-in timezone, it returns [the runtime's default time zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#timezone).
|
||||
* LiquidJS provides an additional `%q` flag for date ordinals. e.g. `{{ '2023/02/02' | date: '%d%q of %b'}}` => `02nd of Feb`
|
||||
* Date literals are firstly converted to `Date` object via [new Date()][jsDate], that means literal values are considered in runtime's time zone by default.
|
||||
* The format filter argument is optional:
|
||||
* If not provided, it defaults to `%A, %B %-e, %Y at %-l:%M %P %z`.
|
||||
* The above default can be overridden by [`dateFormat`](/api/interfaces/LiquidOptions.html#dateFormat) LiquidJS option.
|
||||
|
||||
### Examples
|
||||
```liquid
|
||||
@@ -23,16 +23,14 @@ title: date
|
||||
```
|
||||
|
||||
# TimeZone
|
||||
* By default, dates will be converted to local timezone before output
|
||||
* You can override that by,
|
||||
* setting a timezone for each individual `date` filter via the second parameter
|
||||
* using the [`timezoneOffset`](/api/interfaces/LiquidOptions.html#timezoneOffset) LiquidJS option
|
||||
* Its default value is your local timezone offset which can be obtained by `new Date().getTimezoneOffset()`
|
||||
* During output, LiquidJS uses local timezone which can override by:
|
||||
* setting a timezone in-place when calling `date` filter, or
|
||||
* setting the [`timezoneOffset`](/api/interfaces/LiquidOptions.html#timezoneOffset) LiquidJS option
|
||||
* It defaults to runtime's time one.
|
||||
* Offset can be set as,
|
||||
* minutes: `-360` means `'+06:00'` and `360` means `'-06:00'`
|
||||
* timeZone ID: `Asia/Colombo` or `America/New_York`
|
||||
* Use minutes for better performance with repeated processing of templates with many dates like, converting template for each email recipient
|
||||
* Refer [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for TZ database values
|
||||
* See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for TZ database values
|
||||
|
||||
### Examples
|
||||
```liquid
|
||||
@@ -41,7 +39,6 @@ title: date
|
||||
{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }} => 1991-01-01T04:30:00
|
||||
```
|
||||
|
||||
|
||||
# Input
|
||||
* `date` works on strings if they contain well-formatted dates
|
||||
* Note that LiquidJS is using [JavaScript Date][jsDate] to parse the input string, that means [IETF-compliant RFC 2822 timestamps](https://datatracker.ietf.org/doc/html/rfc2822#page-14) and strings in [a version of ISO8601](https://www.ecma-international.org/ecma-262/11.0/#sec-date.parse) are supported.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: find
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Return the first object in an array for which the queried attribute has the given value or return `nil` if no item in the array satisfies the given criteria. For the following `members` array:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2014, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ members | find: "graduation_year", 2014 | json }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
{"graduation_year":2014,"name":"John"}
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: find_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Return the first object in an array for which the given expression evaluates to true or return `nil` if no item in the array satisfies the evaluated expression.
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2014, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
{"graduation_year":2014,"name":"John"}
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: group_by
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Group an array's items by a given property. For `members` array:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2003, name: 'Jay' },
|
||||
{ graduation_year: 2003, name: 'John' },
|
||||
{ graduation_year: 2004, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ members | group_by: "graduation_year" | json: 2 }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": 2003,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": 2004,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2004,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: group_by_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Group an array's items using a Liquid expression. For `members` array below:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2009, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": "201",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2013,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2014,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "200",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2009,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -16,3 +16,24 @@ Output
|
||||
```text
|
||||
["foo","bar","coo"]
|
||||
```
|
||||
|
||||
## Space
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
An additional `space` parameter can be specified to format the JSON.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign arr = "foo bar coo" | split: " " %}
|
||||
{{ arr | json: 4 }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
[
|
||||
"foo",
|
||||
"bar",
|
||||
"coo"
|
||||
]
|
||||
```
|
||||
|
||||
@@ -12,7 +12,7 @@ Categories | Filters
|
||||
Math | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
|
||||
String | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last,remove, remove_first, remove_last, truncate, truncatewords
|
||||
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br
|
||||
Array | slice, map, sort, sort_natural, uniq, where, first, last, join, reverse, concat, compact, size
|
||||
Array | slice, map, sort, sort_natural, uniq, where, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
|
||||
Date | date
|
||||
Misc | default, json, raw
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: pop
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Pop an element from the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | pop %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: push
|
||||
---
|
||||
|
||||
{% since %}v10.8.0{% endsince %}
|
||||
|
||||
Push an element into array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | push: "peaches" %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: shift
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Shift an element from the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | shift %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -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.
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: unshift
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
Unshift an element to the front of the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign fruits = "oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | unshift: "apples" %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -79,4 +79,28 @@ Output
|
||||
Featured product: Hawaiian print sweater vest
|
||||
```
|
||||
|
||||
Additionally, `property` can be any valid Liquid variable expression as used in output syntax, except that the scope of this expression is within each item. For the following `products` array:
|
||||
|
||||
```javascript
|
||||
const products = [
|
||||
{ meta: { details: { class: 'A' } }, order: 1 },
|
||||
{ meta: { details: { class: 'B' } }, order: 2 },
|
||||
{ meta: { details: { class: 'B' } }, order: 3 }
|
||||
]
|
||||
```
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign selected = products | where: 'meta.details["class"]', "B" %}
|
||||
{% for item in selected -%}
|
||||
- {{ item.order }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Ouput
|
||||
```text
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
|
||||
[truthy]: ../tutorials/truthy-and-falsy.html
|
||||
|
||||
@@ -20,17 +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-).
|
||||
|
||||
### Backers
|
||||
If you personally like LiquidJS and find it's useful to you, you can become a backer!
|
||||
|
||||
<object type="image/svg+xml" data="https://opencollective.com/liquidjs/backers.svg?avatarHeight=72"></object>
|
||||
|
||||
### Sponsors
|
||||
If LiquidJS is benefiting your business/company, please sponsor us to make it better!
|
||||
|
||||
<object type="image/svg+xml" data="https://opencollective.com/liquidjs/sponsors.svg?avatarHeight=72"></object>
|
||||
LiquidJS is Open Source and Free. To help it live and thrive, especially when LiquidJS is benefiting your business, please consider contribute on [Github Sponsors](https://github.com/sponsors/harttle) or [Open Collective][oc]. If I'm missing anything, find me via Twitter (harttleharttle) or email (harttleharttle at gmail), to add you into [the contributors table](https://github.com/harttle/liquidjs#contributors-).
|
||||
|
||||
[oc]: https://opencollective.com/liquidjs/contribute/backer-10665/checkout
|
||||
[shopify/liquid]: https://shopify.github.io/liquid/
|
||||
|
||||
@@ -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.
|
||||
@@ -16,7 +16,7 @@ In the meantime, it's now implemented in JavaScript, that means it has to be mor
|
||||
* **Async as first-class citizen**. Filters and tags can be implemented asynchronously by return a `Promise`.
|
||||
* **Also can be sync**. For scenarios that are not I/O intensive, render synchronously can be much faster. You can call synchronous APIs like `.renderSync()` as long as all the filters and tags in template support to be rendered synchronously. All builtin filters/tags support both sync and async render.
|
||||
* **[Abstract file system][afs]**. Along with async feature, LiquidJS can be used to serve templates stored in Databases [#414][#414], on remote HTTP server [#485][#485], and so on.
|
||||
* **Additional tags and filters** like `layout` and `json`.
|
||||
* **Additional tags and filters** like `layout` and `json`, see below for details.
|
||||
|
||||
## Differences
|
||||
|
||||
@@ -29,11 +29,16 @@ Though we're trying to be compatible with the Ruby version, there are still some
|
||||
* Iteration order for objects. The iteration order of JavaScript objects, and thus LiquidJS objects, is a combination of the insertion order for string keys, and ascending order for number-like keys, while the iteration order of Ruby Hash is simply the insertion order.
|
||||
* Sort stability. The [sort][sort] stability is also not defined in both shopify/liquid and LiquidJS, but it's [considered stable][stable-sort] for LiquidJS in Node.js 12+ and Google Chrome 70+.
|
||||
* Trailing unmatched characters inside filters are allowed in shopify/liquid but not in LiquidJS. It means filter arguments without a colon like `{%raw%}{{ "a b" | split " "}}{%endraw%}` will throw an error in LiquidJS. This is intended to improve Liquid usability, see [#208][#208] and [#212][#212].
|
||||
* LiquidJS has additional tags: [layout][layout] and corresponding `block` tag.
|
||||
* LiquidJS [date][date] filter supports `%q` for date ordinals like `{{ '2023/02/02' | date: '%d%q of %b'}}` => `02nd of Feb`
|
||||
* LiquidJS has more tags/filters than [the Liquid language][liquid]:
|
||||
* LiquidJS-defined tags: [layout][layout], [render][render] and corresponding `block` tag.
|
||||
* LiquidJS-defined filters: [json][json].
|
||||
* Tags/filters that don't depend on Shopify platform are borrowed from [Shopify][shopify-tags].
|
||||
* Tags/filters that don't depend on Jekyll framework are borrowed from [Jekyll][jekyll-filters].
|
||||
* Some tags/filters behave differently: [date][date] filter.
|
||||
|
||||
[date]: https://liquidjs.com/filters/date.html
|
||||
[layout]: https://liquidjs.com/tags/layout.html
|
||||
[layout]: ../tags/layout.html
|
||||
[render]: ../tags/render.html
|
||||
[json]: https://liquidjs.com/filters/json.html
|
||||
[#26]: https://github.com/harttle/liquidjs/pull/26
|
||||
[#59]: https://github.com/harttle/liquidjs/issues/59
|
||||
@@ -47,3 +52,6 @@ Though we're trying to be compatible with the Ruby version, there are still some
|
||||
[plugins]: ./plugins.html#Plugin-List
|
||||
[ruby-liquid]: https://github.com/Shopify/liquid
|
||||
[afs]: https://liquidjs.com/tutorials/render-file.html#Abstract-File-System
|
||||
[liquid]: https://shopify.github.io/liquid/basics/introduction/
|
||||
[shopify-tags]: https://shopify.dev/docs/api/liquid/tags
|
||||
[jekyll-filters]: https://jekyllrb.com/docs/liquid/filters/
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Escaping
|
||||
---
|
||||
|
||||
Escaping is important in all languages, including LiquidJS. While escaping has 2 different meanings for a template engine:
|
||||
|
||||
1. Escaping for the output, i.e. HTML escape. Used to escape HTML special characters so the output will not break HTML structures, aka HTML safe.
|
||||
2. Escaping for the language itself, i.e. Liquid escape. Used to output strings that's considered special in Liquid language. This will be useful when you're writing an article in Liquid template to introduce Liquid language.
|
||||
|
||||
## HTML Escape
|
||||
|
||||
By default output is not escaped. While you can use [escape][escape] filter for this:
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "1 < 2" | escape }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
1 < 2
|
||||
```
|
||||
|
||||
There's also [escape_once][escape_once], [newline_to_br][newline_to_br], [strip_html][strip_html] filters for you to fine tune your output.
|
||||
|
||||
In cases where variables are mostly not trusted, [outputEscape][outputEscape] can be set to `"escape"` to apply escape by default. In this case, when you need some output not to be escaped, [raw][raw] filter can be used:
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "1 < 2" }}
|
||||
{{ "<button>OK</button>" | raw }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
1 < 2
|
||||
<button>OK</button>
|
||||
```
|
||||
|
||||
## Liquid Escape
|
||||
|
||||
To disable Liquid language and output strings like `{{` and `{%`, the [raw][raw] tag can be used.
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% raw %}
|
||||
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
|
||||
{{{ that }}} will not.
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
|
||||
{{{ that }}} will not.
|
||||
```
|
||||
|
||||
Within strings literals in LiquidJS template, `\` can be used to escape special characters in string syntax. For example:
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{{ "\"" }}
|
||||
```
|
||||
|
||||
Output
|
||||
```liquid
|
||||
"
|
||||
```
|
||||
|
||||
[outputEscape]: ./options.html#outputEscape
|
||||
[escape]: ../filters/escape.html
|
||||
[raw]: ../filters/raw.html
|
||||
[escape_once]: ../filters/escape.html
|
||||
[strip_html]: ../filters/strip_html.html
|
||||
[newline_to_br]: ../filters/newline_to_br.html
|
||||
[raw]: ../tags/raw.html
|
||||
@@ -102,6 +102,14 @@ Before 2.0.1, <code>extname</code> is set to `.liquid` by default. To change tha
|
||||
|
||||
it defaults to false. For example, when set to true, a blank string would evaluate to false with jsTruthy. With Shopify's truthiness, a blank string is true.
|
||||
|
||||
## outputEscape
|
||||
|
||||
[outputEscape][outputEscape] can be used to automatically escape output strings. It can be one of `"escape"`, `"json"`, or `(val: unknown) => string`, defaults to `undefined`.
|
||||
|
||||
- For untrusted output variables, set `outputEscape: "escape"` makes them be HTML escaped by default. You'll need [raw][raw] filter for direct output.
|
||||
- `"json"` is useful when you're using LiquidJS to create valid JSON files.
|
||||
- It can even be a function which allows you to control what variables are output throughout LiquidJS. Please note the input can be any type other than string, e.g. an filter returned an non-string value.
|
||||
|
||||
## Date
|
||||
|
||||
**timezoneOffset** is used to specify a different timezone to output dates, your local timezone will be used if not specified. For example, set `timezoneOffset: 0` to output all dates in UTC/GMT 00:00.
|
||||
@@ -151,3 +159,5 @@ Parameter orders are ignored by default, for ea `{% for i in (1..8) reversed lim
|
||||
[wc]: ./whitespace-control.html
|
||||
[intro]: ./intro-to-liquid.html
|
||||
[jekyllInclude]: /api/interfaces/LiquidOptions.html#jekyllInclude
|
||||
[raw]: ../filters/raw.html
|
||||
[outputEscape]: /api/interfaces/LiquidOptions.html#outputEscape
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,14 @@ title: date
|
||||
|
||||
把时间戳转换为字符串。LiquidJS 尝试跟 Shopify/Liquid 保持一致,它用的是 Ruby 核心的 [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime)。此外 LiquidJS 会先通过 [new Date()][newDate] 尝试把输入转换为 Date 对象。
|
||||
|
||||
但 LiquidJS 支持的格式与 [Ruby 的 flag](https://ruby-doc.org/core/strftime_formatting_rdoc.html) 有些不同:
|
||||
* `%Z`(自 v10.11.1 起支持)只有在传入了时区时才起作用(可以通过 `LiquidOption` 传入,也可以在创建日期时单独传入,见下文)。如果传入的时区是个数字,那么它的表现将会与 `%z` 相同。如果没有传入时区,将会返回 [运行时默认时区](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#timezone)。
|
||||
* LiquidJS 提供额外的 `%q` 用来处理序数:`{{ '2023/02/02' | date: '%d%q of %b'}}` => `02nd of Feb`
|
||||
* 日期字面量会通过 [new Date()][jsDate] 转化为 `Date` 对象,这意味着字面量默认使用运行时默认时区。
|
||||
* 格式字参数是可选的:
|
||||
* 如果不传,默认为 `%A, %B %-e, %Y at %-l:%M %P %z`。
|
||||
* 上述默认值可以通过 [`dateFormat`](/api/interfaces/LiquidOptions.html#dateFormat) 参数覆盖。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ article.published_at | date: "%a, %b %d, %y" }}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: find
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
在数组中找到给定的属性为给定的值的第一个元素并返回;如果没有这样的元素则返回 `nil`。对于 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2014, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | find: "graduation_year", 2014 | json }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
{"graduation_year":2014,"name":"John"}
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: find_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
找到数组中给定的表达式值为 `true` 的第一个元素,如果没有这样的元素则返回 `nil`。对于下面的 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2014, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | find_exp: "item", "item.graduation_year == 2014" | json }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
{"graduation_year":2014,"name":"John"}
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: group_by
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
把数组元素按照给定的属性的值分组。对于 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2003, name: 'Jay' },
|
||||
{ graduation_year: 2003, name: 'John' },
|
||||
{ graduation_year: 2004, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | group_by: "graduation_year" | json: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": 2003,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2003,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": 2004,
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2004,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: group_by_exp
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
把数组元素按照给定的 Liquid 表达式的值分组。对于 `members` 数组:
|
||||
|
||||
```javascript
|
||||
const members = [
|
||||
{ graduation_year: 2013, name: 'Jay' },
|
||||
{ graduation_year: 2014, name: 'John' },
|
||||
{ graduation_year: 2009, name: 'Jack' }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" | json: 2 }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
[
|
||||
{
|
||||
"name": "201",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2013,
|
||||
"name": "Jay"
|
||||
},
|
||||
{
|
||||
"graduation_year": 2014,
|
||||
"name": "John"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "200",
|
||||
"items": [
|
||||
{
|
||||
"graduation_year": 2009,
|
||||
"name": "Jack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -16,3 +16,24 @@ title: json
|
||||
```text
|
||||
["foo","bar","coo"]
|
||||
```
|
||||
|
||||
## 格式化
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
可以指定一个 `space` 参数来格式化 JSON。
|
||||
|
||||
Input
|
||||
```liquid
|
||||
{% assign arr = "foo bar coo" | split: " " %}
|
||||
{{ arr | json: 4 }}
|
||||
```
|
||||
|
||||
Output
|
||||
```text
|
||||
[
|
||||
"foo",
|
||||
"bar",
|
||||
"coo"
|
||||
]
|
||||
```
|
||||
|
||||
@@ -12,7 +12,7 @@ LiquidJS 共支持 40+ 个过滤器,可以分为如下几类:
|
||||
数学 | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
|
||||
字符串 | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last, remove, remove_first, remove_last, truncate, truncatewords
|
||||
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br
|
||||
数组 | slice, map, sort, sort_natural, uniq, wheres, first, last, join, reverse, concat, compact, size
|
||||
数组 | slice, map, sort, sort_natural, uniq, where, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
|
||||
日期 | date
|
||||
其他 | default, json
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: pop
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
从数组末尾弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | pop %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: push
|
||||
---
|
||||
|
||||
{% since %}v10.8.0{% endsince %}
|
||||
|
||||
在数组中添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | push: "peaches" %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: shift
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
从数组头部弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | shift %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: unshift
|
||||
---
|
||||
|
||||
{% since %}v10.11.0{% endsince %}
|
||||
|
||||
往数组头部添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign fruits = "oranges, peaches" | split: ", " %}
|
||||
|
||||
{% assign everything = fruits | unshift: "apples" %}
|
||||
|
||||
{% for item in everything %}
|
||||
- {{ item }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- apples
|
||||
- oranges
|
||||
- peaches
|
||||
```
|
||||
@@ -79,4 +79,29 @@ Featured product: {{ new_shirt.title }}
|
||||
Featured product: Hawaiian print sweater vest
|
||||
```
|
||||
|
||||
此外 `property` 可以是任意合法的变量表达式,就像在**输出**结构中一样,只是它的上下文是数组的每一个元素。对于下面的 `products` 数组:
|
||||
|
||||
```javascript
|
||||
const products = [
|
||||
{ meta: { details: { class: 'A' } }, order: 1 },
|
||||
{ meta: { details: { class: 'B' } }, order: 2 },
|
||||
{ meta: { details: { class: 'B' } }, order: 3 }
|
||||
]
|
||||
```
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% assign selected = products | where: 'meta.details["class"]', "B" %}
|
||||
{% for item in selected -%}
|
||||
- {{ item.order }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
|
||||
|
||||
[truthy]: ../tutorials/truthy-and-falsy.html
|
||||
|
||||
@@ -20,17 +20,7 @@ Star 是支持 LiquidJS 最重要的方式,也是最简单的方式:通过
|
||||
|
||||
## 资金支持
|
||||
|
||||
LiquidJS 是开源和免费的,但支持 [Open Collective][oc] 赞助,作为感谢你的名字和头像(或 Logo)会展示在这里,[LiquidJS 首页](https://liquidjs.com/)和 [Github README][liquidjs]。
|
||||
|
||||
### 支持者
|
||||
如果你喜欢 LiquidJS,希望支持我们,你可以成为 Open Collective Backer!
|
||||
|
||||
<object type="image/svg+xml" data="https://opencollective.com/liquidjs/backers.svg?avatarHeight=72"></object>
|
||||
|
||||
### 赞助商
|
||||
如果你的公司/业务在使用 LiquidJS,请考虑赞助我们!让 LiquidJS 变得更好,以及保持开放和免费!
|
||||
|
||||
<object type="image/svg+xml" data="https://opencollective.com/liquidjs/sponsors.svg?avatarHeight=72"></object>
|
||||
LiquidJS 是开源和免费的,但支持 [Open Collective][oc] 和 [Github Sponsors](https://github.com/sponsors/harttle) 赞助,请通过 Twitter (harttleharttle) 或邮件 (harttleharttle at gmail) 联系我,把您加到 [贡献者列表](https://github.com/harttle/liquidjs#contributors-) 中。
|
||||
|
||||
[oc]: https://opencollective.com/liquidjs/
|
||||
[shopify/liquid]: https://shopify.github.io/liquid/
|
||||
|
||||
@@ -29,10 +29,15 @@ LiquidJS 一直很重视兼容于 Ruby 版本的 Liquid。Liquid 模板语言最
|
||||
* 对象的迭代顺序。JavaScript 对象的迭代顺序是插入顺序和数字键递增顺序的组合,但 Ruby Hash 中只是插入顺序(JavaScript 字面量 Object 和 Ruby 字面量 Hash 的插入顺序解释也不同)。
|
||||
* 排序稳定性。shopify/liquid 和 LiquidJS 都没有定义 [sort][sort] 过滤器的稳定性在,它取决于 Ruby/JavaScript 内置的排序算法,在 Node.js 12+ 和 Google Chrome 70+ LiquidJS 的排序是 [稳定的][stable-sort]。
|
||||
* shopify/liquid 允许过滤器尾部的未匹配字符,但 LiquidJS 不允许。这就是说如果过滤器参数前忘记写冒号比如 `{%raw%}{{ "a b" | split " "}}{%endraw%}` LiquidJS 会抛出异常。这是为了提升 Liquid 模板的易用性,参考 [#208][#208] 和 [#212][#212]。
|
||||
* LiquidJS 有额外的标签:[layout][layout] 和相应的 `block`。
|
||||
* LiquidJS 有额外的过滤器:[json][json]。
|
||||
* LiquidJS 比 [Liquid 语言][liquid] 有更多的标签和过滤器:
|
||||
* LiquidJS 自己定义的标签:[layout][layout]、[render][render] 和相应的 `block`。
|
||||
* LiquidJS 自己定义的过滤器:[json][json]。
|
||||
* 从 [Shopify][shopify-tags] 借来的不依赖 Shopify 平台的标签/过滤器。
|
||||
* 从 [Jekyll][jekyll-filters] 借来的不依赖 Jekyll 框架的标签/过滤器。
|
||||
* 有些过滤器和标签表现不同:比如 [date][date]。
|
||||
|
||||
[layout]: https://liquidjs.com/tags/layout.html
|
||||
[layout]: ../tags/layout.html
|
||||
[render]: ../tags/render.html
|
||||
[json]: https://liquidjs.com/filters/json.html
|
||||
[#26]: https://github.com/harttle/liquidjs/pull/26
|
||||
[#59]: https://github.com/harttle/liquidjs/issues/59
|
||||
@@ -46,3 +51,6 @@ LiquidJS 一直很重视兼容于 Ruby 版本的 Liquid。Liquid 模板语言最
|
||||
[plugins]: ./plugins.html#插件列表
|
||||
[ruby-liquid]: https://github.com/Shopify/liquid
|
||||
[afs]: https://liquidjs.com/tutorials/render-file.html#Abstract-File-System
|
||||
[liquid]: https://shopify.github.io/liquid/basics/introduction/
|
||||
[shopify-tags]: https://shopify.dev/docs/api/liquid/tags
|
||||
[jekyll-filters]: https://jekyllrb.com/docs/liquid/filters/
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: 转义
|
||||
---
|
||||
|
||||
LiquidJS 种转义有两种含义:
|
||||
|
||||
1. 输出语言的转义,即 HTML 转义。用来让输出的变量不包含 HTML 特殊字符,不影响 HTML 的结构,也就是输出 HTML 安全的字符串。
|
||||
2. 语言自己的转义,即 Liquid 转义。用来输出包含对于 Liquid 语言来说是特殊字符的字符串,比如你在使用 Liquid 模板语言来编写一篇介绍 Liquid 语法的文章时就会需要 Liquid 转义。
|
||||
|
||||
## HTML 转义
|
||||
|
||||
默认情况下输出是不转义的,但你可以用 [escape][escape] 过滤器来做 HTML 转义:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "1 < 2" | escape }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1 < 2
|
||||
```
|
||||
|
||||
LiquidJS 也提供了其他过滤器来支持不同的转义需求:[escape_once][escape_once], [newline_to_br][newline_to_br], [strip_html][strip_html]。
|
||||
|
||||
当输出的变量不被信任时,可以把 [outputEscape][outputEscape] 参数设置为 `"escape"` 来启用默认 HTML 转义。这种情况下,如果你需要某个输出不被转义,则需要使用 [raw][raw] 过滤器:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "1 < 2" }}
|
||||
{{ "<button>OK</button>" | raw }}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
1 < 2
|
||||
<button>OK</button>
|
||||
```
|
||||
|
||||
## Liquid 转义
|
||||
|
||||
为了输出 Liquid 的特殊字符比如 `{{` 和 `{%`,你需要 [raw][raw] 标签。
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{% raw %}
|
||||
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
|
||||
{{{ that }}} will not.
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
输出
|
||||
```text
|
||||
In LiquidJS, {{ this | escape }} will be HTML-escaped, but
|
||||
{{{ that }}} will not.
|
||||
```
|
||||
|
||||
Within strings literals in LiquidJS template, `\` can be used to escape special characters in string syntax. For example:
|
||||
|
||||
输入
|
||||
```liquid
|
||||
{{ "\"" }}
|
||||
```
|
||||
|
||||
输出
|
||||
```liquid
|
||||
"
|
||||
```
|
||||
|
||||
[outputEscape]: ./options.html#outputEscape
|
||||
[escape]: ../filters/escape.html
|
||||
[raw]: ../filters/raw.html
|
||||
[escape_once]: ../filters/escape.html
|
||||
[strip_html]: ../filters/strip_html.html
|
||||
[newline_to_br]: ../filters/newline_to_br.html
|
||||
[raw]: ../tags/raw.html
|
||||
@@ -100,6 +100,14 @@ LiquidJS 把这个选项默认值设为 <code>true</code> 以兼容于 shopify/l
|
||||
|
||||
例如,空字符串在 JavaScript 中为假(`jsTruthy` 为 `true` 时),在 Shopify 真值表中为真。
|
||||
|
||||
## outputEscape
|
||||
|
||||
[outputEscape][outputEscape] 用来自动转义输出。它的值可以是 `"escape"`、`"json"` 或 `(val: unknown) => string`,默认为 `undefined`。
|
||||
|
||||
- 如果被输出的变量不被信任,可以设置 `outputEscape: "escape"` 来自动把它们 HTML 转义。如果要直接输出则需要使用 [raw][raw] 过滤器。
|
||||
- 如果你在用 LiquidJS 来生产 JSON 文件,可以设置为 `"json"`。
|
||||
- `outputEscape` 甚至可以是函数,你可以借此控制整个 LiquidJS 的变量输出。注意函数的输入不一定是字符串,因为过滤器的返回值可以不是字符串,你的函数将会接到这个值。
|
||||
|
||||
## 时间日期和时区
|
||||
|
||||
**timezoneOffset** 用来指定一个和你当地时区不同的时区,所有日期和时间输出时都转换到这个指定的时区。例如设置 `timezoneOffset: 0` 将会把所有日期按照 UTC/GMT 00:00 来输出。
|
||||
@@ -147,3 +155,5 @@ LiquidJS 把这个选项默认值设为 <code>true</code> 以兼容于 shopify/l
|
||||
[wc]: ./whitespace-control.html
|
||||
[intro]: ./intro-to-liquid.html
|
||||
[jekyllInclude]: /api/interfaces/LiquidOptions.html#jekyllInclude
|
||||
[raw]: ../filters/raw.html
|
||||
[outputEscape]: /api/interfaces/LiquidOptions.html#outputEscape
|
||||
|
||||
Vendored
+3
-4
@@ -11,12 +11,9 @@ index:
|
||||
contributors:
|
||||
title: Contributors
|
||||
description: 'LiquidJS follows the <a href="https://github.com/all-contributors/all-contributors">all-contributors</a> specification, see <a href="tutorials/contribution-guidelines.html">guildelines here</a>! Thanks goes to these wonderful people:'
|
||||
backers:
|
||||
title: Backers
|
||||
description: 'Love LiquidJS and want to contribute? <a href="https://opencollective.com/liquidjs">Become a backer</a>. Find out more <a href="tutorials/contribution-guidelines.html#Financial-Support">how to contribute</a>!'
|
||||
sponsors:
|
||||
title: Sponsors
|
||||
description: 'If LiquidJS is benefiting your business/company, please <a href="https://opencollective.com/liquidjs">sponsor us</a> to make it better!'
|
||||
description: 'If you personally love LiquidJS or it's benefiting your business, please <a href="https://github.com/sponsors/harttle">sponsor us</a>!'
|
||||
|
||||
playground:
|
||||
title: Playground
|
||||
@@ -43,10 +40,12 @@ sidebar:
|
||||
|
||||
advanced: Advanced
|
||||
caching: Caching
|
||||
escaping: Escaping
|
||||
registeration: Register Filters/Tags
|
||||
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
+2
@@ -40,10 +40,12 @@ sidebar:
|
||||
|
||||
advanced: 高级主题
|
||||
caching: 缓存
|
||||
escaping: 转义
|
||||
registeration: 注册标签/过滤器
|
||||
access_scope_in_filters: 过滤器里访问上下文
|
||||
parse_parameters: 参数解析
|
||||
render_tag_content: 渲染标签内容
|
||||
drops: Liquid Drop
|
||||
sync_and_async: 同步和异步
|
||||
whitespace: 换行和缩进
|
||||
plugins: 插件
|
||||
|
||||
Vendored
+4
-11
@@ -37,27 +37,20 @@
|
||||
<div class="inner">
|
||||
<h3>{{__('index.contributors.title')}}</h3>
|
||||
<p class="description">{{__('index.contributors.description')}}</p>
|
||||
<div class="all-contributors">
|
||||
<div class="contributors">
|
||||
{{ partial('partial/all-contributors') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="backers-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<h3>{{__('index.backers.title')}}</h3>
|
||||
<p class="description">{{__('index.backers.description')}}</p>
|
||||
<img src="https://opencollective.com/liquidjs/individuals.svg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sponsors-wrap">
|
||||
<div class="wrapper">
|
||||
<div class="inner">
|
||||
<h3>{{__('index.sponsors.title')}}</h3>
|
||||
<p class="description">{{__('index.sponsors.description')}}</p>
|
||||
<img class="open-collective" src="https://opencollective.com/liquidjs/organizations.svg">
|
||||
<div class="contributors">
|
||||
{{ partial('partial/financial-contributors') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{% if page.layout === 'playground' %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/liquidjs@latest/dist/liquid.browser.min.js"></script>
|
||||
{{ js('js/liquid.browser.min.js') }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src-min/ace.js"></script>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="0%"><a href="https://harttle.land"><img src="https://avatars3.githubusercontent.com/u/4427974?v=4?s=100" width="100px;" alt="Jun Yang"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/chenos"><img src="https://avatars0.githubusercontent.com/u/2993310?v=4?s=100" width="100px;" alt="chenos"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://zachleat.com/"><img src="https://avatars2.githubusercontent.com/u/39355?v=4?s=100" width="100px;" alt="Zach Leatherman"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/thardy"><img src="https://avatars3.githubusercontent.com/u/120636?v=4?s=100" width="100px;" alt="Tim Hardy"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://paulrobertlloyd.com/"><img src="https://avatars3.githubusercontent.com/u/813383?v=4?s=100" width="100px;" alt="Paul Robert Lloyd"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://twitter.com/alecdotbiz"><img src="https://avatars2.githubusercontent.com/u/1925840?v=4?s=100" width="100px;" alt="Alec Larson"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/pmalouin"><img src="https://avatars1.githubusercontent.com/u/1411117?v=4?s=100" width="100px;" alt="Patrick Malouin"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://jaswrks.com"><img src="https://avatars3.githubusercontent.com/u/1563559?v=4?s=100" width="100px;" alt="jaswrks"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://oott123.com"><img src="https://avatars2.githubusercontent.com/u/905663?v=4?s=100" width="100px;" alt="三三"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/ssendev"><img src="https://avatars0.githubusercontent.com/u/450793?v=4?s=100" width="100px;" alt="ssendev"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/wojtask9"><img src="https://avatars3.githubusercontent.com/u/6099236?v=4?s=100" width="100px;" alt="wojtask9"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/thelornenelson"><img src="https://avatars3.githubusercontent.com/u/24596583?v=4?s=100" width="100px;" alt="Andrew Barclay"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.stam.pr/"><img src="https://avatars2.githubusercontent.com/u/142338?v=4?s=100" width="100px;" alt="Cory Mawhorter"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/thehappybug"><img src="https://avatars0.githubusercontent.com/u/3393530?v=4?s=100" width="100px;" alt="Mehdi Jaffery"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/robinbijlani"><img src="https://avatars0.githubusercontent.com/u/2503108?v=4?s=100" width="100px;" alt="Robin Bijlani"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.rmkennedy.com"><img src="https://avatars3.githubusercontent.com/u/8356669?v=4?s=100" width="100px;" alt="Ryan Kennedy"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/strax"><img src="https://avatars2.githubusercontent.com/u/587213?v=4?s=100" width="100px;" alt="Sami Kukkonen"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://ScottFreeCode.github.io/"><img src="https://avatars3.githubusercontent.com/u/16506071?v=4?s=100" width="100px;" alt="Scott Santucci"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://stevenrescigno.com"><img src="https://avatars3.githubusercontent.com/u/8505293?v=4?s=100" width="100px;" alt="Steven "/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://efcl.info/"><img src="https://avatars1.githubusercontent.com/u/19714?v=4?s=100" width="100px;" alt="azu"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/wyozi"><img src="https://avatars3.githubusercontent.com/u/4894573?v=4?s=100" width="100px;" alt="Joonas"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/jamelait"><img src="https://avatars1.githubusercontent.com/u/14369255?v=4?s=100" width="100px;" alt="Jamel A."/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://brandonpittman.net"><img src="https://avatars0.githubusercontent.com/u/967145?v=4?s=100" width="100px;" alt="Brandon Pittman"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/tgrandgent"><img src="https://avatars3.githubusercontent.com/u/17069042?v=4?s=100" width="100px;" alt="tgrandgent"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/mastodon0"><img src="https://avatars1.githubusercontent.com/u/7924332?v=4?s=100" width="100px;" alt="Martin Schuster"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://js.chenlei.me"><img src="https://avatars0.githubusercontent.com/u/6339390?v=4?s=100" width="100px;" alt="Ray"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/CriGoT"><img src="https://avatars0.githubusercontent.com/u/1936786?v=4?s=100" width="100px;" alt="Cristofer Gonzales"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.raymondcamden.com"><img src="https://avatars3.githubusercontent.com/u/393660?v=4?s=100" width="100px;" alt="Raymond Camden"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://stedman.dev"><img src="https://avatars1.githubusercontent.com/u/183122?v=4?s=100" width="100px;" alt="Steve Stedman"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://ciccarello.me"><img src="https://avatars0.githubusercontent.com/u/11273838?v=4?s=100" width="100px;" alt="Anthony Ciccarello"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://twitter.com/IAmTrySound"><img src="https://avatars0.githubusercontent.com/u/5635476?v=4?s=100" width="100px;" alt="Bogdan Chadkin"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://hightouch.io"><img src="https://avatars0.githubusercontent.com/u/5959235?v=4?s=100" width="100px;" alt="Tejas Manohar"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://about.me/peterdehaan"><img src="https://avatars2.githubusercontent.com/u/557895?v=4?s=100" width="100px;" alt="Peter deHaan"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4?s=100" width="100px;" alt="amit777"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://www.ifi.uzh.ch/en/ce/people/schuldenzucker.html"><img src="https://avatars3.githubusercontent.com/u/1100776?v=4?s=100" width="100px;" alt="Steffen Schuldenzucker"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/Pixcell"><img src="https://avatars0.githubusercontent.com/u/4005291?v=4?s=100" width="100px;" alt="Pixcell"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://jasonet.co"><img src="https://avatars.githubusercontent.com/u/10660468?v=4?s=100" width="100px;" alt="Jason Etcovitch"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/kayuapi"><img src="https://avatars.githubusercontent.com/u/10304328?v=4?s=100" width="100px;" alt="ZC"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://memmie.lenglet.name"><img src="https://avatars.githubusercontent.com/u/729275?v=4?s=100" width="100px;" alt="Memmie Lenglet"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/ilhamdev0"><img src="https://avatars.githubusercontent.com/u/57636145?v=4?s=100" width="100px;" alt="ilhamdev0"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/c412216887"><img src="https://avatars.githubusercontent.com/u/29691650?v=4?s=100" width="100px;" alt="一饮一啄皆是人生"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://digitalinspiration.com/"><img src="https://avatars.githubusercontent.com/u/1344071?v=4?s=100" width="100px;" alt="Amit Agarwal"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://n1ru4l.cloud/"><img src="https://avatars.githubusercontent.com/u/14338007?v=4?s=100" width="100px;" alt="Laurin Quast"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/mattvague"><img src="https://avatars.githubusercontent.com/u/64985?v=4?s=100" width="100px;" alt="Matt Vague"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/bglw"><img src="https://avatars.githubusercontent.com/u/40188355?v=4?s=100" width="100px;" alt="Liam Bigelow"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://about.me/jasonkurian"><img src="https://avatars.githubusercontent.com/u/2642545?v=4?s=100" width="100px;" alt="Jason Kurian"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/dphm"><img src="https://avatars.githubusercontent.com/u/1707217?v=4?s=100" width="100px;" alt="d pham (they/them)"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://www.aleksandrhovhannisyan.com/"><img src="https://avatars.githubusercontent.com/u/19352442?v=4?s=100" width="100px;" alt="Aleksandr Hovhannisyan"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/jg-rp"><img src="https://avatars.githubusercontent.com/u/72664870?v=4?s=100" width="100px;" alt="jg-rp"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/ameyaapte1"><img src="https://avatars.githubusercontent.com/u/16054747?v=4?s=100" width="100px;" alt="Ameya Apte"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/tbdrz"><img src="https://avatars.githubusercontent.com/u/50599116?v=4?s=100" width="100px;" alt="tbdrz"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://santialbo.com"><img src="https://avatars.githubusercontent.com/u/1557563?v=4?s=100" width="100px;" alt="Santi Albo"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/YahangWu"><img src="https://avatars.githubusercontent.com/u/12295975?v=4?s=100" width="100px;" alt="Yahang Wu"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/hongl-1"><img src="https://avatars.githubusercontent.com/u/101576612?v=4?s=100" width="100px;" alt="hongl"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/zxx-457"><img src="https://avatars.githubusercontent.com/u/114141362?v=4?s=100" width="100px;" alt="zxx-457"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/prassie"><img src="https://avatars.githubusercontent.com/u/1357831?v=4?s=100" width="100px;" alt="prassie"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://slavv.com/"><img src="https://avatars.githubusercontent.com/u/713329?v=4?s=100" width="100px;" alt="Slav Ivanov"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="http://www.orgflow.io/"><img src="https://avatars.githubusercontent.com/u/3889090?v=4?s=100" width="100px;" alt="Daniel Rosenberg"/></a></td>
|
||||
<td align="center" valign="top" width="0%"><a href="https://github.com/bobgubko"><img src="https://avatars.githubusercontent.com/u/733312?v=4?s=100" width="100px;" alt="bobgubko"/></a></td>
|
||||
<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>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
@@ -0,0 +1 @@
|
||||
<table> <tbody> <tr> <td align="center" valign="top" ><a href="https://www.opensense.com/"><img src="https://images.opencollective.com/opensense-inc/bf840ae/logo/256.png?height=100" alt="Opensense Inc."/></a></td> <td align="center" valign="top" ><a href="https://www.11ty.dev/"><img src="https://avatars.githubusercontent.com/u/35147177?v=4?s=100" alt="Eleventy"/></a></td> <td align="center" valign="top" ><a href="http://about.me/peterdehaan"><img src="https://avatars2.githubusercontent.com/u/557895?v=4?s=100" alt="Peter deHaan"/></a></td> <td align="center" valign="top" ><a href="https://opencollective.com/touchless"><img src="https://images.opencollective.com/touchless/273bc74/logo/256.png?height=100" alt="Touchless"/></a></td> <td align="center" valign="top" ><a href="https://www.dropkiq.com/"><img src="https://images.opencollective.com/1bertlol/43a8ea8/logo/256.png?height=100" alt="Adam Darrah"/></a></td> <td align="center" valign="top" ><a href="https://dailycontributors.com/"><img src="https://images.opencollective.com/dailycontributors/3c2e057/logo/256.png?height=100&width=100" alt="Dailycontributors"/></a></td> <td align="center" valign="top" ><a href="https://github.com/coni2k"><img src="https://avatars0.githubusercontent.com/u/1284601?v=4?s=100" alt="coni2k"/></a></td> <td align="center" valign="top" ><a href="https://github.com/amit777"><img src="https://avatars0.githubusercontent.com/u/2703309?v=4?s=100" alt="amit777"/></a></td> <td align="center" valign="top" ><a href="https://opencollective.com/khaled-salem"><img src="https://images.opencollective.com/khaled-salem/avatar/256.png?height=256" alt="Khaled Salem"/></a></td> <td align="center" valign="top" ><a href="https://sentry.io/"><img src="https://avatars.githubusercontent.com/u/1396951?v=4?s=100" alt="Sentry"/></a></td> <td align="center" valign="top" ><a href="https://www.checkoutblocks.com/"><img src="https://avatars.githubusercontent.com/u/114603307?v=4?s=100" alt="Checkout Blocks"/></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>
|
||||
|
||||
-1
@@ -19,7 +19,6 @@
|
||||
</select>
|
||||
</div>
|
||||
<a href="https://github.com/{{ config.github }}" class="main-nav-link icon-nav-link"><i class="icon-github"></i> <span class="icon-nav-title">Github</span></a>
|
||||
<a href="https://opencollective.com/liquidjs" class="main-nav-link icon-nav-link"><i class="icon-opencollective"></i> <span class="icon-nav-title">Support</span></a>
|
||||
<a id="mobile-nav-toggle">
|
||||
<span class="mobile-nav-toggle-bar"></span>
|
||||
<span class="mobile-nav-toggle-bar"></span>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
+20
-18
@@ -171,7 +171,7 @@
|
||||
background: var(--color-link-hover)
|
||||
color: #fff
|
||||
|
||||
#backers-wrap, #sponsors-wrap, #contributors-wrap
|
||||
#sponsors-wrap, #contributors-wrap
|
||||
background: var(--color-navy-lighter)
|
||||
border-top: 1px solid #161d24
|
||||
border-bottom: 1px solid #161d24
|
||||
@@ -192,28 +192,20 @@
|
||||
text-decoration: none
|
||||
|
||||
#contributors-wrap
|
||||
.all-contributors
|
||||
tr
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
td
|
||||
margin: 10px 2px 0
|
||||
a img
|
||||
display: inline-block
|
||||
height: 72px
|
||||
width: 72px
|
||||
background: var(--color-gray)
|
||||
border-radius: 50%
|
||||
margin-bottom: 10px
|
||||
.description a
|
||||
color: #fff
|
||||
&:hover
|
||||
background: #fff
|
||||
color: var(--color-navy-lighter)
|
||||
|
||||
#sponsors-wrap, #backers-wrap
|
||||
#sponsors-wrap
|
||||
border: none
|
||||
text-align: right
|
||||
background: var(--color-content-bg)
|
||||
overflow: hidden;
|
||||
.contributors
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
.inner
|
||||
h3
|
||||
color: var(--color-default)
|
||||
@@ -235,6 +227,16 @@
|
||||
background: #fff
|
||||
color: var(--color-navy-lighter)
|
||||
|
||||
#backers-wrap
|
||||
text-align: right
|
||||
background: var(--color-content-bg)
|
||||
.contributors
|
||||
tr
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
td
|
||||
margin: 10px 2px 0
|
||||
a img
|
||||
display: inline-block
|
||||
height: 72px
|
||||
width: 72px
|
||||
background: var(--color-gray)
|
||||
border-radius: 50%
|
||||
margin-bottom: 10px
|
||||
|
||||
+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.11.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+10
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.8.2",
|
||||
"version": "10.11.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",
|
||||
@@ -9,22 +9,24 @@
|
||||
"./dist/liquid.node.cjs.js": "./dist/liquid.browser.umd.js",
|
||||
"./dist/liquid.node.esm.js": "./dist/liquid.browser.esm.js"
|
||||
},
|
||||
"types": "dist/src/index.d.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint \"**/*.mjs\" \"**/*.ts\" .",
|
||||
"check": "npm run build && npm test && npm run lint && npm run perf:diff",
|
||||
"test": "jest --coverage",
|
||||
"check": "npm run build && npm run build:docs && npm test && npm run lint && npm run perf:diff",
|
||||
"test": "jest",
|
||||
"test:coverage": "jest --coverage src test/integration",
|
||||
"test:e2e": "jest test/e2e",
|
||||
"perf": "cd benchmark && npm ci && npm start",
|
||||
"perf:diff": "bin/perf-diff.sh",
|
||||
"perf:engines": "cd benchmark && npm run engines",
|
||||
"postversion": "npm run build:dist",
|
||||
"build": "npm run build:dist && npm run build:docs",
|
||||
"build:dist": "rollup -c rollup.config.mjs",
|
||||
"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": {
|
||||
|
||||
+4
-3
@@ -18,11 +18,12 @@ const treeshake = {
|
||||
const tsconfig = (target) => ({
|
||||
check: true,
|
||||
tsconfigOverride: {
|
||||
include: [ 'src' ],
|
||||
exclude: [ 'test', 'benchmark' ],
|
||||
include: ['src'],
|
||||
exclude: ['test', 'benchmark'],
|
||||
compilerOptions: {
|
||||
target,
|
||||
module: 'ES2015'
|
||||
module: 'ES2015',
|
||||
rootDir: 'src'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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, isObject } from '../util'
|
||||
|
||||
type PropertyKey = string | number;
|
||||
|
||||
@@ -37,7 +37,7 @@ export class Context {
|
||||
this.sync = !!renderOptions.sync
|
||||
this.opts = opts
|
||||
this.globals = renderOptions.globals ?? opts.globals
|
||||
this.environments = env
|
||||
this.environments = isObject(env) ? env : Object(env)
|
||||
this.strictVariables = renderOptions.strictVariables ?? this.opts.strictVariables
|
||||
this.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly
|
||||
}
|
||||
@@ -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!('.'))
|
||||
}
|
||||
}
|
||||
|
||||
+81
-17
@@ -1,8 +1,8 @@
|
||||
import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, isArray, isNil, last as arrayLast, hasOwnProperty } from '../util'
|
||||
import { isTruthy } from '../render'
|
||||
import { FilterImpl } from '../template'
|
||||
import { Scope } from '../context'
|
||||
import { isComparable } from '../drop'
|
||||
import { equals, evalToken, isTruthy } from '../render'
|
||||
import { Value, FilterImpl } from '../template'
|
||||
import { Context, Scope } from '../context'
|
||||
import { Tokenizer } from '../parser'
|
||||
|
||||
export const join = argumentsToValue((v: any[], arg: string) => toArray(v).join(arg === undefined ? ' ' : arg))
|
||||
export const last = argumentsToValue((v: any) => isArray(v) ? arrayLast(v) : '')
|
||||
@@ -11,10 +11,10 @@ export const reverse = argumentsToValue((v: any[]) => [...toArray(v)].reverse())
|
||||
|
||||
export function * sort<T> (this: FilterImpl, arr: T[], property?: string): IterableIterator<unknown> {
|
||||
const values: [T, string | number][] = []
|
||||
for (const item of toArray(toValue(arr))) {
|
||||
for (const item of toArray(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) => {
|
||||
@@ -25,7 +25,6 @@ export function * sort<T> (this: FilterImpl, arr: T[], property?: string): Itera
|
||||
}
|
||||
|
||||
export function sort_natural<T> (input: T[], property?: string) {
|
||||
input = toValue(input)
|
||||
const propertyString = stringify(property)
|
||||
const compare = property === undefined
|
||||
? caseInsensitiveCompare
|
||||
@@ -37,27 +36,51 @@ 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('.')))
|
||||
for (const item of toArray(arr)) {
|
||||
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(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)))
|
||||
}
|
||||
|
||||
export function concat<T1, T2> (v: T1[], arg: T2[] = []): (T1 | T2)[] {
|
||||
v = toValue(v)
|
||||
arg = toArray(arg).map(v => toValue(v))
|
||||
return toArray(v).concat(arg)
|
||||
return toArray(v).concat(toArray(arg))
|
||||
}
|
||||
|
||||
export function push<T> (v: T[], arg: T): T[] {
|
||||
return concat(v, [arg])
|
||||
}
|
||||
|
||||
export function unshift<T> (v: T[], arg: T): T[] {
|
||||
const clone = [...toArray(v)]
|
||||
clone.unshift(arg)
|
||||
return clone
|
||||
}
|
||||
|
||||
export function pop<T> (v: T[]): T[] {
|
||||
const clone = [...toArray(v)]
|
||||
clone.pop()
|
||||
return clone
|
||||
}
|
||||
|
||||
export function shift<T> (v: T[]): T[] {
|
||||
const clone = [...toArray(v)]
|
||||
clone.shift()
|
||||
return clone
|
||||
}
|
||||
|
||||
export function slice<T> (v: T[] | string, begin: number, length = 1): T[] | string {
|
||||
v = toValue(v)
|
||||
if (isNil(v)) return []
|
||||
@@ -68,17 +91,58 @@ export function slice<T> (v: T[] | string, begin: number, length = 1): T[] | str
|
||||
|
||||
export function * where<T extends object> (this: FilterImpl, arr: T[], property: string, expected?: any): IterableIterator<unknown> {
|
||||
const values: unknown[] = []
|
||||
arr = toArray(toValue(arr))
|
||||
arr = toArray(arr)
|
||||
const token = new Tokenizer(stringify(property)).readScopeValue()
|
||||
for (const item of arr) {
|
||||
values.push(yield this.context._getFromScope(item, stringify(property).split('.')))
|
||||
values.push(yield evalToken(token, new Context(item)))
|
||||
}
|
||||
return arr.filter((_, i) => {
|
||||
if (expected === undefined) return isTruthy(values[i], this.context)
|
||||
if (isComparable(expected)) return expected.equals(values[i])
|
||||
return values[i] === expected
|
||||
return equals(values[i], expected)
|
||||
})
|
||||
}
|
||||
|
||||
export function * group_by<T extends object> (arr: T[], property: string): IterableIterator<unknown> {
|
||||
const map = new Map()
|
||||
arr = toArray(arr)
|
||||
const token = new Tokenizer(stringify(property)).readScopeValue()
|
||||
for (const item of arr) {
|
||||
const key = yield evalToken(token, new Context(item))
|
||||
if (!map.has(key)) map.set(key, [])
|
||||
map.get(key).push(item)
|
||||
}
|
||||
return [...map.entries()].map(([name, items]) => ({ name, items }))
|
||||
}
|
||||
|
||||
export function * group_by_exp<T extends object> (this: FilterImpl, arr: T[], itemName: string, exp: string): IterableIterator<unknown> {
|
||||
const map = new Map()
|
||||
const keyTemplate = new Value(stringify(exp), this.liquid)
|
||||
for (const item of toArray(arr)) {
|
||||
const key = yield keyTemplate.value(new Context({ [itemName]: item }))
|
||||
if (!map.has(key)) map.set(key, [])
|
||||
map.get(key).push(item)
|
||||
}
|
||||
return [...map.entries()].map(([name, items]) => ({ name, items }))
|
||||
}
|
||||
|
||||
export function * find<T extends object> (this: FilterImpl, arr: T[], property: string, expected: string): IterableIterator<unknown> {
|
||||
const token = new Tokenizer(stringify(property)).readScopeValue()
|
||||
for (const item of toArray(arr)) {
|
||||
const value = yield evalToken(token, new Context(item))
|
||||
if (equals(value, expected)) return item
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function * find_exp<T extends object> (this: FilterImpl, arr: T[], itemName: string, exp: string): IterableIterator<unknown> {
|
||||
const predicate = new Value(stringify(exp), this.liquid)
|
||||
for (const item of toArray(arr)) {
|
||||
const value = yield predicate.value(new Context({ [itemName]: item }))
|
||||
if (value) return item
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function uniq<T> (arr: T[]): T[] {
|
||||
arr = toValue(arr)
|
||||
const u = {}
|
||||
|
||||
+2
-12
@@ -25,9 +25,9 @@ export function date (this: FilterImpl, v: string | Date, format?: string, timez
|
||||
}
|
||||
if (!isValidDate(date)) return v
|
||||
if (timezoneOffset !== undefined) {
|
||||
date = new TimezoneDate(date, parseTimezoneOffset(date, timezoneOffset))
|
||||
date = new TimezoneDate(date, timezoneOffset)
|
||||
} else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) {
|
||||
date = new TimezoneDate(date, parseTimezoneOffset(date, opts.timezoneOffset))
|
||||
date = new TimezoneDate(date, opts.timezoneOffset)
|
||||
}
|
||||
return strftime(date, format)
|
||||
}
|
||||
@@ -35,13 +35,3 @@ export function date (this: FilterImpl, v: string | Date, format?: string, timez
|
||||
function isValidDate (date: any): date is Date {
|
||||
return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime())
|
||||
}
|
||||
|
||||
/**
|
||||
* need pass in a `date` because offset is dependent on whether DST is active
|
||||
*/
|
||||
function parseTimezoneOffset (date: Date, timeZone: string | number) {
|
||||
if (isNumber(timeZone)) return timeZone
|
||||
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }))
|
||||
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }))
|
||||
return (utcDate.getTime() - tzDate.getTime()) / 6e4
|
||||
}
|
||||
|
||||
+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\'')
|
||||
|
||||
+97
-39
@@ -1,26 +1,28 @@
|
||||
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,
|
||||
operators: Operators = defaultOptions.operators,
|
||||
public file?: string,
|
||||
private range?: [number, number]
|
||||
range?: [number, number]
|
||||
) {
|
||||
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,10 +245,15 @@ 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)
|
||||
}
|
||||
|
||||
readNonEmptyIdentifier (): IdentifierToken | undefined {
|
||||
const id = this.readIdentifier()
|
||||
return id.size() ? id : undefined
|
||||
}
|
||||
|
||||
readTagName (): string {
|
||||
this.skipBlank()
|
||||
// Handle inline comment tags
|
||||
@@ -254,8 +274,8 @@ export class Tokenizer {
|
||||
this.skipBlank()
|
||||
if (this.peek() === ',') ++this.p
|
||||
const begin = this.p
|
||||
const name = this.readIdentifier()
|
||||
if (!name.size()) return
|
||||
const name = this.readNonEmptyIdentifier()
|
||||
if (!name) return
|
||||
let value
|
||||
|
||||
this.skipBlank()
|
||||
@@ -271,8 +291,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 +308,81 @@ export class Tokenizer {
|
||||
}
|
||||
|
||||
readValue (): ValueToken | undefined {
|
||||
const value = this.readQuoted() || this.readRange()
|
||||
if (value) return value
|
||||
this.skipBlank()
|
||||
const begin = this.p
|
||||
const variable = this.readLiteral() || this.readQuoted() || this.readRange() || this.readNumber()
|
||||
const props = this.readProperties(!variable)
|
||||
if (!props.length) return variable
|
||||
return new PropertyAccessToken(variable, props, this.input, begin, this.p)
|
||||
}
|
||||
|
||||
if (this.peek() === '[') {
|
||||
this.p++
|
||||
const prop = this.readQuoted()
|
||||
if (!prop) return
|
||||
if (this.peek() !== ']') return
|
||||
this.p++
|
||||
return new PropertyAccessToken(prop, [], this.p)
|
||||
}
|
||||
readScopeValue (): ValueToken | undefined {
|
||||
this.skipBlank()
|
||||
const begin = this.p
|
||||
const props = this.readProperties()
|
||||
if (!props.length) return undefined
|
||||
return new PropertyAccessToken(undefined, props, this.input, begin, this.p)
|
||||
}
|
||||
|
||||
const variable = this.readIdentifier()
|
||||
if (!variable.size()) return
|
||||
|
||||
let isNumber = variable.isNumber(true)
|
||||
const props: (QuotedToken | IdentifierToken)[] = []
|
||||
private readProperties (isBegin = true): (ValueToken | IdentifierToken)[] {
|
||||
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 (isBegin && !props.length) {
|
||||
const prop = this.readNonEmptyIdentifier()
|
||||
if (prop) {
|
||||
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
|
||||
const prop = this.readNonEmptyIdentifier()
|
||||
if (!prop) break
|
||||
props.push(prop)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return props
|
||||
}
|
||||
|
||||
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 +445,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' },
|
||||
@@ -143,6 +147,20 @@ describe('Expression', function () {
|
||||
const ctx = new Context({ x: 'XXX', X: new TemplateDrop() })
|
||||
expect(await toPromise(create('x contains X').evaluate(ctx, false))).toBe(true)
|
||||
})
|
||||
it('should support Drops for "x contains "x"" when x is an array', async () => {
|
||||
class TemplateDrop extends Drop {
|
||||
valueOf () { return 'X' }
|
||||
}
|
||||
const ctx = new Context({ x: [new TemplateDrop()], X: 'X' })
|
||||
expect(await toPromise(create('x contains X').evaluate(ctx, false))).toBe(true)
|
||||
})
|
||||
it('should support Drops for "x contains "x"" when x is an array on both operands', async () => {
|
||||
class TemplateDrop extends Drop {
|
||||
valueOf () { return 'X' }
|
||||
}
|
||||
const ctx = new Context({ x: [new TemplateDrop()], X: new TemplateDrop() })
|
||||
expect(await toPromise(create('x contains X').evaluate(ctx, false))).toBe(true)
|
||||
})
|
||||
it('should support value and !=', async function () {
|
||||
const ctx = new Context({ empty: '' })
|
||||
expect(await toPromise(create('empty and empty != ""').evaluate(ctx, false))).toBe(false)
|
||||
@@ -162,6 +180,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 +191,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,12 +35,10 @@ 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> {
|
||||
@@ -50,24 +47,20 @@ function * evalPropertyAccessToken (token: PropertyAccessToken, ctx: Context, le
|
||||
props.push((yield evalToken(prop, ctx, false)) as unknown as string)
|
||||
}
|
||||
try {
|
||||
return yield ctx._get([token.propertyName, ...props])
|
||||
if (token.variable) {
|
||||
const variable = yield evalToken(token.variable, ctx, lenient)
|
||||
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) {
|
||||
|
||||
+11
-10
@@ -1,8 +1,8 @@
|
||||
import { isComparable } from '../drop/comparable'
|
||||
import { Context } from '../context'
|
||||
import { isFunction, toValue } from '../util'
|
||||
import { toValue } from '../util'
|
||||
import { isFalsy, isTruthy } from '../render/boolean'
|
||||
import { isArray } from '../util/underscore'
|
||||
import { isArray, isFunction } from '../util/underscore'
|
||||
|
||||
export type UnaryOperatorHandler = (operand: any, ctx: Context) => boolean;
|
||||
export type BinaryOperatorHandler = (lhs: any, rhs: any, ctx: Context) => boolean;
|
||||
@@ -10,8 +10,8 @@ export type OperatorHandler = UnaryOperatorHandler | BinaryOperatorHandler;
|
||||
export type Operators = Record<string, OperatorHandler>
|
||||
|
||||
export const defaultOperators: Operators = {
|
||||
'==': equal,
|
||||
'!=': (l: any, r: any) => !equal(l, r),
|
||||
'==': equals,
|
||||
'!=': (l: any, r: any) => !equals(l, r),
|
||||
'>': (l: any, r: any) => {
|
||||
if (isComparable(l)) return l.gt(r)
|
||||
if (isComparable(r)) return r.lt(l)
|
||||
@@ -34,26 +34,27 @@ export const defaultOperators: Operators = {
|
||||
},
|
||||
'contains': (l: any, r: any) => {
|
||||
l = toValue(l)
|
||||
r = toValue(r)
|
||||
return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false
|
||||
if (isArray(l)) return l.some((i) => equals(i, r))
|
||||
if (isFunction(l?.indexOf)) return l.indexOf(toValue(r)) > -1
|
||||
return false
|
||||
},
|
||||
'not': (v: any, ctx: Context) => isFalsy(toValue(v), ctx),
|
||||
'and': (l: any, r: any, ctx: Context) => isTruthy(toValue(l), ctx) && isTruthy(toValue(r), ctx),
|
||||
'or': (l: any, r: any, ctx: Context) => isTruthy(toValue(l), ctx) || isTruthy(toValue(r), ctx)
|
||||
}
|
||||
|
||||
function equal (lhs: any, rhs: any): boolean {
|
||||
export function equals (lhs: any, rhs: any): boolean {
|
||||
if (isComparable(lhs)) return lhs.equals(rhs)
|
||||
if (isComparable(rhs)) return rhs.equals(lhs)
|
||||
lhs = toValue(lhs)
|
||||
rhs = toValue(rhs)
|
||||
if (isArray(lhs)) {
|
||||
return isArray(rhs) && arrayEqual(lhs, rhs)
|
||||
return isArray(rhs) && arrayEquals(lhs, rhs)
|
||||
}
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
function arrayEqual (lhs: any[], rhs: any[]): boolean {
|
||||
function arrayEquals (lhs: any[], rhs: any[]): boolean {
|
||||
if (lhs.length !== rhs.length) return false
|
||||
return !lhs.some((value, i) => !equal(value, rhs[i]))
|
||||
return !lhs.some((value, i) => !equals(value, rhs[i]))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseStringLiteral } from './parse-string-literal'
|
||||
import { parseStringLiteral } from './string'
|
||||
|
||||
describe('parseStringLiteral()', function () {
|
||||
it('should parse octal escape', () => {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user