mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-14 03:40:38 -07:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7910e4996c | ||
|
|
dbbf628803 | ||
|
|
26ea2856c7 | ||
|
|
a55f543f49 | ||
|
|
d1d517d1ec | ||
|
|
1c816d4fc3 | ||
|
|
34877950bf | ||
|
|
75c815a4d7 | ||
|
|
f1f896c29d | ||
|
|
0ee6dbb511 | ||
|
|
30e04ba16d | ||
|
|
e2311dfd6e | ||
|
|
2def22c85e | ||
|
|
4af7be695c | ||
|
|
05c47da46d | ||
|
|
66011d14b0 | ||
|
|
1cdf10b57d | ||
|
|
4f9a49988a | ||
|
|
f41c1fc02f |
+28
-1
@@ -14,7 +14,7 @@
|
||||
"login": "harttle",
|
||||
"name": "Jun Yang",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/4427974?v=4",
|
||||
"profile": "https://harttle.land",
|
||||
"profile": "https://github.com/harttle",
|
||||
"contributions": [
|
||||
"maintenance",
|
||||
"code"
|
||||
@@ -811,6 +811,33 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timbze",
|
||||
"name": "Timmy Braun",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/35117769?v=4",
|
||||
"profile": "https://github.com/timbze",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "talboren",
|
||||
"name": "Tal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/68807791?v=4",
|
||||
"profile": "https://github.com/talboren",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "VladimirFilonov",
|
||||
"name": "Vladimir Filonov",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/813224?v=4",
|
||||
"profile": "https://filonov.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
description: Testing conventions — e2e uses built dist, integration uses src
|
||||
globs: test/**/*.ts
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Testing
|
||||
|
||||
## End-to-end tests (`test/e2e`)
|
||||
|
||||
- **Use the built package**, not TypeScript sources under `src/`.
|
||||
- Import the public API from the package root (for example `import { Liquid } from '../..'`), which resolves through `package.json` to **`dist/`** (`main`, `module`, etc.).
|
||||
- **Avoid** `import … from '../../src/liquid'` (or other `src/` paths) in `test/e2e/**` so e2e matches what consumers get from npm and you do not depend on an unbuilt tree.
|
||||
|
||||
## Integration and unit tests
|
||||
|
||||
- Tests under `test/integration/`, `src/**/*.spec.ts`, and similar may import from **`src/`** when the suite is meant to run against the current TypeScript sources (typical for this repo’s Jest setup).
|
||||
@@ -1,3 +1,26 @@
|
||||
## [10.25.7](https://github.com/harttle/liquidjs/compare/v10.25.6...v10.25.7) (2026-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **filters:** support Buffer input in base64_encode to prevent binary data corruption ([#881](https://github.com/harttle/liquidjs/issues/881)) ([0ee6dbb](https://github.com/harttle/liquidjs/commit/0ee6dbb511aa926f6d490293282060abf3bab37f))
|
||||
|
||||
## [10.25.6](https://github.com/harttle/liquidjs/compare/v10.25.5...v10.25.6) (2026-04-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* nested block for layout ([#883](https://github.com/harttle/liquidjs/issues/883)) ([e2311df](https://github.com/harttle/liquidjs/commit/e2311dfd6e82f73509308aa8a3a1fafc92e226f0))
|
||||
|
||||
## [10.25.5](https://github.com/harttle/liquidjs/compare/v10.25.4...v10.25.5) (2026-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enforce root containment for renderFile/parseFile lookups ([#870](https://github.com/harttle/liquidjs/issues/870)) ([f41c1fc](https://github.com/harttle/liquidjs/commit/f41c1fc02fe901598f3328118b42b13bc6bc9b04))
|
||||
* null date should return empty ([#868](https://github.com/harttle/liquidjs/issues/868)) ([#872](https://github.com/harttle/liquidjs/issues/872)) ([4f9a499](https://github.com/harttle/liquidjs/commit/4f9a49988a93c156524981e189a4fec238e682b8))
|
||||
* rounding negative away from zero when half ([#873](https://github.com/harttle/liquidjs/issues/873)) ([1cdf10b](https://github.com/harttle/liquidjs/commit/1cdf10b57d82f0592414efbfca19e204b37aea9f))
|
||||
|
||||
## [10.25.4](https://github.com/harttle/liquidjs/compare/v10.25.3...v10.25.4) (2026-04-07)
|
||||
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ For more details, refer to the [Setup Guide][setup].
|
||||
|
||||
- [Eleventy](https://www.11ty.dev/): Eleventy, a simpler static site generator.
|
||||
- [Github Docs](https://github.com/github/docs): The open-source repo for docs.github.com.
|
||||
- [Kibana](https://github.com/elastic/kibana): Elastic's analytics and visualization platform for Elasticsearch; workflow features use LiquidJS for Liquid templates.
|
||||
- [Opensense](https://www.opensense.com/): The smarter way to send email.
|
||||
- [Directus](https://docs.directus.io/): an instant REST+GraphQL API and intuitive no-code data collaboration app for any SQL database.
|
||||
- [Semgrep](https://github.com/returntocorp/semgrep): Lightweight static analysis for many languages.
|
||||
- [Rock](https://www.rockrms.com/): An open source CMS, Relationship Management System (RMS) and Church Management System (ChMS) all rolled into one.
|
||||
- [Mitosis](https://github.com/BuilderIO/mitosis): Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
|
||||
- [Pattern Lab](https://patternlab.io/): a frontend workshop environment that helps you build, view, test, and showcase your design system's UI components.
|
||||
@@ -64,6 +64,7 @@ For more details, refer to the [Setup Guide][setup].
|
||||
- [Microsoft Power Pages](https://learn.microsoft.com/en-us/power-pages/introduction): a secure, enterprise-grade, low-code software as a service (SaaS) platform for creating, hosting, and administering modern external-facing business websites.
|
||||
- [Azure API Management developer portal](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-developer-portal): an automatically generated, fully customizable website with the documentation of your APIs.
|
||||
- [WISMOlabs](https://wismolabs.com/): Post Purchase Experience platform for eCommerce retailers enhancing customer satisfaction by using LiquidJS to provide customizable post-purchase experiences through programmable email, SMS, order tracking pages, and webhooks.
|
||||
- [Freshet](https://chromewebstore.google.com/detail/freshet/mpclplhdencffbilobpcapccnihpelcg): *JSON in, page out* — a Chrome extension that uses LiquidJS templates per URL pattern, so the JSON becomes a rendered, useful page.
|
||||
|
||||
Feel free to create a PR or contact me to add your use case into this list!
|
||||
|
||||
@@ -82,6 +83,7 @@ If you personally love LiquidJS or it's benefiting your business, please conside
|
||||
<a href="https://syntax.fm/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/130389858?v=4&s=100" height="80" style="vertical-align: middle;" alt="Syntax Podcast" title="Syntax Podcast"/></a>
|
||||
<br/>
|
||||
<a href="https://www.testmuai.com/?utm_medium=sponsor&utm_source=liquidjs" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/27130435?s=200&v=4" width="80" style="vertical-align: middle;" alt="TestMu AI" title="TestMu AI"/></a>
|
||||
<a href="https://github.com/talboren" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://avatars.githubusercontent.com/u/68807791?v=4&s=100" height="80" style="vertical-align: middle;" alt="Tal" title="Tal (@talboren)"/></a>
|
||||
<a href="https://chudovo.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/Chudovo/avatar/256.png?height=100" width="160" style="vertical-align: middle;background: white;padding: 8px 16px;" alt="Chudovo" title="Chudovo"/></a>
|
||||
<a href="https://dailycontributors.com/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/dailycontributors/3c2e057/logo/256.png?height=50&width=100" width="120" style="vertical-align: middle;" alt="Dailycontributors" title="Dailycontributors"/></a>
|
||||
<a href="https://www.pakstyle.pk/" style="display: inline-block; vertical-align: middle; margin: 8px;"><img src="https://images.opencollective.com/pakstyle/2b81605/logo/256.png?height=100" height="80" style="vertical-align: middle;" alt="PakStyle.pk" title="PakStyle.pk"/></a>
|
||||
@@ -108,7 +110,7 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://harttle.land"><img src="https://avatars3.githubusercontent.com/u/4427974?v=4?s=100" width="100px;" alt="Jun Yang"/><br /><sub><b>Jun Yang</b></sub></a><br /><a href="#maintenance-harttle" title="Maintenance">🚧</a> <a href="https://github.com/harttle/liquidjs/commits?author=harttle" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/harttle"><img src="https://avatars3.githubusercontent.com/u/4427974?v=4?s=100" width="100px;" alt="Jun Yang"/><br /><sub><b>Jun Yang</b></sub></a><br /><a href="#maintenance-harttle" title="Maintenance">🚧</a> <a href="https://github.com/harttle/liquidjs/commits?author=harttle" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chenos"><img src="https://avatars0.githubusercontent.com/u/2993310?v=4?s=100" width="100px;" alt="chenos"/><br /><sub><b>chenos</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=chenos" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://zachleat.com/"><img src="https://avatars2.githubusercontent.com/u/39355?v=4?s=100" width="100px;" alt="Zach Leatherman"/><br /><sub><b>Zach Leatherman</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/issues?q=author%3Azachleat" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/thardy"><img src="https://avatars3.githubusercontent.com/u/120636?v=4?s=100" width="100px;" alt="Tim Hardy"/><br /><sub><b>Tim Hardy</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=thardy" title="Code">💻</a></td>
|
||||
@@ -220,6 +222,9 @@ Want to contribute? see [Contribution Guidelines][contribution]. Thanks goes to
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MorielHarush"><img src="https://avatars.githubusercontent.com/u/93482738?v=4?s=100" width="100px;" alt="MorielHarush"/><br /><sub><b>MorielHarush</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=MorielHarush" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://doruk.ch"><img src="https://avatars.githubusercontent.com/u/77903714?v=4?s=100" width="100px;" alt="Peak Twilight"/><br /><sub><b>Peak Twilight</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=peaktwilight" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/joecottam"><img src="https://avatars.githubusercontent.com/u/44173086?v=4?s=100" width="100px;" alt="Joe Cottam"/><br /><sub><b>Joe Cottam</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=joecottam" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/timbze"><img src="https://avatars.githubusercontent.com/u/35117769?v=4?s=100" width="100px;" alt="Timmy Braun"/><br /><sub><b>Timmy Braun</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=timbze" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/talboren"><img src="https://avatars.githubusercontent.com/u/68807791?v=4?s=100" width="100px;" alt="Tal"/><br /><sub><b>Tal</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=talboren" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://filonov.dev"><img src="https://avatars.githubusercontent.com/u/813224?v=4?s=100" width="100px;" alt="Vladimir Filonov"/><br /><sub><b>Vladimir Filonov</b></sub></a><br /><a href="https://github.com/harttle/liquidjs/commits?author=VladimirFilonov" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rm -rf docs/source/api
|
||||
typedoc --plugin typedoc-plugin-missing-exports ./src --gitRevision master --out docs/source/api
|
||||
@@ -0,0 +1,24 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const src = path.join(root, 'CHANGELOG.md')
|
||||
|
||||
let content = fs.readFileSync(src, 'utf8').replace(/\r\n/g, '\n')
|
||||
|
||||
const lines = content.split('\n')
|
||||
lines[0] = lines[0]
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
content = lines.join('\n')
|
||||
|
||||
content = content
|
||||
.replace(/{%/g, '{% raw %}{%{% endraw %}')
|
||||
.replace(/\{\{/g, '{% raw %}{{{% endraw %}')
|
||||
|
||||
const enFrontmatter = '---\ntitle: Changelog\nauto: true\n---\n\n'
|
||||
const zhFrontmatter = '---\ntitle: 更新日志\nauto: true\n---\n\n'
|
||||
|
||||
fs.writeFileSync(path.join(root, 'docs/source/tutorials/changelog.md'), enFrontmatter + content)
|
||||
fs.writeFileSync(path.join(root, 'docs/source/zh-cn/tutorials/changelog.md'), zhFrontmatter + content)
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd docs
|
||||
cp ../CHANGELOG.md source/tutorials/changelog.md
|
||||
sed -i \
|
||||
-e 's/{%/{% raw %}{%{% endraw %}/g' \
|
||||
-e 's/{{/{% raw %}{{{% endraw %}/g' \
|
||||
-e '1 s/"/\"/g' \
|
||||
-e '1 s/</\</g' \
|
||||
-e '1 s/>/\>/g' \
|
||||
source/tutorials/changelog.md
|
||||
cp source/tutorials/changelog.md source/zh-cn/tutorials/changelog.md
|
||||
|
||||
sed -i -e '1i\---\ntitle: Changelog\nauto: true\n---\n' source/tutorials/changelog.md
|
||||
sed -i -e '1i\---\ntitle: 更新日志\nauto: true\n---\n' source/zh-cn/tutorials/changelog.md
|
||||
@@ -0,0 +1,39 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const readme = fs.readFileSync(path.join(root, 'README.md'), 'utf8').replace(/\r\n/g, '\n')
|
||||
|
||||
function extractSection (text, beginMarker, endMarker) {
|
||||
const lines = text.split('\n')
|
||||
let inside = false
|
||||
const result = []
|
||||
for (const line of lines) {
|
||||
if (line.includes(endMarker)) inside = false
|
||||
if (inside) result.push(line)
|
||||
if (line.includes(beginMarker)) inside = true
|
||||
}
|
||||
return result.join('\n')
|
||||
}
|
||||
|
||||
function transformContributors (html) {
|
||||
return html
|
||||
.replace(/<br \/>.*?<\/td>/g, '</a></td>')
|
||||
.replace(/width="[^"]*"/g, '')
|
||||
.replace(/\n/g, '')
|
||||
.replace(/<\/tr>\s*<tr>/g, '')
|
||||
}
|
||||
|
||||
function transformFinancial (html) {
|
||||
return html
|
||||
.replace(/<br \/>.*?<\/td>/g, '</a></td>')
|
||||
.replace(/\n/g, '')
|
||||
.replace(/<\/tr>\s*<tr>/g, '')
|
||||
}
|
||||
|
||||
const allContributors = transformContributors(extractSection(readme, 'ALL-CONTRIBUTORS-LIST:START', 'ALL-CONTRIBUTORS-LIST:END'))
|
||||
const financialContributors = transformFinancial(extractSection(readme, 'FINANCIAL-CONTRIBUTORS-BEGIN', 'FINANCIAL-CONTRIBUTORS-END'))
|
||||
|
||||
const outDir = path.join(root, 'docs/themes/navy/layout/partial')
|
||||
fs.writeFileSync(path.join(outDir, 'all-contributors.swig'), allContributors)
|
||||
fs.writeFileSync(path.join(outDir, 'financial-contributors.swig'), financialContributors)
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run `sed` in a way that's compatible with both macOS (BSD) and Linux (GNU)
|
||||
sedi() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
/usr/bin/sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# create docs/themes/navy/layout/partial/all-contributors.swig
|
||||
awk '/ALL-CONTRIBUTORS-LIST:START/{flag=1;next}/ALL-CONTRIBUTORS-LIST:END/{flag=0}flag' README.md | \
|
||||
sed 's/<br \/>.*<\/td>/<\/a><\/td>/g' | \
|
||||
sed 's/width="[^"]*"//g' | \
|
||||
tr -d '\n' | \
|
||||
sed 's/<\/tr>\s*<tr>//g' \
|
||||
> docs/themes/navy/layout/partial/all-contributors.swig
|
||||
|
||||
# create docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
awk '/FINANCIAL-CONTRIBUTORS-BEGIN/{flag=1;next}/FINANCIAL-CONTRIBUTORS-END/{flag=0}flag' README.md | \
|
||||
sed 's/<br \/>.*<\/td>/<\/a><\/td>/g' | \
|
||||
tr -d '\n' | \
|
||||
sed 's/<\/tr>\s*<tr>//g' \
|
||||
> docs/themes/navy/layout/partial/financial-contributors.swig
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BUNDLES=min npm run build
|
||||
mkdir -p docs/public/js/
|
||||
cp dist/liquid.browser.min.js docs/public/js/
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
./bin/build-docs-liquid.sh
|
||||
./bin/build-contributors.sh
|
||||
./bin/build-apidoc.sh
|
||||
./bin/build-changelog.sh
|
||||
|
||||
cd docs
|
||||
npm ci
|
||||
npm run build
|
||||
cp CNAME public/
|
||||
|
||||
if [ "$HEXO_ALGOLIA_INDEXING_KEY" != "" ]; then
|
||||
npm run index
|
||||
fi
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prerequisites:
|
||||
# 1.imagemagick. try brew install imagemagick
|
||||
# 2. logo.png in size 512x512
|
||||
# 3. this script should be run with cwd docs/source/icon/
|
||||
|
||||
echo creating apple touch icons...
|
||||
apple=(57x57 60x60 72x72 76x76 114x114 120x120 144x144 152x152)
|
||||
for size in "${apple[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size apple-touch-icon-$size.png
|
||||
done
|
||||
cp logo.png apple-touch-icon.png
|
||||
convert logo.png \
|
||||
\( +clone -alpha extract \
|
||||
-draw 'fill black polygon 0,0 0,80 80,0 fill white circle 80,80 80,0' \
|
||||
\( +clone -flip \) -compose Multiply -composite \
|
||||
\( +clone -flop \) -compose Multiply -composite \
|
||||
\) -alpha off -compose CopyOpacity -composite apple-touch-icon-precomposed.png
|
||||
|
||||
echo creating favicon...
|
||||
convert logo.png -resize 48x48 ../favicon.ico
|
||||
favicon=(16x16 32x32 96x96 160x160 196x196)
|
||||
for size in "${favicon[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size favicon-$size.png
|
||||
done
|
||||
|
||||
echo creating mstile icons...
|
||||
mstile=(70x70 144x144 150x150 310x310)
|
||||
for size in "${mstile[@]}"; do
|
||||
echo $size
|
||||
convert logo.png -resize $size mstile-$size.png
|
||||
done
|
||||
convert mstile-150x150.png -gravity center -background white -extent 310x150 mstile-310x150.png
|
||||
@@ -0,0 +1,17 @@
|
||||
const { execSync } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const version = require(path.join(root, 'package.json')).version
|
||||
|
||||
const fileLocal = path.join(root, 'dist/liquid.node.js')
|
||||
const fileLatest = path.join(root, `dist/liquid.node.${version}.js`)
|
||||
|
||||
if (!fs.existsSync(fileLatest)) {
|
||||
const url = `https://unpkg.com/liquidjs@${version}/dist/liquid.node.js`
|
||||
console.log(`Downloading liquidjs@${version}...`)
|
||||
execSync(`curl -sL -o "${fileLatest}" "${url}"`)
|
||||
}
|
||||
|
||||
execSync(`node benchmark/diff.js "${fileLocal}" "${fileLatest}"`, { cwd: root, stdio: 'inherit' })
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION_LATEST=$(cat package.json | grep '"version":' | head -1 | awk -F'"' '{print $4}')
|
||||
FILE_LOCAL=dist/liquid.node.js
|
||||
FILE_LATEST=dist/liquid.node.$VERSION_LATEST.js
|
||||
URL_LATEST=https://unpkg.com/liquidjs@$VERSION_LATEST/dist/liquid.node.js
|
||||
|
||||
if [ ! -f "$FILE_LATEST" ]; then
|
||||
curl $URL_LATEST > $FILE_LATEST
|
||||
fi
|
||||
|
||||
exec node benchmark/diff.js $FILE_LOCAL $FILE_LATEST
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: hmac_sha256
|
||||
---
|
||||
|
||||
{% since %}vNEXT{% endsince %}
|
||||
|
||||
Converts a string into an SHA-256 hash using a hash message authentication code (HMAC). The secret key is passed as the filter argument. The output is a lowercase hexadecimal string.
|
||||
|
||||
Input
|
||||
|
||||
```liquid
|
||||
{%- assign secret_potion = 'Polyjuice' | hmac_sha256: 'Polina' -%}
|
||||
My secret potion: {{ secret_potion }}
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
My secret potion: 8e0d5d65cff1242a4af66c8f4a32854fd5fb80edcc8aabe9b302b29c7c71dc20
|
||||
```
|
||||
@@ -16,5 +16,6 @@ Array | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_
|
||||
Date | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string
|
||||
Misc | default, json, jsonify, inspect, raw, to_integer
|
||||
Base64 | base64_encode, base64_decode
|
||||
Crypto | sha256, hmac_sha256
|
||||
|
||||
[shopify/liquid]: https://github.com/Shopify/liquid
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: sha256
|
||||
---
|
||||
|
||||
{% since %}vNEXT{% endsince %}
|
||||
|
||||
Converts a string into an SHA-256 hash. The output is a lowercase hexadecimal string.
|
||||
|
||||
Input
|
||||
|
||||
```liquid
|
||||
{%- assign secret_potion = 'Polyjuice' | sha256 -%}
|
||||
My secret potion: {{ secret_potion }}
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
My secret potion: 44ac1d7a2936e30a5de07082fd65d6fe9b1fb658a1a98bfe65bc5959beac5dd0
|
||||
```
|
||||
Generated
+416
-17
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.25.4",
|
||||
"version": "10.25.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "liquidjs",
|
||||
"version": "10.25.4",
|
||||
"version": "10.25.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^10.0.0"
|
||||
@@ -48,12 +48,14 @@
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^29.5.0",
|
||||
"jsdom": "^16.5.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"rollup": "^1.26.3",
|
||||
"rollup-plugin-replace": "^2.1.0",
|
||||
"rollup-plugin-typescript2": "^0.31.1",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"rollup-plugin-version-injector": "^1.3.3",
|
||||
"semantic-release": "^25.0.3",
|
||||
"shx": "^0.4.0",
|
||||
"sinon": "^15.0.2",
|
||||
"supertest": "^3.4.2",
|
||||
"ts-jest": "^29.0.5",
|
||||
@@ -151,6 +153,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz",
|
||||
"integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
@@ -1530,6 +1533,7 @@
|
||||
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.3",
|
||||
@@ -2876,6 +2880,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz",
|
||||
"integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
@@ -3091,6 +3096,7 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3720,12 +3726,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3752,6 +3759,7 @@
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001449",
|
||||
"electron-to-chromium": "^1.4.284",
|
||||
@@ -4879,6 +4887,16 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enquirer": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
|
||||
@@ -5293,6 +5311,7 @@
|
||||
"integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.12.11",
|
||||
"@eslint/eslintrc": "^0.4.3",
|
||||
@@ -5470,6 +5489,7 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
|
||||
"integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flat": "^1.3.1",
|
||||
@@ -5544,6 +5564,7 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz",
|
||||
"integrity": "sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eslint-plugin-es": "^1.3.1",
|
||||
"eslint-utils": "^1.3.1",
|
||||
@@ -5594,6 +5615,7 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz",
|
||||
"integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -5617,6 +5639,7 @@
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"eslint": ">=5.0.0"
|
||||
}
|
||||
@@ -6002,16 +6025,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.4"
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
@@ -6075,10 +6099,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -7195,6 +7220,16 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
|
||||
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/into-stream": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz",
|
||||
@@ -7363,6 +7398,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -8963,6 +8999,7 @@
|
||||
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
@@ -9049,6 +9086,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memorystream": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
|
||||
"integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/meow": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
|
||||
@@ -9105,12 +9151,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -9543,6 +9590,95 @@
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-normalize-package-bin": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
|
||||
"integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-all2": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz",
|
||||
"integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"memorystream": "^0.3.1",
|
||||
"picomatch": "^4.0.2",
|
||||
"pidtree": "^0.6.0",
|
||||
"read-package-json-fast": "^4.0.0",
|
||||
"shell-quote": "^1.7.3",
|
||||
"which": "^5.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"npm-run-all": "bin/npm-run-all/index.js",
|
||||
"npm-run-all2": "bin/npm-run-all/index.js",
|
||||
"run-p": "bin/run-p/index.js",
|
||||
"run-s": "bin/run-s/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.5.0 || >=22.0.0",
|
||||
"npm": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-all2/node_modules/ansi-styles": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-all2/node_modules/isexe": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
|
||||
"integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-all2/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-all2/node_modules/which": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
||||
"integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
@@ -11275,6 +11411,7 @@
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -11584,6 +11721,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/p-finally": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||
"integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/p-is-promise": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz",
|
||||
@@ -11842,6 +11989,19 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pidtree": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"pidtree": "bin/pidtree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
|
||||
@@ -12135,6 +12295,17 @@
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
||||
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
||||
@@ -12275,6 +12446,30 @@
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/read-package-json-fast": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz",
|
||||
"integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"json-parse-even-better-errors": "^4.0.0",
|
||||
"npm-normalize-package-bin": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz",
|
||||
"integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz",
|
||||
@@ -12502,6 +12697,18 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||
"integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/redent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
|
||||
@@ -12727,6 +12934,7 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz",
|
||||
"integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/node": "*",
|
||||
@@ -12965,6 +13173,7 @@
|
||||
"integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||
"@semantic-release/error": "^4.0.0",
|
||||
@@ -13877,6 +14086,166 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz",
|
||||
"integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"execa": "^1.0.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"interpret": "^1.0.0",
|
||||
"rechoir": "^0.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"shjs": "bin/shjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/cross-spawn": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
|
||||
"integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nice-try": "^1.0.4",
|
||||
"path-key": "^2.0.1",
|
||||
"semver": "^5.5.0",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.8"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/execa": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
|
||||
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^6.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
"is-stream": "^1.1.0",
|
||||
"npm-run-path": "^2.0.0",
|
||||
"p-finally": "^1.0.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"strip-eof": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/get-stream": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/is-stream": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
||||
"integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||
"integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/path-key": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||
"integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
"integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/shebang-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||
"integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs/node_modules/which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"which": "bin/which"
|
||||
}
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.13.0.tgz",
|
||||
@@ -13887,6 +14256,23 @@
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/shx": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz",
|
||||
"integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.8",
|
||||
"shelljs": "^0.9.2"
|
||||
},
|
||||
"bin": {
|
||||
"shx": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
@@ -14356,6 +14742,16 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
"integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-final-newline": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||
@@ -14756,6 +15152,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -14795,6 +15192,7 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@@ -15111,6 +15509,7 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liquidjs",
|
||||
"version": "10.25.4",
|
||||
"version": "10.25.7",
|
||||
"sideEffects": false,
|
||||
"description": "A simple, expressive and safe Shopify / Github Pages compatible template engine in pure JavaScript.",
|
||||
"main": "dist/liquid.node.js",
|
||||
@@ -21,7 +21,7 @@
|
||||
"test:coverage": "jest --coverage src test/integration",
|
||||
"test:e2e": "jest test/e2e",
|
||||
"test:demo": "./test/demo/test.sh",
|
||||
"perf:diff": "bin/perf-diff.sh",
|
||||
"perf:diff": "node bin/perf-diff.js",
|
||||
"perf:engines": "cd benchmark && npm run engines",
|
||||
"version": "npm run build && npm test",
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
@@ -29,7 +29,12 @@
|
||||
"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"
|
||||
"build:docs": "run-s build:docs-liquid build:contributors build:apidoc build:changelog build:docs-hexo",
|
||||
"build:docs-liquid": "cross-env BUNDLES=min rollup -c rollup.config.mjs && shx mkdir -p docs/public/js && shx cp dist/liquid.browser.min.js docs/public/js/",
|
||||
"build:contributors": "node bin/build-contributors.js",
|
||||
"build:apidoc": "shx rm -rf docs/source/api && typedoc --plugin typedoc-plugin-missing-exports ./src --gitRevision master --out docs/source/api",
|
||||
"build:changelog": "node bin/build-changelog.js",
|
||||
"build:docs-hexo": "cd docs && npm ci && npm run build && shx cp CNAME public/"
|
||||
},
|
||||
"bin": {
|
||||
"liquidjs": "./bin/liquid.js",
|
||||
@@ -95,12 +100,14 @@
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^29.5.0",
|
||||
"jsdom": "^16.5.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"rollup": "^1.26.3",
|
||||
"rollup-plugin-replace": "^2.1.0",
|
||||
"rollup-plugin-typescript2": "^0.31.1",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"rollup-plugin-version-injector": "^1.3.3",
|
||||
"semantic-release": "^25.0.3",
|
||||
"shx": "^0.4.0",
|
||||
"sinon": "^15.0.2",
|
||||
"supertest": "^3.4.2",
|
||||
"ts-jest": "^29.0.5",
|
||||
|
||||
+10
-2
@@ -50,6 +50,11 @@ const browserBase64 = {
|
||||
delimiters: ['', ''],
|
||||
'./base64-impl': '../build/base64-impl-browser'
|
||||
}
|
||||
const browserCrypto = {
|
||||
include: './src/filters/crypto.ts',
|
||||
delimiters: ['', ''],
|
||||
'./crypto-impl': '../build/crypto-impl-browser'
|
||||
}
|
||||
const browserStream = {
|
||||
include: './src/emitters/index.ts',
|
||||
delimiters: ['', ''],
|
||||
@@ -67,7 +72,7 @@ const nodeCjs = {
|
||||
format: 'cjs',
|
||||
banner
|
||||
}],
|
||||
external: ['path', 'fs', 'stream'],
|
||||
external: ['path', 'fs', 'stream', 'crypto'],
|
||||
plugins: [versionInjection, typescript(tsconfig('ES2020'))],
|
||||
treeshake,
|
||||
input
|
||||
@@ -79,7 +84,7 @@ const nodeEsm = {
|
||||
format: 'esm',
|
||||
banner
|
||||
}],
|
||||
external: ['path', 'fs', 'stream'],
|
||||
external: ['path', 'fs', 'stream', 'crypto'],
|
||||
plugins: [
|
||||
versionInjection,
|
||||
replace(esmRequire),
|
||||
@@ -100,6 +105,7 @@ const browserEsm = {
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es6'))
|
||||
],
|
||||
@@ -119,6 +125,7 @@ const browserUmd = {
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5'))
|
||||
],
|
||||
@@ -138,6 +145,7 @@ const browserMin = {
|
||||
versionInjection,
|
||||
replace(browserFS),
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5')),
|
||||
uglify()
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { webcrypto } from 'crypto'
|
||||
import * as cryptoImpl from './crypto-impl-browser'
|
||||
|
||||
describe('crypto-impl/browser', function () {
|
||||
beforeEach(function () {
|
||||
Object.defineProperty(global, 'crypto', {
|
||||
value: webcrypto,
|
||||
writable: true,
|
||||
configurable: true
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
delete (global as any).crypto
|
||||
})
|
||||
|
||||
describe('#sha256()', function () {
|
||||
it('should hash the Shopify reference example', async function () {
|
||||
expect(await cryptoImpl.sha256('Polyjuice'))
|
||||
.toBe('44ac1d7a2936e30a5de07082fd65d6fe9b1fb658a1a98bfe65bc5959beac5dd0')
|
||||
})
|
||||
|
||||
it('should hash an empty string', async function () {
|
||||
expect(await cryptoImpl.sha256(''))
|
||||
.toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
|
||||
})
|
||||
|
||||
it('should hash unicode characters', async function () {
|
||||
expect(await cryptoImpl.sha256('你好世界'))
|
||||
.toBe('beca6335b20ff57ccc47403ef4d9e0b8fccb4442b3151c2e7d50050673d43172')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#hmacSha256()', function () {
|
||||
it('should hash the Shopify reference example', async function () {
|
||||
expect(await cryptoImpl.hmacSha256('Polyjuice', 'Polina'))
|
||||
.toBe('8e0d5d65cff1242a4af66c8f4a32854fd5fb80edcc8aabe9b302b29c7c71dc20')
|
||||
})
|
||||
|
||||
it('should hash an empty message with a key', async function () {
|
||||
expect(await cryptoImpl.hmacSha256('', 'key'))
|
||||
.toBe('5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
function bufferToHex (buffer: ArrayBuffer): string {
|
||||
const bytes = new Uint8Array(buffer)
|
||||
let hex = ''
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += bytes[i].toString(16).padStart(2, '0')
|
||||
}
|
||||
return hex
|
||||
}
|
||||
|
||||
export async function sha256 (str: string): Promise<string> {
|
||||
const data = new TextEncoder().encode(str)
|
||||
const digest = await crypto.subtle.digest('SHA-256', data)
|
||||
return bufferToHex(digest)
|
||||
}
|
||||
|
||||
export async function hmacSha256 (str: string, key: string): Promise<string> {
|
||||
const encoder = new TextEncoder()
|
||||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
encoder.encode(key),
|
||||
{ name: 'HMAC', hash: 'SHA-256' },
|
||||
false,
|
||||
['sign']
|
||||
)
|
||||
const signature = await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(str))
|
||||
return bufferToHex(signature)
|
||||
}
|
||||
@@ -48,8 +48,8 @@ export class Context {
|
||||
this.memoryLimit = memoryLimit ?? new Limiter('memory alloc', renderOptions.memoryLimit ?? opts.memoryLimit)
|
||||
this.renderLimit = renderLimit ?? new Limiter('template render', getPerformance().now() + (renderOptions.renderLimit ?? opts.renderLimit))
|
||||
}
|
||||
public getRegister (key: string) {
|
||||
return (this.registers[key] = this.registers[key] || {})
|
||||
public getRegister<T> (key: string, defaultValue: T = undefined as T): T {
|
||||
return (this.registers[key] = this.registers[key] || defaultValue)
|
||||
}
|
||||
public setRegister (key: string, value: any) {
|
||||
return (this.registers[key] = value)
|
||||
@@ -106,7 +106,8 @@ export class Context {
|
||||
return new Context(scope, this.opts, {
|
||||
sync: this.sync,
|
||||
globals: this.globals,
|
||||
strictVariables: this.strictVariables
|
||||
strictVariables: this.strictVariables,
|
||||
ownPropertyOnly: this.ownPropertyOnly
|
||||
}, {
|
||||
renderLimit: this.renderLimit,
|
||||
memoryLimit: this.memoryLimit
|
||||
|
||||
@@ -8,7 +8,11 @@ import { FilterImpl } from '../template'
|
||||
import { stringify } from '../util'
|
||||
import { base64Encode, base64Decode } from './base64-impl'
|
||||
|
||||
export function base64_encode (this: FilterImpl, value: string): string {
|
||||
export function base64_encode (this: FilterImpl, value: string | Buffer): string {
|
||||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
||||
this.context.memoryLimit.use(value.byteLength)
|
||||
return value.toString('base64')
|
||||
}
|
||||
const str = stringify(value)
|
||||
this.context.memoryLimit.use(str.length)
|
||||
return base64Encode(str)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createHash, createHmac } from 'crypto'
|
||||
|
||||
export function sha256 (str: string): string {
|
||||
return createHash('sha256').update(str, 'utf8').digest('hex')
|
||||
}
|
||||
|
||||
export function hmacSha256 (str: string, key: string): string {
|
||||
return createHmac('sha256', key).update(str, 'utf8').digest('hex')
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Crypto related filters
|
||||
*
|
||||
* Implements sha256 and hmac_sha256 filters for Shopify compatibility
|
||||
*/
|
||||
|
||||
import { FilterImpl } from '../template'
|
||||
import { stringify } from '../util'
|
||||
import { sha256 as sha256Impl, hmacSha256 as hmacSha256Impl } from './crypto-impl'
|
||||
|
||||
export function sha256 (this: FilterImpl, value: unknown): string | Promise<string> {
|
||||
const str = stringify(value)
|
||||
this.context.memoryLimit.use(str.length)
|
||||
return sha256Impl(str)
|
||||
}
|
||||
|
||||
export function hmac_sha256 (this: FilterImpl, value: unknown, key: unknown): string | Promise<string> {
|
||||
const str = stringify(value)
|
||||
const keyStr = stringify(key)
|
||||
this.context.memoryLimit.use(str.length + keyStr.length)
|
||||
return hmacSha256Impl(str, keyStr)
|
||||
}
|
||||
+3
-1
@@ -45,7 +45,9 @@ function parseDate (v: string | Date, opts: NormalizedFullOptions, timezoneOffse
|
||||
const defaultTimezoneOffset = timezoneOffset ?? opts.timezoneOffset
|
||||
const locale = opts.locale
|
||||
v = toValue(v)
|
||||
if (v === 'now' || v === 'today') {
|
||||
if (isNil(v)) {
|
||||
return undefined
|
||||
} else if (v === 'now' || v === 'today') {
|
||||
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset)
|
||||
} else if (isNumber(v)) {
|
||||
date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset)
|
||||
|
||||
+1
-1
@@ -45,5 +45,5 @@ export function newline_to_br (this: FilterImpl, v: string) {
|
||||
export function strip_html (this: FilterImpl, v: string) {
|
||||
const str = stringify(v)
|
||||
this.context.memoryLimit.use(str.length)
|
||||
return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '')
|
||||
return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<[\s\S]*?>|<!--[\s\S]*?-->/g, '')
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as arrayFilters from './array'
|
||||
import * as dateFilters from './date'
|
||||
import * as stringFilters from './string'
|
||||
import * as base64Filters from './base64'
|
||||
import * as cryptoFilters from './crypto'
|
||||
import misc from './misc'
|
||||
import { FilterImplOptions } from '../template'
|
||||
|
||||
@@ -16,5 +17,6 @@ export const filters: Record<string, FilterImplOptions> = {
|
||||
...dateFilters,
|
||||
...stringFilters,
|
||||
...base64Filters,
|
||||
...cryptoFilters,
|
||||
...misc
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,5 +15,7 @@ export function round (v: number, arg = 0) {
|
||||
v = toNumber(v)
|
||||
arg = toNumber(arg)
|
||||
const amp = Math.pow(10, arg)
|
||||
return Math.round(v * amp) / amp
|
||||
const scaled = v * amp
|
||||
// Round half away from zero
|
||||
return Math.sign(v) * Math.round(Math.abs(scaled)) / amp
|
||||
}
|
||||
|
||||
@@ -30,5 +30,11 @@ describe('fs/loader', function () {
|
||||
const result = toValueSync(loader.lookup('./foo/bar', LookupType.Partials, true, '/root/current'))
|
||||
expect(result).toBe(resolve('/root/foo/bar'))
|
||||
})
|
||||
it('should enforce containment for LookupType.Root', function () {
|
||||
const mockFs = { ...fs, existsSync: () => true, exists: async () => true }
|
||||
const loader = new Loader({ relativeReference: false, fs: mockFs, extname: '', root: ['/safe'] } as any)
|
||||
expect(() => toValueSync(loader.lookup('/etc/hosts', LookupType.Root, true)))
|
||||
.toThrow(/ENOENT/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+4
-7
@@ -43,15 +43,12 @@ export class Loader {
|
||||
|
||||
public * lookup (file: string, type: LookupType, sync?: boolean, currentFile?: string): Generator<unknown, string, string> {
|
||||
const dirs = this.options[type]
|
||||
const enforceRoot = type !== LookupType.Root
|
||||
for (const filepath of this.candidates(file, dirs, currentFile)) {
|
||||
if (enforceRoot) {
|
||||
let allowed = false
|
||||
for (const dir of dirs) {
|
||||
if (yield this.contains(!!sync, dir, filepath)) { allowed = true; break }
|
||||
}
|
||||
if (!allowed) continue
|
||||
let allowed = false
|
||||
for (const dir of dirs) {
|
||||
if (yield this.contains(!!sync, dir, filepath)) { allowed = true; break }
|
||||
}
|
||||
if (!allowed) continue
|
||||
if (yield this.exists(!!sync, filepath)) return filepath
|
||||
}
|
||||
throw this.lookupError(file, dirs)
|
||||
|
||||
@@ -15,6 +15,7 @@ export class Render {
|
||||
if (!emitter) {
|
||||
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter()
|
||||
}
|
||||
ctx.renderLimit.check(getPerformance().now())
|
||||
const errors = []
|
||||
for (const tpl of templates) {
|
||||
ctx.renderLimit.check(getPerformance().now())
|
||||
|
||||
+8
-3
@@ -23,20 +23,25 @@ export default class extends Tag {
|
||||
* render (ctx: Context, emitter: Emitter) {
|
||||
const blockRender = this.getBlockRender(ctx)
|
||||
if (ctx.getRegister('blockMode') === BlockMode.STORE) {
|
||||
ctx.getRegister('blocks')[this.block] = blockRender
|
||||
ctx.getRegister('blocks', {} as Record<string, any>)[this.block] = blockRender
|
||||
} else {
|
||||
yield blockRender(new BlockDrop(), emitter)
|
||||
}
|
||||
}
|
||||
|
||||
private getBlockRender (ctx: Context) {
|
||||
const self = this as Tag
|
||||
const { liquid, templates } = this
|
||||
const renderChild = ctx.getRegister('blocks')[this.block]
|
||||
const renderChild = ctx.getRegister('blocks', {} as Record<string, any>)[this.block]
|
||||
const renderCurrent = function * (superBlock: BlockDrop, emitter: Emitter) {
|
||||
// add {{ block.super }} support when rendering
|
||||
const stack: Tag[] = ctx.getRegister('blockStack', [])
|
||||
if (stack.includes(self)) throw new Error('block tag cannot be nested')
|
||||
|
||||
stack.push(self)
|
||||
ctx.push({ block: superBlock })
|
||||
yield liquid.renderer.renderTemplates(templates, ctx, emitter)
|
||||
ctx.pop()
|
||||
stack.pop()
|
||||
}
|
||||
return renderChild
|
||||
? (superBlock: BlockDrop, emitter: Emitter) => renderChild(
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ export default class extends Tag {
|
||||
* render (ctx: Context, emitter: Emitter): Generator<unknown, unknown, unknown> {
|
||||
const group = (yield evalToken(this.group, ctx)) as ValueToken
|
||||
const fingerprint = `cycle:${group}:` + this.candidates.join(',')
|
||||
const groups = ctx.getRegister('cycle')
|
||||
const groups = ctx.getRegister('cycle', {} as Record<string, number>)
|
||||
let idx = groups[fingerprint]
|
||||
|
||||
if (idx === undefined) {
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export default class extends Tag {
|
||||
}
|
||||
|
||||
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText()
|
||||
ctx.push({ continue: ctx.getRegister(continueKey) })
|
||||
ctx.push({ continue: ctx.getRegister(continueKey, {}) })
|
||||
const hash = yield this.hash.render(ctx)
|
||||
ctx.pop()
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ export default class extends Tag {
|
||||
// render remaining contents and store rendered results
|
||||
ctx.setRegister('blockMode', BlockMode.STORE)
|
||||
const html = yield renderer.renderTemplates(this.templates, ctx)
|
||||
const blocks = ctx.getRegister('blocks')
|
||||
const blocks = ctx.getRegister('blocks', {} as Record<string, any>)
|
||||
|
||||
// set whole content to anonymous block if anonymous doesn't specified
|
||||
if (blocks[''] === undefined) blocks[''] = (parent: BlankDrop, emitter: Emitter) => emitter.write(html)
|
||||
|
||||
@@ -82,4 +82,39 @@ describe('.parseAndRender()', function () {
|
||||
expect(() => e.parseAndRenderSync('{% render "link" %}')).toThrow(/ENOENT|Failed to lookup/)
|
||||
})
|
||||
})
|
||||
describe('layout: nested {% block %} regression', function () {
|
||||
let root: string
|
||||
beforeEach(function () {
|
||||
root = mkdtempSync(join(tmpdir(), 'liquid-e2e-layout-nested-'))
|
||||
})
|
||||
afterEach(function () {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
})
|
||||
it('should reject same-name {% block %} nested in child template (no hang / OOM)', async function () {
|
||||
writeFileSync(
|
||||
join(root, 'layout.html'),
|
||||
'<header>{% block a %}default-a{% endblock %}</header>' +
|
||||
'<main>{% block b %}default-b{% endblock %}</main>' +
|
||||
'<footer>{% block c %}default-c{% endblock %}</footer>'
|
||||
)
|
||||
writeFileSync(
|
||||
join(root, 'template.html'),
|
||||
'{% layout "layout" %}' +
|
||||
'{% block a %}outer-a {% block a %}inner-a{% endblock %}{% endblock %}' +
|
||||
'{% block b %}content-b{% endblock %}' +
|
||||
'{% block c %}content-c{% endblock %}'
|
||||
)
|
||||
const liquid = new Liquid({ root, extname: '.html' })
|
||||
await expect(liquid.renderFile('template')).rejects.toThrow(/block tag cannot be nested/)
|
||||
})
|
||||
it('should reject nested anonymous {% block %} in child template (no hang / OOM)', async function () {
|
||||
writeFileSync(join(root, 'parent.html'), 'X{%block%}{%endblock%}Y')
|
||||
writeFileSync(
|
||||
join(root, 'template.html'),
|
||||
'{% layout "parent" %}{%block%}A{%block%}B{%endblock%}{%endblock%}'
|
||||
)
|
||||
const liquid = new Liquid({ root, extname: '.html' })
|
||||
await expect(liquid.renderFile('template')).rejects.toThrow(/block tag cannot be nested/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -38,6 +38,7 @@ describe('#renderFile()', function () {
|
||||
return expect(html).toContain('"name": "liquidjs"')
|
||||
})
|
||||
it('should render file with context', async function () {
|
||||
engine = new Liquid({ root: views, extname: '.html' })
|
||||
const html = await engine.renderFile(resolve(views, 'name.html'), { name: 'harttle' })
|
||||
return expect(html).toBe('My name is harttle.')
|
||||
})
|
||||
|
||||
@@ -4,8 +4,8 @@ import { drainStream } from '../stub/stream'
|
||||
describe('.renderToNodeStream()', function () {
|
||||
it('should render to stream in Node.js', done => {
|
||||
const cjs = require('../../dist/liquid.node')
|
||||
const engine = new cjs.Liquid()
|
||||
const tpl = engine.parseFileSync(resolve(__dirname, '../stub/root/foo.html'))
|
||||
const engine = new cjs.Liquid({ root: resolve(__dirname, '../stub/root/') })
|
||||
const tpl = engine.parseFileSync('foo.html')
|
||||
const stream = engine.renderToNodeStream(tpl)
|
||||
let html = ''
|
||||
stream.on('data', (data: string) => { html += data })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test } from '../../stub/render'
|
||||
import { test, liquid } from '../../stub/render'
|
||||
|
||||
describe('filters/base64', function () {
|
||||
describe('base64_encode', function () {
|
||||
@@ -65,6 +65,33 @@ describe('filters/base64', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('base64_encode with Buffer input', function () {
|
||||
it('should encode a Buffer to base64 without data corruption', async () => {
|
||||
const buf = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xff, 0xfe])
|
||||
const result = await liquid.parseAndRender('{{ data | base64_encode }}', { data: buf })
|
||||
expect(result).toBe(buf.toString('base64'))
|
||||
})
|
||||
|
||||
it('should preserve bytes that are invalid UTF-8', async () => {
|
||||
const buf = Buffer.from([0x80, 0xff, 0xfe, 0x00, 0x01])
|
||||
const result = await liquid.parseAndRender('{{ data | base64_encode }}', { data: buf })
|
||||
const decoded = Buffer.from(result, 'base64')
|
||||
expect(decoded).toEqual(buf)
|
||||
})
|
||||
|
||||
it('should handle an empty Buffer', async () => {
|
||||
const buf = Buffer.alloc(0)
|
||||
const result = await liquid.parseAndRender('{{ data | base64_encode }}', { data: buf })
|
||||
expect(result).toBe('')
|
||||
})
|
||||
|
||||
it('should handle a Buffer containing valid UTF-8 text', async () => {
|
||||
const buf = Buffer.from('Hello World', 'utf8')
|
||||
const result = await liquid.parseAndRender('{{ data | base64_encode }}', { data: buf })
|
||||
expect(result).toBe(Buffer.from('Hello World').toString('base64'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('base64 round-trip', function () {
|
||||
it('should encode and decode back to original', () => {
|
||||
return test('{{ "Hello, World!" | base64_encode | base64_decode }}', 'Hello, World!')
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { test } from '../../stub/render'
|
||||
|
||||
const SHA256_EMPTY = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
||||
|
||||
describe('filters/crypto', function () {
|
||||
describe('sha256', function () {
|
||||
it('should hash the Shopify reference example', () => {
|
||||
return test(
|
||||
'{{ "Polyjuice" | sha256 }}',
|
||||
'44ac1d7a2936e30a5de07082fd65d6fe9b1fb658a1a98bfe65bc5959beac5dd0'
|
||||
)
|
||||
})
|
||||
|
||||
it('should hash an empty string', () => {
|
||||
return test('{{ "" | sha256 }}', SHA256_EMPTY)
|
||||
})
|
||||
|
||||
it('should treat undefined as empty string', () => {
|
||||
return test('{{ foo | sha256 }}', SHA256_EMPTY)
|
||||
})
|
||||
|
||||
it('should treat null as empty string', () => {
|
||||
return test('{{ null | sha256 }}', SHA256_EMPTY)
|
||||
})
|
||||
|
||||
it('should stringify numeric input', () => {
|
||||
return test(
|
||||
'{{ 123 | sha256 }}',
|
||||
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
||||
)
|
||||
})
|
||||
|
||||
it('should stringify boolean input', () => {
|
||||
return test(
|
||||
'{{ true | sha256 }}',
|
||||
'b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hmac_sha256', function () {
|
||||
it('should hash the Shopify reference example', () => {
|
||||
return test(
|
||||
"{{ 'Polyjuice' | hmac_sha256: 'Polina' }}",
|
||||
'8e0d5d65cff1242a4af66c8f4a32854fd5fb80edcc8aabe9b302b29c7c71dc20'
|
||||
)
|
||||
})
|
||||
|
||||
it('should accept a numeric key (stringified)', () => {
|
||||
return test(
|
||||
"{{ 'hello' | hmac_sha256: 42 }}",
|
||||
'3bdadea6ed0e95ededc15dc4421ce7654c970156843dfd997be3fef5358168ca'
|
||||
)
|
||||
})
|
||||
|
||||
it('should hash an empty message with an empty key', () => {
|
||||
return test(
|
||||
"{{ '' | hmac_sha256: '' }}",
|
||||
'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'
|
||||
)
|
||||
})
|
||||
|
||||
it('should treat undefined input as empty string', () => {
|
||||
return test(
|
||||
"{{ foo | hmac_sha256: '' }}",
|
||||
'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -21,8 +21,12 @@ describe('filters/date', function () {
|
||||
const time = String(new Date('2017-03-07T12:00:00').getTime() / 1000)
|
||||
return test('{{ time | date: "%Y-%m-%dT%H:%M:%S" }}', { time }, '2017-03-07T12:00:00')
|
||||
})
|
||||
it('should treat nil as 0', () => {
|
||||
expect(liquid.parseAndRenderSync('{{ nil | date: "%Y-%m-%dT%H:%M:%S", "Asia/Shanghai" }}')).toEqual('1970-01-01T08:00:00')
|
||||
it('should treat null as invalid', () => {
|
||||
const time = null
|
||||
return test('{{ time | date: "%Y-%m-%dT%H:%M:%S" }}', { time }, '')
|
||||
})
|
||||
it('should treat nil as invalid', () => {
|
||||
expect(liquid.parseAndRenderSync('{{ nil | date: "%Y-%m-%dT%H:%M:%S", "Asia/Shanghai" }}')).toEqual('')
|
||||
})
|
||||
it('should treat undefined as invalid', () => {
|
||||
expect(liquid.parseAndRenderSync('{{ num | date: "%Y-%m-%dT%H:%M:%S", "Asia/Shanghai" }}', { num: undefined })).toEqual('')
|
||||
|
||||
@@ -77,5 +77,10 @@ describe('filters/html', function () {
|
||||
it('should strip until empty', function () {
|
||||
return test('{{"<br/><br />< p ></p></ p >" | strip_html }}', '')
|
||||
})
|
||||
it('should strip generic tags spanning ASCII newlines inside the tag', function () {
|
||||
expect(liquid.parseAndRenderSync('{{"<img\nsrc=x\nonerror=alert(1)>" | strip_html}}')).toBe('')
|
||||
expect(liquid.parseAndRenderSync('{{"<img\rsrc=x\ronerror=alert(1)>" | strip_html}}')).toBe('')
|
||||
expect(liquid.parseAndRenderSync('{{"<svg\nonload=alert(1)>" | strip_html}}')).toBe('')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,6 +65,8 @@ describe('filters/math', function () {
|
||||
describe('round', function () {
|
||||
it('should return "1" for 1.2', () => test('{{1.2|round}}', '1'))
|
||||
it('should return "3" for 2.7', () => test('{{2.7|round}}', '3'))
|
||||
it('should return "-3" for -2.5 (away from zero)', () => test('{{num|round}}', { num: -2.5 }, '-3'))
|
||||
it('should return "-2" for -2.49 (closest integer)', () => test('{{num|round}}', { num: -2.49 }, '-2'))
|
||||
it('should return "183.36" for 183.357,2',
|
||||
() => test('{{183.357|round: 2}}', '183.36'))
|
||||
it('should convert string to number', () => test('{{"2.7"|round}}', '3'))
|
||||
|
||||
@@ -48,6 +48,16 @@ describe('DoS related', function () {
|
||||
await expect(liquid.parseAndRender('{% render "large" %}')).rejects.toThrow('template render limit exceeded')
|
||||
await expect(liquid.parseAndRender('{% render "small" %}')).resolves.toBe('12345')
|
||||
})
|
||||
it('should enforce renderLimit when for body has no template nodes', () => {
|
||||
const liquid = new Liquid({ memoryLimit: 1e9, renderLimit: 1 })
|
||||
expect(() => liquid.parseAndRenderSync('{%- for i in (1..5000000) -%}{%- endfor -%}', {}))
|
||||
.toThrow('template render limit exceeded')
|
||||
})
|
||||
it('should enforce renderLimit when tablerow body has no template nodes', () => {
|
||||
const liquid = new Liquid({ memoryLimit: 1e9, renderLimit: 1 })
|
||||
expect(() => liquid.parseAndRenderSync('{%- tablerow i in (1..1000000) cols:1 -%}{%- endtablerow -%}', {}))
|
||||
.toThrow('template render limit exceeded')
|
||||
})
|
||||
})
|
||||
describe('#memoryLimit', () => {
|
||||
it('should throw for too many array creation in filters', async () => {
|
||||
|
||||
@@ -109,6 +109,7 @@ describe('Liquid', function () {
|
||||
})
|
||||
})
|
||||
describe('#renderFile', function () {
|
||||
afterEach(restore)
|
||||
it('should throw with lookup list when file not exist', function () {
|
||||
const engine = new Liquid({
|
||||
root: ['/boo', '/root/'],
|
||||
@@ -116,6 +117,22 @@ describe('Liquid', function () {
|
||||
})
|
||||
return expect(engine.renderFile('/not/exist.html')).rejects.toThrow(/Failed to lookup "\/not\/exist.html" in "\/boo,\/root\/"/)
|
||||
})
|
||||
it('should reject absolute paths outside root', async function () {
|
||||
mock({
|
||||
'/safe/foo.html': 'safe',
|
||||
'/etc/secret': 'SECRET'
|
||||
})
|
||||
const engine = new Liquid({ root: ['/safe'] })
|
||||
await expect(engine.renderFile('/etc/secret')).rejects.toThrow(/Failed to lookup/)
|
||||
})
|
||||
it('should reject absolute paths outside root (sync)', function () {
|
||||
mock({
|
||||
'/safe/foo.html': 'safe',
|
||||
'/etc/secret': 'SECRET'
|
||||
})
|
||||
const engine = new Liquid({ root: ['/safe'] })
|
||||
expect(() => engine.renderFileSync('/etc/secret')).toThrow(/Failed to lookup/)
|
||||
})
|
||||
})
|
||||
describe('#parseFile', function () {
|
||||
it('should throw with lookup list when file not exist', function () {
|
||||
@@ -127,7 +144,7 @@ describe('Liquid', function () {
|
||||
})
|
||||
it('should fallback to require.resolve in Node.js', async function () {
|
||||
const engine = new Liquid({
|
||||
root: ['/root/'],
|
||||
root: [process.cwd()],
|
||||
extname: '.html'
|
||||
})
|
||||
const tpls = await engine.parseFileSync('jest')
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('tags/include', function () {
|
||||
})
|
||||
return liquid.renderFile('/parent.html').catch(function (e) {
|
||||
expect(e.name).toBe('TokenizationError')
|
||||
expect(e.message).toMatch('illegal file path, file:/parent.html, line:1, col:11')
|
||||
expect(e.message).toMatch(/illegal file path, file:.*parent.html, line:1, col:11/)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -166,6 +166,27 @@ describe('tags/layout', function () {
|
||||
const html = await liquid.renderFile('/main.html')
|
||||
return expect(html).toBe('XAY')
|
||||
})
|
||||
it('should reject nested {% block %} with the same name (no OOM / hang)', function () {
|
||||
mock({
|
||||
'/layout.html':
|
||||
'<header>{% block a %}default-a{% endblock %}</header>' +
|
||||
'<main>{% block b %}default-b{% endblock %}</main>' +
|
||||
'<footer>{% block c %}default-c{% endblock %}</footer>',
|
||||
'/template.html':
|
||||
'{% layout "layout" %}' +
|
||||
'{% block a %}outer-a {% block a %}inner-a{% endblock %}{% endblock %}' +
|
||||
'{% block b %}content-b{% endblock %}' +
|
||||
'{% block c %}content-c{% endblock %}'
|
||||
})
|
||||
return expect(liquid.renderFile('/template.html')).rejects.toThrow(/block tag cannot be nested/)
|
||||
})
|
||||
it('should reject nested anonymous {% block %} (no OOM / hang)', function () {
|
||||
mock({
|
||||
'/parent.html': 'X{%block%}{%endblock%}Y'
|
||||
})
|
||||
const src = '{% layout "parent.html" %}{%block%}A{%block%}B{%endblock%}{%endblock%}'
|
||||
return expect(liquid.parseAndRender(src)).rejects.toThrow(/block tag cannot be nested/)
|
||||
})
|
||||
it('should not bleed scope into `include` layout', async function () {
|
||||
mock({
|
||||
'/parent.html': 'X{%block a%}{%endblock%}Y{%block b%}{%endblock%}Z',
|
||||
|
||||
@@ -271,6 +271,27 @@ describe('tags/render', function () {
|
||||
return expect(staticLiquid.renderFile('parent.html')).rejects.toThrow(/Failed to lookup "..\/bar\/child.html"/)
|
||||
})
|
||||
|
||||
describe('per-render ownPropertyOnly', function () {
|
||||
it('should propagate to {% render %} partial (spawned context)', async function () {
|
||||
mock({
|
||||
'/_user.liquid': '{{ user.passwordHash }}'
|
||||
})
|
||||
const engine = new Liquid({ ownPropertyOnly: false, root: '/' })
|
||||
class User {
|
||||
name: string
|
||||
constructor (n: string) {
|
||||
this.name = n
|
||||
}
|
||||
}
|
||||
Object.assign(User.prototype, { passwordHash: 'secret-from-prototype' })
|
||||
const u = new User('alice')
|
||||
const tpl = 'Direct:[{{ user.passwordHash }}] Render:[{% render "_user.liquid", user: user %}]'
|
||||
const html = await engine.parseAndRender(tpl, { user: u }, { ownPropertyOnly: true })
|
||||
expect(html).toBe('Direct:[] Render:[]')
|
||||
expect(engine.parseAndRenderSync(tpl, { user: u }, { ownPropertyOnly: true })).toBe('Direct:[] Render:[]')
|
||||
})
|
||||
})
|
||||
|
||||
describe('static partial', function () {
|
||||
let staticLiquid: Liquid
|
||||
beforeEach(() => {
|
||||
|
||||
Reference in New Issue
Block a user