mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
fix: post-increment, pre-decrement working on #76
This commit is contained in:
+12
-11
@@ -1,15 +1,16 @@
|
||||
'use strict'
|
||||
const strftime = require('./src/util/strftime.js')
|
||||
const _ = require('./src/util/underscore.js')
|
||||
const isTruthy = require('./src/syntax.js').isTruthy
|
||||
|
||||
var escapeMap = {
|
||||
let escapeMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
}
|
||||
var unescapeMap = {
|
||||
let unescapeMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
@@ -17,14 +18,14 @@ var unescapeMap = {
|
||||
''': "'"
|
||||
}
|
||||
|
||||
var filters = {
|
||||
let filters = {
|
||||
'abs': v => Math.abs(v),
|
||||
'append': (v, arg) => v + arg,
|
||||
'capitalize': str => stringify(str).charAt(0).toUpperCase() + str.slice(1),
|
||||
'ceil': v => Math.ceil(v),
|
||||
'concat': (v, arg) => Array.prototype.concat.call(v, arg),
|
||||
'date': (v, arg) => {
|
||||
var date = v
|
||||
let date = v
|
||||
if (v === 'now') {
|
||||
date = new Date()
|
||||
} else if (_.isString(v)) {
|
||||
@@ -41,7 +42,7 @@ var filters = {
|
||||
'first': v => v[0],
|
||||
'floor': v => Math.floor(v),
|
||||
'join': (v, arg) => v.join(arg),
|
||||
'last': v => v[v.length - 1],
|
||||
'last': v => _.last(v),
|
||||
'lstrip': v => stringify(v).replace(/^\s+/, ''),
|
||||
'map': (arr, arg) => arr.map(v => v[arg]),
|
||||
'minus': bindFixed((v, arg) => v - arg),
|
||||
@@ -56,7 +57,7 @@ var filters = {
|
||||
'replace_first': (v, arg1, arg2) => stringify(v).replace(arg1, arg2),
|
||||
'reverse': v => v.reverse(),
|
||||
'round': (v, arg) => {
|
||||
var amp = Math.pow(10, arg || 0)
|
||||
let amp = Math.pow(10, arg || 0)
|
||||
return Math.round(v * amp, arg) / amp
|
||||
},
|
||||
'rstrip': str => stringify(str).replace(/\s+$/, ''),
|
||||
@@ -78,13 +79,13 @@ var filters = {
|
||||
},
|
||||
'truncatewords': (v, l, o) => {
|
||||
if (o === undefined) o = '...'
|
||||
var arr = v.split(' ')
|
||||
var ret = arr.slice(0, l).join(' ')
|
||||
let arr = v.split(' ')
|
||||
let ret = arr.slice(0, l).join(' ')
|
||||
if (arr.length > l) ret += o
|
||||
return ret
|
||||
},
|
||||
'uniq': function (arr) {
|
||||
var u = {}
|
||||
let u = {}
|
||||
return (arr || []).filter(val => {
|
||||
if (u.hasOwnProperty(val)) {
|
||||
return false
|
||||
@@ -106,7 +107,7 @@ function unescape (str) {
|
||||
}
|
||||
|
||||
function getFixed (v) {
|
||||
var p = (v + '').split('.')
|
||||
let p = (v + '').split('.')
|
||||
return (p.length > 1) ? p[1].length : 0
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ function stringify (obj) {
|
||||
|
||||
function bindFixed (cb) {
|
||||
return (l, r) => {
|
||||
var f = getMaxFixed(l, r)
|
||||
let f = getMaxFixed(l, r)
|
||||
return cb(l, r).toFixed(f)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-16
@@ -5,16 +5,16 @@ const assert = require('./util/assert.js')
|
||||
|
||||
var Scope = {
|
||||
getAll: function () {
|
||||
return this.scopes.reduce((ctx, val) => Object.assign(ctx, val), Object.create(null))
|
||||
return this.contexts.reduce((ctx, val) => Object.assign(ctx, val), Object.create(null))
|
||||
},
|
||||
get: function (path) {
|
||||
let paths = this.propertyAccessSeq(path)
|
||||
let scope = this.findScopeFor(paths[0])
|
||||
let scope = this.findContextFor(paths[0]) || _.last(this.contexts)
|
||||
return paths.reduce((value, key) => this.readProperty(value, key), scope)
|
||||
},
|
||||
set: function (path, v) {
|
||||
let paths = this.propertyAccessSeq(path)
|
||||
let scope = this.findScopeFor(paths[0])
|
||||
let scope = this.findContextFor(paths[0]) || _.last(this.contexts)
|
||||
paths.some((key, i) => {
|
||||
if (!_.isObject(scope)) {
|
||||
return true
|
||||
@@ -29,28 +29,32 @@ var Scope = {
|
||||
scope = scope[key]
|
||||
})
|
||||
},
|
||||
unshift: function (ctx) {
|
||||
return this.contexts.unshift(ctx)
|
||||
},
|
||||
push: function (ctx) {
|
||||
this.scopes.push(ctx)
|
||||
return this.contexts.push(ctx)
|
||||
},
|
||||
pop: function (ctx) {
|
||||
if (!arguments.length) {
|
||||
return this.scopes.pop()
|
||||
return this.contexts.pop()
|
||||
}
|
||||
let i = this.scopes.findIndex(scope => scope === ctx)
|
||||
let i = this.contexts.findIndex(scope => scope === ctx)
|
||||
if (i === -1) {
|
||||
throw new TypeError('scope not found, cannot pop')
|
||||
}
|
||||
return this.scopes.splice(i, 1)[0]
|
||||
return this.contexts.splice(i, 1)[0]
|
||||
},
|
||||
findScopeFor: function (key) {
|
||||
let i = this.scopes.length - 1
|
||||
while (i >= 0 && !(key in this.scopes[i])) {
|
||||
i--
|
||||
findContextFor: function (key, filter) {
|
||||
filter = filter || (() => true)
|
||||
for (let i = this.contexts.length - 1; i >= 0; i--) {
|
||||
let candidate = this.contexts[i]
|
||||
if (!filter(candidate)) continue
|
||||
if (key in candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
i = this.scopes.length - 1
|
||||
}
|
||||
return this.scopes[i]
|
||||
return null
|
||||
},
|
||||
readProperty: function (obj, key) {
|
||||
let val
|
||||
@@ -153,6 +157,13 @@ exports.factory = function (ctx, opts) {
|
||||
}
|
||||
var scope = Object.create(Scope)
|
||||
scope.opts = _.assign(defaultOptions, opts)
|
||||
scope.scopes = [ctx || {}]
|
||||
scope.contexts = [ctx || {}]
|
||||
return scope
|
||||
}
|
||||
|
||||
exports.types = {
|
||||
AssignScope: Object.create(null),
|
||||
CaptureScope: Object.create(null),
|
||||
IncrementScope: Object.create(null),
|
||||
DecrementScope: Object.create(null)
|
||||
}
|
||||
|
||||
+11
-10
@@ -1,26 +1,27 @@
|
||||
'use strict'
|
||||
const lexical = require('./lexical.js')
|
||||
const Syntax = require('./syntax.js')
|
||||
const assert = require('./util/assert.js')
|
||||
|
||||
function hash (markup, scope) {
|
||||
var obj = {}
|
||||
var match
|
||||
let obj = {}
|
||||
let match
|
||||
lexical.hashCapture.lastIndex = 0
|
||||
while ((match = lexical.hashCapture.exec(markup))) {
|
||||
var k = match[1]
|
||||
var v = match[2]
|
||||
let k = match[1]
|
||||
let v = match[2]
|
||||
obj[k] = Syntax.evalValue(v, scope)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var tagImpls = {}
|
||||
let tagImpls = {}
|
||||
|
||||
var _tagInstance = {
|
||||
let _tagInstance = {
|
||||
render: function (scope) {
|
||||
var obj = hash(this.token.args, scope)
|
||||
var impl = this.tagImpl
|
||||
let obj = hash(this.token.args, scope)
|
||||
let impl = this.tagImpl
|
||||
if (typeof impl.render !== 'function') {
|
||||
return Promise.resolve('')
|
||||
}
|
||||
@@ -31,7 +32,7 @@ module.exports = function () {
|
||||
this.token = token
|
||||
this.name = token.name
|
||||
|
||||
var tagImpl = tagImpls[this.name]
|
||||
let tagImpl = tagImpls[this.name]
|
||||
assert(tagImpl, `tag ${this.name} not found`)
|
||||
this.tagImpl = Object.create(tagImpl)
|
||||
if (this.tagImpl.parse) {
|
||||
@@ -45,7 +46,7 @@ module.exports = function () {
|
||||
}
|
||||
|
||||
function construct (token, tokens) {
|
||||
var instance = Object.create(_tagInstance)
|
||||
let instance = Object.create(_tagInstance)
|
||||
instance.parse(token, tokens)
|
||||
return instance
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
const resolve = require('resolve-url')
|
||||
const splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^/]+?|)(\.[^./]*|))(?:[/]*)$/
|
||||
const urlRe = /^(?:\w+:)?\/\/([^\s.]+\.\S{2}|localhost[:?\d]*)\S*$/
|
||||
const _ = require('./underscore')
|
||||
|
||||
// https://github.com/jinder/path/blob/master/path.js#L567
|
||||
exports.extname = function (path) {
|
||||
@@ -16,7 +17,7 @@ exports.resolve = function (root, path) {
|
||||
if (Object.prototype.toString.call(root) === '[object Array]') {
|
||||
root = root[0]
|
||||
}
|
||||
if (root && root.charAt(root.length - 1) !== '/') {
|
||||
if (root && _.last(root) !== '/') {
|
||||
root += '/'
|
||||
}
|
||||
return resolve(root, path)
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ const Liquid = require('..')
|
||||
const lexical = Liquid.lexical
|
||||
const re = new RegExp(`(${lexical.identifier.source})\\s*=(.*)`)
|
||||
const assert = require('../src/util/assert.js')
|
||||
const types = require('../src/scope').types
|
||||
|
||||
module.exports = function (liquid) {
|
||||
liquid.registerTag('assign', {
|
||||
@@ -13,7 +14,7 @@ module.exports = function (liquid) {
|
||||
this.value = match[2]
|
||||
},
|
||||
render: function (scope) {
|
||||
let ctx = Object.create(null)
|
||||
let ctx = Object.create(types.AssignScope)
|
||||
ctx[this.key] = liquid.evalValue(this.value, scope)
|
||||
scope.push(ctx)
|
||||
return Promise.resolve('')
|
||||
|
||||
+17
-3
@@ -1,6 +1,8 @@
|
||||
'use strict'
|
||||
const Liquid = require('..')
|
||||
const lexical = Liquid.lexical
|
||||
const assert = require('../src/util/assert.js')
|
||||
const types = require('../src/scope').types
|
||||
|
||||
module.exports = function (liquid) {
|
||||
liquid.registerTag('decrement', {
|
||||
@@ -10,9 +12,21 @@ module.exports = function (liquid) {
|
||||
this.variable = match[0]
|
||||
},
|
||||
render: function (scope, hash) {
|
||||
var v = scope.get(this.variable)
|
||||
if (typeof v !== 'number') v = 0
|
||||
scope.set(this.variable, v - 1)
|
||||
let context = scope.findContextFor(
|
||||
this.variable,
|
||||
ctx => {
|
||||
return Object.getPrototypeOf(ctx) !== types.CaptureScope &&
|
||||
Object.getPrototypeOf(ctx) !== types.AssignScope
|
||||
}
|
||||
)
|
||||
if (!context) {
|
||||
context = Object.create(types.DecrementScope)
|
||||
scope.unshift(context)
|
||||
}
|
||||
if (typeof context[this.variable] !== 'number') {
|
||||
context[this.variable] = 0
|
||||
}
|
||||
return --context[this.variable]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+20
-4
@@ -1,18 +1,34 @@
|
||||
'use strict'
|
||||
const Liquid = require('..')
|
||||
const assert = require('../src/util/assert.js')
|
||||
const lexical = Liquid.lexical
|
||||
const types = require('../src/scope').types
|
||||
|
||||
module.exports = function (liquid) {
|
||||
liquid.registerTag('increment', {
|
||||
parse: function (token) {
|
||||
var match = token.args.match(lexical.identifier)
|
||||
let match = token.args.match(lexical.identifier)
|
||||
assert(match, `illegal identifier ${token.args}`)
|
||||
this.variable = match[0]
|
||||
},
|
||||
render: function (scope, hash) {
|
||||
var v = scope.get(this.variable)
|
||||
if (typeof v !== 'number') v = 0
|
||||
scope.set(this.variable, v + 1)
|
||||
let context = scope.findContextFor(
|
||||
this.variable,
|
||||
ctx => {
|
||||
return Object.getPrototypeOf(ctx) !== types.CaptureScope &&
|
||||
Object.getPrototypeOf(ctx) !== types.AssignScope
|
||||
}
|
||||
)
|
||||
if (!context) {
|
||||
context = Object.create(types.IncrementScope)
|
||||
scope.unshift(context)
|
||||
}
|
||||
if (typeof context[this.variable] !== 'number') {
|
||||
context[this.variable] = 0
|
||||
}
|
||||
let val = context[this.variable]
|
||||
context[this.variable]++
|
||||
return val
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+2
-3
@@ -9,14 +9,13 @@ module.exports = function (liquid) {
|
||||
if (token.name === 'endraw') stream.stop()
|
||||
else this.tokens.push(token)
|
||||
})
|
||||
.on('end', x => {
|
||||
.on('end', () => {
|
||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||
})
|
||||
stream.start()
|
||||
},
|
||||
render: function (scope, hash) {
|
||||
var tokens = this.tokens.map(token => token.raw).join('')
|
||||
return Promise.resolve(tokens)
|
||||
return this.tokens.map(token => token.raw).join('')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+2
-2
@@ -141,7 +141,7 @@ describe('filters', function () {
|
||||
|
||||
it('should support split/first', function () {
|
||||
var src = '{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}' +
|
||||
'{{ my_array | first }}'
|
||||
'{{ my_array | first }}'
|
||||
return test(src, 'apples')
|
||||
})
|
||||
|
||||
@@ -160,7 +160,7 @@ describe('filters', function () {
|
||||
|
||||
it('should support split/last', function () {
|
||||
var src = '{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}' +
|
||||
'{{ my_array|last }}'
|
||||
'{{ my_array|last }}'
|
||||
return test(src, 'tiger')
|
||||
})
|
||||
|
||||
|
||||
+25
-21
@@ -1,3 +1,4 @@
|
||||
'use strict'
|
||||
const Liquid = require('../..')
|
||||
const chai = require('chai')
|
||||
const expect = chai.expect
|
||||
@@ -5,34 +6,37 @@ chai.use(require('chai-as-promised'))
|
||||
|
||||
describe('tags/decrement', function () {
|
||||
var liquid = Liquid()
|
||||
|
||||
it('should throw when variable expression illegal', function () {
|
||||
var src = '{% decrement / %}{{one}}'
|
||||
var src = '{% decrement / %}{{var}}'
|
||||
var ctx = {}
|
||||
return expect(liquid.parseAndRender(src, ctx)).to.be.rejectedWith(/illegal/)
|
||||
})
|
||||
|
||||
it('should support decrement', function () {
|
||||
var src = '{% decrement one %}{{one}}'
|
||||
var ctx = {
|
||||
one: 1
|
||||
}
|
||||
return expect(liquid.parseAndRender(src, ctx))
|
||||
.to.eventually.equal('0')
|
||||
})
|
||||
|
||||
it('should decrement undefined', function () {
|
||||
var src = '{% decrement empty %}{{empty}}'
|
||||
it('should decrement undefined variable', function () {
|
||||
let src = '{% decrement var %}{% decrement var %}{% decrement var %}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('-1')
|
||||
.to.eventually.equal('-1-2-3')
|
||||
})
|
||||
|
||||
it('should support decrement multiple times', function () {
|
||||
var src = '{% decrement foo %}{%decrement foo%}{{foo}}'
|
||||
var ctx = {
|
||||
foo: 1
|
||||
}
|
||||
return expect(liquid.parseAndRender(src, ctx))
|
||||
.to.eventually.equal('-1')
|
||||
it('should decrement defined variable', function () {
|
||||
let src = '{% decrement var %}{% decrement var %}{% decrement var %}'
|
||||
let ctx = {'var': 10}
|
||||
return liquid.parseAndRender(src, ctx)
|
||||
.then(x => {
|
||||
expect(x).to.equal('987')
|
||||
expect(ctx.var).to.equal(7)
|
||||
})
|
||||
})
|
||||
|
||||
it('should be independent from assign', function () {
|
||||
let src = '{% assign var=10 %}{% decrement var %}{% decrement var %}{% decrement var %}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('-1-2-3')
|
||||
})
|
||||
|
||||
it('should not shading assign', function () {
|
||||
let src = '{% assign var=10 %}{% decrement var %}{% decrement var %}{% decrement var %} {{var}}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('-1-2-3 10')
|
||||
})
|
||||
})
|
||||
|
||||
+25
-20
@@ -1,32 +1,37 @@
|
||||
'use strict'
|
||||
const Liquid = require('../..')
|
||||
const chai = require('chai')
|
||||
const expect = chai.expect
|
||||
chai.use(require('chai-as-promised'))
|
||||
|
||||
describe('tags/increment', function () {
|
||||
var liquid = Liquid()
|
||||
let liquid = Liquid()
|
||||
|
||||
it('should support increment', function () {
|
||||
var src = '{% increment one %}{{one}}'
|
||||
var ctx = {
|
||||
one: 1
|
||||
}
|
||||
return expect(liquid.parseAndRender(src, ctx))
|
||||
.to.eventually.equal('2')
|
||||
})
|
||||
|
||||
it('should increment undefined', function () {
|
||||
var src = '{% increment empty %}{{empty}}'
|
||||
it('should increment undefined variable', function () {
|
||||
let src = '{% increment one %}{% increment one %}{% increment one %}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('1')
|
||||
.to.eventually.equal('012')
|
||||
})
|
||||
|
||||
it('should support increment multiple times', function () {
|
||||
var src = '{% increment foo %}{%increment foo%}{{foo}}'
|
||||
var ctx = {
|
||||
foo: 1
|
||||
}
|
||||
return expect(liquid.parseAndRender(src, ctx))
|
||||
.to.eventually.equal('3')
|
||||
it('should increment defined variable', function () {
|
||||
let src = '{% increment one %}{% increment one %}{% increment one %}'
|
||||
let ctx = {one: 7}
|
||||
return liquid.parseAndRender(src, ctx)
|
||||
.then(x => {
|
||||
expect(x).to.equal('789')
|
||||
expect(ctx.one).to.equal(10)
|
||||
})
|
||||
})
|
||||
|
||||
it('should be independent from assign', function () {
|
||||
let src = '{% assign var=10 %}{% increment var %}{% increment var %}{% increment var %}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('012')
|
||||
})
|
||||
|
||||
it('should not shading assign', function () {
|
||||
let src = '{% assign var=10 %}{% increment var %}{% increment var %}{% increment var %} {{var}}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.eventually.equal('012 10')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -260,6 +260,7 @@ describe('error', function () {
|
||||
.be.rejected
|
||||
.then(function (err) {
|
||||
mock.restore()
|
||||
console.log(err, err.name)
|
||||
expect(err.name).to.equal('RenderError')
|
||||
expect(err.file).to.equal(path.resolve('/foo.html'))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user