mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-12 19:00:39 -07:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6111eb6bd9 | ||
|
|
9481008f2b | ||
|
|
cc4a9ce0a7 | ||
|
|
5e3928654b | ||
|
|
962e5b6433 |
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: 'latest'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '22'
|
||||
node-version: 'latest'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Test
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: 'latest'
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: 'latest'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Lint
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: 'latest'
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -24,12 +24,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
if [ ${{ github.ref == 'refs/heads/master' }} ]; then
|
||||
npx semantic-release
|
||||
else
|
||||
npx semantic-release --dry-run
|
||||
fi
|
||||
run: npx semantic-release
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
|
||||
@@ -7,20 +7,10 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
timezone: [Etc/GMT, Asia/Shanghai, America/New_York]
|
||||
node-version: [22]
|
||||
node-version: [latest]
|
||||
include:
|
||||
- os: macos-latest
|
||||
timezone: America/New_York
|
||||
node-versoin: 22
|
||||
- os: ubuntu-latest
|
||||
timezone: Etc/GMT
|
||||
node-version: 20
|
||||
- os: ubuntu-latest
|
||||
timezone: Asia/Shanghai
|
||||
node-version: 18
|
||||
- os: ubuntu-latest
|
||||
timezone: Asia/Shanghai
|
||||
node-version: 16
|
||||
node-version: lts/*
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -39,7 +29,7 @@ jobs:
|
||||
name: dist-${{ matrix.os }}
|
||||
path: dist
|
||||
- name: Run Test
|
||||
run: TZ=${{ matrix.timezone }} npm test
|
||||
run: TZ=${{ matrix.timezone || 'Etc/GMT' }} npm test
|
||||
- name: Archive npm failure logs
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
@@ -57,7 +47,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: latest
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
const gitPlugin = [
|
||||
'@semantic-release/git',
|
||||
{
|
||||
assets: ['package.json', 'package-lock.json', 'CHANGELOG.md'],
|
||||
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
|
||||
}
|
||||
]
|
||||
|
||||
const githubPlugin = [
|
||||
'@semantic-release/github',
|
||||
{
|
||||
assets: [
|
||||
{ path: 'dist/*.umd.js', label: 'liquid.js' },
|
||||
{ path: 'dist/*.min.js', label: 'liquid.min.js' },
|
||||
{ path: 'dist/*.min.js.map', label: 'liquid.min.js.map' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// next is branch-protected (PR-only); skip @semantic-release/git there and publish to npm only.
|
||||
const onMaster = process.env.GITHUB_REF === 'refs/heads/master'
|
||||
const onNext = process.env.GITHUB_REF === 'refs/heads/next'
|
||||
|
||||
// On next, breaking changes are v11 WIP — bump alpha prerelease only, not major.
|
||||
const commitAnalyzer = onNext
|
||||
? ['@semantic-release/commit-analyzer', {
|
||||
releaseRules: [
|
||||
{ breaking: true, release: 'patch' }
|
||||
]
|
||||
}]
|
||||
: '@semantic-release/commit-analyzer'
|
||||
|
||||
const basePlugins = [
|
||||
commitAnalyzer,
|
||||
'@semantic-release/release-notes-generator',
|
||||
'@semantic-release/changelog',
|
||||
'@semantic-release/npm'
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
branches: [
|
||||
'master',
|
||||
{ name: 'next', prerelease: 'alpha' }
|
||||
],
|
||||
plugins: [
|
||||
...basePlugins,
|
||||
...(onMaster ? [gitPlugin] : []),
|
||||
githubPlugin
|
||||
]
|
||||
}
|
||||
@@ -7,13 +7,13 @@ while ! grep -q "Express running" "$LOG_FILE"; do
|
||||
if ! kill -0 $SERVER_PID; then
|
||||
echo "Server exited unexpectedly."
|
||||
cat $LOG_FILE
|
||||
return 1
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
curl http://127.0.0.1:3000 | grep -q 'Welcome to LiquidJS'
|
||||
RESULT=$?
|
||||
killall node
|
||||
killall node || true
|
||||
rm $LOG_FILE
|
||||
if [ $RESULT != 0 ]; then
|
||||
exit 1
|
||||
|
||||
Generated
+1
-1
@@ -64,7 +64,7 @@
|
||||
"typescript": "^4.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
||||
+1
-40
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint \"**/*.mjs\" \"**/*.ts\" .",
|
||||
@@ -121,45 +121,6 @@
|
||||
"dependencies": {
|
||||
"commander": "^10.0.0"
|
||||
},
|
||||
"release": {
|
||||
"branch": "master",
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"@semantic-release/npm",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
"assets": [
|
||||
{
|
||||
"path": "dist/*.umd.js",
|
||||
"label": "liquid.js"
|
||||
},
|
||||
{
|
||||
"path": "dist/*.min.js",
|
||||
"label": "liquid.min.js"
|
||||
},
|
||||
{
|
||||
"path": "dist/*.min.js.map",
|
||||
"label": "liquid.min.js.map"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
},
|
||||
|
||||
+5
-6
@@ -23,8 +23,7 @@ const tsconfig = (target) => ({
|
||||
compilerOptions: {
|
||||
target,
|
||||
module: 'ES2015',
|
||||
rootDir: 'src',
|
||||
downlevelIteration: true
|
||||
rootDir: 'src'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -89,7 +88,7 @@ const nodeEsm = {
|
||||
plugins: [
|
||||
versionInjection,
|
||||
replace(esmRequire),
|
||||
typescript(tsconfig('es6'))
|
||||
typescript(tsconfig('ES2020'))
|
||||
],
|
||||
treeshake,
|
||||
input
|
||||
@@ -108,7 +107,7 @@ const browserEsm = {
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es6'))
|
||||
typescript(tsconfig('ES2020'))
|
||||
],
|
||||
treeshake,
|
||||
input
|
||||
@@ -128,7 +127,7 @@ const browserUmd = {
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5'))
|
||||
typescript(tsconfig('ES2020'))
|
||||
],
|
||||
treeshake,
|
||||
input
|
||||
@@ -148,7 +147,7 @@ const browserMin = {
|
||||
replace(browserBase64),
|
||||
replace(browserCrypto),
|
||||
replace(browserStream),
|
||||
typescript(tsconfig('es5')),
|
||||
typescript(tsconfig('ES2020')),
|
||||
uglify()
|
||||
],
|
||||
treeshake,
|
||||
|
||||
@@ -2,11 +2,6 @@ import * as base64 from './base64-impl-browser'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
describe('base64-impl/browser', function () {
|
||||
if (+(process.version.match(/^v(\d+)/) as RegExpMatchArray)[1] < 8) {
|
||||
console.info('jsdom not supported, skipping base64-impl-browser...')
|
||||
return
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
const dom = new JSDOM(``, {
|
||||
url: 'https://example.com/',
|
||||
|
||||
@@ -3,10 +3,6 @@ import * as sinon from 'sinon'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
describe('fs/browser', function () {
|
||||
if (+(process.version.match(/^v(\d+)/) as RegExpMatchArray)[1] < 8) {
|
||||
console.info('jsdom not supported, skipping template-browser...')
|
||||
return
|
||||
}
|
||||
beforeEach(function () {
|
||||
const dom = new JSDOM(``, {
|
||||
url: 'https://example.com/foo/bar/',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './emitter'
|
||||
export * from './simple-emitter'
|
||||
export * from './streamed-emitter'
|
||||
export * from './keeping-type-emitter'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { stringify, toValue } from '../util'
|
||||
import { Emitter } from './emitter'
|
||||
|
||||
export class KeepingTypeEmitter implements Emitter {
|
||||
public buffer: any = '';
|
||||
|
||||
public write (html: any) {
|
||||
html = toValue(html)
|
||||
// This will only preserve the type if the value is isolated.
|
||||
// I.E:
|
||||
// {{ my-port }} -> 42
|
||||
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
||||
if (typeof html !== 'string' && this.buffer === '') {
|
||||
this.buffer = html
|
||||
} else {
|
||||
this.buffer = stringify(this.buffer) + stringify(html)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export function * sort_natural<T> (this: FilterImpl, arr: T[], property?: string
|
||||
return yield * sortBy.call(this, arr, property, caseInsensitiveCompare)
|
||||
}
|
||||
|
||||
export const size = (v: string | any[]) => (v && v.length) || 0
|
||||
export const size = (v: string | any[]) => v?.length || 0
|
||||
|
||||
export function * map (this: FilterImpl, arr: Scope[], property: string): IterableIterator<unknown> {
|
||||
const results = []
|
||||
|
||||
@@ -79,8 +79,6 @@ export interface LiquidOptions {
|
||||
templates?: {[key: string]: string};
|
||||
/** the global scope passed down to all partial and layout templates, i.e. templates included by `include`, `layout` and `render` tags. */
|
||||
globals?: object;
|
||||
/** Whether or not to keep value type when writing the Output, not working for streamed rendering. Defaults to `false`. */
|
||||
keepOutputType?: boolean;
|
||||
/** Default escape filter applied to output values, when set, you'll have to add `| raw` for values don't need to be escaped. Defaults to `undefined`. */
|
||||
outputEscape?: OutputEscapeOption;
|
||||
/** An object of operators for conditional statements. Defaults to the regular Liquid operators. */
|
||||
@@ -160,7 +158,6 @@ export interface NormalizedFullOptions extends NormalizedOptions {
|
||||
preserveTimezones: boolean;
|
||||
greedy: boolean;
|
||||
globals: object;
|
||||
keepOutputType: boolean;
|
||||
operators: Operators;
|
||||
parseLimit: number;
|
||||
renderLimit: number;
|
||||
@@ -196,7 +193,6 @@ export const defaultOptions: NormalizedFullOptions = {
|
||||
ownPropertyOnly: true,
|
||||
lenientIf: false,
|
||||
globals: {},
|
||||
keepOutputType: false,
|
||||
operators: defaultOperators,
|
||||
memoryLimit: Infinity,
|
||||
parseLimit: Infinity,
|
||||
@@ -204,11 +200,11 @@ export const defaultOptions: NormalizedFullOptions = {
|
||||
}
|
||||
|
||||
export function normalize (options: LiquidOptions): NormalizedFullOptions {
|
||||
if (options.hasOwnProperty('root')) {
|
||||
if (!options.hasOwnProperty('partials')) options.partials = options.root
|
||||
if (!options.hasOwnProperty('layouts')) options.layouts = options.root
|
||||
if ('root' in options) {
|
||||
if (!('partials' in options)) options.partials = options.root
|
||||
if (!('layouts' in options)) options.layouts = options.root
|
||||
}
|
||||
if (options.hasOwnProperty('cache')) {
|
||||
if ('cache' in options) {
|
||||
let cache: LiquidCache | undefined
|
||||
if (typeof options.cache === 'number') cache = options.cache > 0 ? new LRU(options.cache) : undefined
|
||||
else if (typeof options.cache === 'object') cache = options.cache
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { Context } from './context'
|
||||
import { toPromise, toValueSync, isFunction, forOwn, isString, strictUniq } from './util'
|
||||
import { TagClass, createTagClass, TagImplOptions, FilterImplOptions, Template, Value, StaticAnalysisOptions, StaticAnalysis, analyze, analyzeSync, SegmentArray } from './template'
|
||||
import { toPromise, toValueSync, forOwn, isString, strictUniq } from './util'
|
||||
import { TagClass, FilterImplOptions, Template, Value, StaticAnalysisOptions, StaticAnalysis, analyze, analyzeSync, SegmentArray } from './template'
|
||||
import { LookupType } from './fs/loader'
|
||||
import { Render } from './render'
|
||||
import { Parser } from './parser'
|
||||
@@ -101,8 +101,8 @@ export class Liquid {
|
||||
public registerFilter (name: string, filter: FilterImplOptions) {
|
||||
this.filters[name] = filter
|
||||
}
|
||||
public registerTag (name: string, tag: TagClass | TagImplOptions) {
|
||||
this.tags[name] = isFunction(tag) ? tag : createTagClass(tag)
|
||||
public registerTag (name: string, tag: TagClass) {
|
||||
this.tags[name] = tag
|
||||
}
|
||||
public plugin (plugin: (this: Liquid, L: typeof Liquid) => void) {
|
||||
return plugin.call(this, Liquid)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getPerformance } from '../util/performance'
|
||||
import { toPromise, RenderError, LiquidErrors, LiquidError } from '../util'
|
||||
import { Context } from '../context'
|
||||
import { Template } from '../template'
|
||||
import { Emitter, KeepingTypeEmitter, StreamedEmitter, SimpleEmitter } from '../emitters'
|
||||
import { Emitter, StreamedEmitter, SimpleEmitter } from '../emitters'
|
||||
|
||||
export class Render {
|
||||
public renderTemplatesToNodeStream (templates: Template[], ctx: Context): NodeJS.ReadableStream {
|
||||
@@ -13,7 +13,7 @@ export class Render {
|
||||
}
|
||||
public * renderTemplates (templates: Template[], ctx: Context, emitter?: Emitter): IterableIterator<any> {
|
||||
if (!emitter) {
|
||||
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter()
|
||||
emitter = new SimpleEmitter()
|
||||
}
|
||||
ctx.renderLimit.check(getPerformance().now())
|
||||
const errors = []
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export * from './template'
|
||||
export * from './template-impl'
|
||||
export * from './tag'
|
||||
export * from './tag-options-adapter'
|
||||
export * from './filter'
|
||||
export * from './filter-impl-options'
|
||||
export * from './hash'
|
||||
|
||||
@@ -2,7 +2,6 @@ import { toPromise } from '../util'
|
||||
import { Context } from '../context'
|
||||
import { Output } from '../template'
|
||||
import { OutputToken } from '../tokens'
|
||||
import { defaultOptions } from '../liquid-options'
|
||||
|
||||
describe('Output', function () {
|
||||
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
|
||||
@@ -30,59 +29,4 @@ describe('Output', function () {
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toBe('FOO')
|
||||
})
|
||||
it('should respect to .toString()', async () => {
|
||||
const scope = new Context({ obj: { toString: () => 'FOO' } })
|
||||
const output = new Output(token, liquid)
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toBe('FOO')
|
||||
})
|
||||
describe('when keepOutputType is enabled', () => {
|
||||
const emitter: any = {
|
||||
write: (html: any) => {
|
||||
if (emitter.keepOutputType && typeof html !== 'string') {
|
||||
emitter.html = html
|
||||
} else {
|
||||
emitter.html += html as string
|
||||
}
|
||||
},
|
||||
html: '',
|
||||
keepOutputType: true
|
||||
}
|
||||
const token = { content: 'foo', input: 'foo' } as OutputToken
|
||||
|
||||
beforeEach(() => { emitter.html = '' })
|
||||
|
||||
it('should respect output variable number type', async () => {
|
||||
const scope = new Context({
|
||||
foo: 42
|
||||
}, { ...defaultOptions, keepOutputType: true })
|
||||
const output = new Output(token, liquid)
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toBe(42)
|
||||
})
|
||||
it('should respect output variable boolean type', async () => {
|
||||
const scope = new Context({
|
||||
foo: true
|
||||
}, { ...defaultOptions, keepOutputType: true })
|
||||
const output = new Output(token, liquid)
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toBe(true)
|
||||
})
|
||||
it('should respect output variable object type', async () => {
|
||||
const scope = new Context({
|
||||
foo: 'test'
|
||||
}, { ...defaultOptions, keepOutputType: true })
|
||||
const output = new Output(token, liquid)
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toBe('test')
|
||||
})
|
||||
it('should respect output variable string type', async () => {
|
||||
const scope = new Context({
|
||||
foo: { a: { b: 42 } }
|
||||
}, { ...defaultOptions, keepOutputType: true })
|
||||
const output = new Output(token, liquid)
|
||||
await toPromise(output.render(scope, emitter))
|
||||
return expect(emitter.html).toEqual({ a: { b: 42 } })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { isFunction } from '../util'
|
||||
import { Hash } from './hash'
|
||||
import { Tag, TagClass, TagRenderReturn } from './tag'
|
||||
import { TagToken, TopLevelToken } from '../tokens'
|
||||
import { Emitter } from '../emitters'
|
||||
import { Context } from '../context'
|
||||
import type { Liquid } from '../liquid'
|
||||
|
||||
export interface TagImplOptions {
|
||||
[key: string]: any
|
||||
parse?: (this: Tag & TagImplOptions, token: TagToken, remainingTokens: TopLevelToken[]) => void;
|
||||
render: (this: Tag & TagImplOptions, ctx: Context, emitter: Emitter, hash: Record<string, any>) => TagRenderReturn;
|
||||
}
|
||||
|
||||
export function createTagClass (options: TagImplOptions): TagClass {
|
||||
return class extends Tag {
|
||||
constructor (token: TagToken, tokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, tokens, liquid)
|
||||
if (isFunction(options.parse)) {
|
||||
options.parse.call(this, token, tokens)
|
||||
}
|
||||
}
|
||||
* render (ctx: Context, emitter: Emitter): TagRenderReturn {
|
||||
const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx)) as Record<string, any>
|
||||
return yield options.render.call(this, ctx, emitter, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Drop } from '../drop/drop'
|
||||
|
||||
export const toString = Object.prototype.toString
|
||||
export const hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
const toLowerCase = String.prototype.toLowerCase
|
||||
|
||||
export const hasOwnProperty = Object.hasOwnProperty
|
||||
|
||||
export function isString (value: any): value is string {
|
||||
return typeof value === 'string'
|
||||
}
|
||||
@@ -90,8 +89,7 @@ export function isUndefined (value: any): boolean {
|
||||
}
|
||||
|
||||
export function isArray (value: any): value is any[] {
|
||||
// be compatible with IE 8
|
||||
return toString.call(value) === '[object Array]'
|
||||
return Array.isArray(value)
|
||||
}
|
||||
|
||||
export function isArrayLike (value: any): value is any[] {
|
||||
|
||||
+2
-2
@@ -14,9 +14,9 @@ for demo in $(ls demo); do
|
||||
npm link liquidjs
|
||||
|
||||
if npm test; then
|
||||
echo [success] demo/webpack
|
||||
echo "[success] demo/$demo"
|
||||
else
|
||||
echo [fail] demo/webpack
|
||||
echo "[fail] demo/$demo"
|
||||
exit 1
|
||||
fi
|
||||
cd -
|
||||
|
||||
+1
-26
@@ -1,4 +1,4 @@
|
||||
import { TopLevelToken, TagToken, Tokenizer, Context, Liquid, Drop, toValueSync, LiquidError, IfTag } from '../..'
|
||||
import { Tokenizer, Context, Liquid, Drop, toValueSync, LiquidError, IfTag } from '../..'
|
||||
import { spawnSync } from 'child_process'
|
||||
import { resolve as resolvePath } from 'path'
|
||||
const LiquidUMD = require('../../dist/liquid.browser.umd.js').Liquid
|
||||
@@ -362,33 +362,8 @@ describe('Issues', function () {
|
||||
const html = await liquid.parseAndRender(tpl)
|
||||
expect(html).toMatch(/^\s*This is a love or luck potion.\s+This is a strength or health or love potion.\s*$/)
|
||||
})
|
||||
it('tag registration compatible to v9 #570', async () => {
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('metadata_file', {
|
||||
parse (tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||
this.str = tagToken.args
|
||||
},
|
||||
async render (ctx: Context) {
|
||||
const content = await Promise.resolve(`{{${this.str}}}`)
|
||||
return this.liquid.parseAndRender(content.toString(), ctx)
|
||||
}
|
||||
})
|
||||
const tpl = '{% metadata_file foo %}'
|
||||
const ctx = { foo: 'FOO' }
|
||||
const html = await liquid.parseAndRender(tpl, ctx)
|
||||
expect(html).toBe('FOO')
|
||||
})
|
||||
it('date filter should return parsed input when no format is provided #573', async () => {
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('metadata_file', {
|
||||
parse (tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||
this.str = tagToken.args
|
||||
},
|
||||
async render (ctx: Context) {
|
||||
const content = await Promise.resolve(`{{${this.str}}}`)
|
||||
return this.liquid.parseAndRender(content.toString(), ctx)
|
||||
}
|
||||
})
|
||||
const tpl = `{{ 'now' | date }}`
|
||||
const html = await liquid.parseAndRender(tpl)
|
||||
// sample: Thursday, February 2, 2023 at 6:25 pm +0000
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Liquid } from '../../../src/liquid'
|
||||
|
||||
describe('LiquidOptions#*keepOutputType*', function () {
|
||||
it('should respect keepOutputType', async function () {
|
||||
const engine = new Liquid({
|
||||
keepOutputType: true
|
||||
})
|
||||
const context = {
|
||||
'my-boolean': true,
|
||||
'my-number': 42,
|
||||
'my-string': 'test'
|
||||
}
|
||||
const booleanHtml = await engine.parseAndRender('{{my-boolean}}', context)
|
||||
expect(booleanHtml).toBe(true)
|
||||
const numberHtml = await engine.parseAndRender('{{my-number}}', context)
|
||||
expect(numberHtml).toBe(42)
|
||||
const html = await engine.parseAndRender('{{my-string}}', context)
|
||||
expect(html).toBe('test')
|
||||
const composedHtml = await engine.parseAndRender('{{my-string}}:{{my-number}}', context)
|
||||
expect(composedHtml).toBe('test:42')
|
||||
})
|
||||
|
||||
it('should respect keepOutputType = false as default', async function () {
|
||||
const engine = new Liquid()
|
||||
const context = {
|
||||
'my-boolean': true,
|
||||
'my-number': 42,
|
||||
'my-string': 'test'
|
||||
}
|
||||
const booleanHtml = await engine.parseAndRender('{{my-boolean}}', context)
|
||||
expect(booleanHtml).toBe('true')
|
||||
const numberHtml = await engine.parseAndRender('{{my-number}}', context)
|
||||
expect(numberHtml).toBe('42')
|
||||
const html = await engine.parseAndRender('{{my-string}}', context)
|
||||
expect(html).toBe('test')
|
||||
const composedHtml = await engine.parseAndRender('{{my-string}}:{{my-number}}', context)
|
||||
expect(composedHtml).toBe('test:42')
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Liquid, Context, isFalsy } from '../../../src'
|
||||
import { mock, restore } from '../../stub/mockfs'
|
||||
import { drainStream } from '../../stub/stream'
|
||||
import { ThrowingTag } from '../../stub/tags'
|
||||
import { resolve } from 'path'
|
||||
|
||||
describe('Liquid', function () {
|
||||
@@ -231,11 +232,7 @@ describe('Liquid', function () {
|
||||
'/root/error.html': 'A{%throwingTag%}B'
|
||||
})
|
||||
engine = new Liquid({ root: ['/root/'] })
|
||||
engine.registerTag('throwingTag', {
|
||||
render: function () {
|
||||
throw new Error('intended render error')
|
||||
}
|
||||
})
|
||||
engine.registerTag('throwingTag', ThrowingTag)
|
||||
})
|
||||
afterEach(restore)
|
||||
it('should render a simple value', async () => {
|
||||
@@ -244,7 +241,7 @@ describe('Liquid', function () {
|
||||
})
|
||||
it('should throw RenderError when tag throws', async () => {
|
||||
const stream = await engine.renderFileToNodeStream('error.html')
|
||||
expect(drainStream(stream)).rejects.toThrow(/intended render error/)
|
||||
expect(drainStream(stream)).rejects.toThrow(/intended error/)
|
||||
})
|
||||
})
|
||||
describe('#analyze', () => {
|
||||
|
||||
@@ -1,38 +1,57 @@
|
||||
import { Liquid } from '../../../src/liquid'
|
||||
import { Tag } from '../../../src/template/tag'
|
||||
import type { Context } from '../../../src/context'
|
||||
import type { TagToken, TopLevelToken } from '../../../src/tokens'
|
||||
|
||||
describe('liquid#registerTag()', function () {
|
||||
it('should support render to simple string', async () => {
|
||||
class SimpleStringTag extends Tag {
|
||||
render () {
|
||||
return 'B'
|
||||
}
|
||||
}
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('simple-string', {
|
||||
render: () => 'B'
|
||||
})
|
||||
liquid.registerTag('simple-string', SimpleStringTag)
|
||||
const html = await liquid.parseAndRender(`A{% simple-string %}C`)
|
||||
return expect(html).toBe('ABC')
|
||||
})
|
||||
it('should support async tag render', async () => {
|
||||
class AsyncStringTag extends Tag {
|
||||
async render () {
|
||||
return 'B'
|
||||
}
|
||||
}
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('async-string', {
|
||||
render: async () => 'B'
|
||||
})
|
||||
liquid.registerTag('async-string', AsyncStringTag)
|
||||
const html = await liquid.parseAndRender(`A{% async-string %}C`)
|
||||
return expect(html).toBe('ABC')
|
||||
})
|
||||
it('should have access to ctx in render()', async () => {
|
||||
class DynamicStringTag extends Tag {
|
||||
async render (ctx: Context) {
|
||||
return ctx.get(['c'])
|
||||
}
|
||||
}
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('dynamic-string', {
|
||||
render: async (ctx) => ctx.get(['c'])
|
||||
})
|
||||
liquid.registerTag('dynamic-string', DynamicStringTag)
|
||||
const html = await liquid.parseAndRender(`A{% dynamic-string %}C`, {
|
||||
c: 'B'
|
||||
})
|
||||
return expect(html).toBe('ABC')
|
||||
})
|
||||
it('should have access to tag arguments', async () => {
|
||||
class ArgumentReflectorTag extends Tag {
|
||||
variable: string
|
||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, remainTokens, liquid)
|
||||
this.variable = token.args.split('=')[1]
|
||||
}
|
||||
async render (ctx: Context) {
|
||||
return ctx.get([this.variable])
|
||||
}
|
||||
}
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('argument-reflector', {
|
||||
parse: function (token) { this.variable = token.args.split('=')[1] },
|
||||
render: async function (ctx) { return ctx.get(this.variable) }
|
||||
})
|
||||
liquid.registerTag('argument-reflector', ArgumentReflectorTag)
|
||||
const html = await liquid.parseAndRender(`A{% argument-reflector variable=c %}C`, {
|
||||
c: 'B'
|
||||
})
|
||||
|
||||
@@ -2,7 +2,8 @@ import { RenderError } from '../../../src/util/error'
|
||||
import { Liquid } from '../../../src/liquid'
|
||||
import { resolve } from 'path'
|
||||
import { mock, restore } from '../../stub/mockfs'
|
||||
import { throwIntendedError, rejectIntendedError } from '../../stub/util'
|
||||
import { throwIntendedError } from '../../stub/util'
|
||||
import { ThrowingTag, RejectingTag, ThrowsOnParseTag } from '../../stub/tags'
|
||||
|
||||
const strictEngine = new Liquid({
|
||||
strictVariables: true,
|
||||
@@ -13,9 +14,9 @@ const strictCatchingEngine = new Liquid({
|
||||
strictVariables: true,
|
||||
strictFilters: true
|
||||
})
|
||||
strictEngine.registerTag('throwingTag', { render: throwIntendedError })
|
||||
strictEngine.registerTag('throwingTag', ThrowingTag)
|
||||
strictEngine.registerFilter('throwingFilter', throwIntendedError)
|
||||
strictCatchingEngine.registerTag('throwingTag', { render: throwIntendedError })
|
||||
strictCatchingEngine.registerTag('throwingTag', ThrowingTag)
|
||||
strictCatchingEngine.registerFilter('throwingFilter', throwIntendedError)
|
||||
|
||||
describe('error', function () {
|
||||
@@ -83,8 +84,8 @@ describe('error', function () {
|
||||
engine = new Liquid({
|
||||
root: '/'
|
||||
})
|
||||
engine.registerTag('throwingTag', { render: throwIntendedError })
|
||||
engine.registerTag('rejectingTag', { render: rejectIntendedError })
|
||||
engine.registerTag('throwingTag', ThrowingTag)
|
||||
engine.registerTag('rejectingTag', RejectingTag)
|
||||
engine.registerFilter('throwingFilter', throwIntendedError)
|
||||
})
|
||||
it('should throw RenderError when tag throws', async function () {
|
||||
@@ -244,10 +245,7 @@ describe('error', function () {
|
||||
let engine: Liquid
|
||||
beforeEach(function () {
|
||||
engine = new Liquid()
|
||||
engine.registerTag('throwsOnParse', {
|
||||
parse: throwIntendedError,
|
||||
render: () => ''
|
||||
})
|
||||
engine.registerTag('throwsOnParse', ThrowsOnParseTag)
|
||||
})
|
||||
it('should throw ParseError when filter not defined', async function () {
|
||||
await expect(strictEngine.parseAndRender('{{1 | a}}')).rejects.toMatchObject({
|
||||
@@ -337,11 +335,7 @@ describe('error', function () {
|
||||
engine = new Liquid({
|
||||
root: '/'
|
||||
})
|
||||
engine.registerTag('throwingTag', {
|
||||
render: function () {
|
||||
throw new Error('intended error')
|
||||
}
|
||||
})
|
||||
engine.registerTag('throwingTag', ThrowingTag)
|
||||
})
|
||||
it('should throw RenderError when tag throws', function () {
|
||||
const src = '{%throwingTag%}'
|
||||
|
||||
@@ -7,9 +7,6 @@ describe('tags/for', function () {
|
||||
let liquid: Liquid, scope: Scope
|
||||
beforeEach(function () {
|
||||
liquid = new Liquid()
|
||||
liquid.registerTag('throwingTag', {
|
||||
render: function () { throw new Error('intended render error') }
|
||||
})
|
||||
scope = {
|
||||
one: 1,
|
||||
// eslint-disable-next-line
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { throwIntendedError, rejectIntendedError } from './util'
|
||||
import { Tag } from '../../src/template/tag'
|
||||
import type { TagToken, TopLevelToken } from '../../src/tokens'
|
||||
import type { Liquid } from '../../src/liquid'
|
||||
|
||||
export class ThrowingTag extends Tag {
|
||||
render () {
|
||||
throwIntendedError()
|
||||
}
|
||||
}
|
||||
|
||||
export class RejectingTag extends Tag {
|
||||
async render () {
|
||||
await rejectIntendedError()
|
||||
}
|
||||
}
|
||||
|
||||
export class ThrowsOnParseTag extends Tag {
|
||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, remainTokens, liquid)
|
||||
throwIntendedError()
|
||||
}
|
||||
render () {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module":"CommonJS",
|
||||
"lib": ["es2015", "es2016", "es2017", "dom"],
|
||||
"target": "ES2020",
|
||||
"module":"ES2020",
|
||||
"lib": ["ES2020", "dom"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"declaration": true,
|
||||
|
||||
Reference in New Issue
Block a user