diff --git a/context.js b/context.js new file mode 100644 index 000000000..fa57b44f3 --- /dev/null +++ b/context.js @@ -0,0 +1,26 @@ +const _ = require('lodash'); +const identifier = require('./identifier.js'); + +var context = { + get: function(str){ + if(identifier.isLiteral(str)){ + return identifier.parseLiteral(str); + } + if(identifier.isVariable(str)){ + return _.get(this.context, str); + } + return ''; + }, + init: function(ctx){ + this.context = ctx; + }, + merge: function(ctx){ + _.merge(this.context, ctx); + } +}; + +exports.factory = function(_ctx){ + var ctx = Object.create(context); + ctx.init(_ctx); + return ctx; +}; diff --git a/identifier.js b/identifier.js new file mode 100644 index 000000000..e9f61de37 --- /dev/null +++ b/identifier.js @@ -0,0 +1,41 @@ +const _ = require('lodash'); + +var singleQuoted = /^'[^']*'$/; +var doubleQuoted = /^"[^"]*"$/; + +var number = /^(?:\d+\.?\d*|\.?\d+)$/; +var bool = /^(?:true|false)$/i; +var range = /^\((\d+)\.\.(\d+)\)$/; + +var quoted = new RegExp(`${singleQuoted.source}|${doubleQuoted.source}`); +var literal = new RegExp(`${quoted.source}|${range.source}|${bool.source}|${number.source}`, 'i'); +var variable = /^[a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*|\[\d+\])*$/; +var identifier = new RegExp(`${literal.source}|${variable.source}`, 'i'); + +exports.patterns = { + quoted, number, bool, range, literal +}; + +exports.isLiteral = function(str) { + return literal.test(str); +}; + +exports.isVariable = function(str) { + return variable.test(str); +}; + +exports.parseLiteral = function(str) { + var res; + if(res = str.match(number)){ + return Number(str); + } + if(res = str.match(bool)){ + return str.toLowerCase() === 'true'; + } + if(res = str.match(quoted)){ + return str.slice(1, -1); + } + if(res = str.match(range)){ + return _.range(res[1], res[2]); + } +}; diff --git a/package.json b/package.json index 647c7d687..d9a30252d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "homepage": "https://github.com/harttle/shopify-liquid#readme", "dependencies": { "bluebird": "^3.4.0", - "debug": "^2.2.0", "lodash": "^4.13.1" }, "devDependencies": { diff --git a/test/context.js b/test/context.js new file mode 100644 index 000000000..17ba67411 --- /dev/null +++ b/test/context.js @@ -0,0 +1,37 @@ +var chai = require("chai"); +var should = chai.should(); +chai.use(require("chai-as-promised")); + +var context = require('../context.js'); + +describe('context', function() { + var ctx; + before(function(){ + ctx = context.factory({ + foo: 'bar', + bar: ['a', {b: [1, 2]}] + }); + }); + + it('should parse literal', function() { + ctx.get('2.3').should.equal(2.3); + ctx.get('(2..4)').should.deep.equal([2,3]); + ctx.get('"foo"').should.equal("foo"); + }); + + it('should get property', function() { + ctx.get('foo').should.equal('bar'); + }); + + it('should get desendent property', function() { + ctx.get('bar[0]').should.equal('a'); + ctx.get('bar[1].b').should.deep.equal([1, 2]); + ctx.get('bar[1].b[1]').should.equal(2); + }); + + it('should merge context', function() { + ctx.merge({foo: 'foo', foo1: 'foo1'}); + ctx.get('foo').should.equal('foo'); + ctx.get('foo1').should.equal('foo1'); + }); +}); diff --git a/test/identifier.js b/test/identifier.js new file mode 100644 index 000000000..072022fa7 --- /dev/null +++ b/test/identifier.js @@ -0,0 +1,76 @@ +var chai = require("chai"); +var should = chai.should(); +chai.use(require("chai-as-promised")); + +var identifier = require('../identifier.js'); + +describe('identifier', function() { + it('should test boolean literal', function() { + identifier.isLiteral('true').should.equal(true); + identifier.isLiteral('TrUE').should.equal(true); + identifier.isLiteral('false').should.equal(true); + }); + + it('should test number literal', function() { + identifier.isLiteral('2.3').should.equal(true); + identifier.isLiteral('.3').should.equal(true); + identifier.isLiteral('3.').should.equal(true); + identifier.isLiteral('23').should.equal(true); + }); + + it('should test string literal', function() { + identifier.isLiteral('""').should.equal(true); + identifier.isLiteral('"a\'b"').should.equal(true); + identifier.isLiteral("''").should.equal(true); + identifier.isLiteral("'a bcd'").should.equal(true); + }); + + it("should test range literal", function() { + identifier.isLiteral("(12..32)").should.equal(true); + }); + + it("should test variable", function() { + identifier.isVariable("foo").should.equal(true); + identifier.isVariable("foo.bar.foo").should.equal(true); + identifier.isVariable("foo[0].b").should.equal(true); + }); + + it('should test none literal', function() { + identifier.isLiteral('2a').should.equal(false); + identifier.isLiteral('"x').should.equal(false); + identifier.isLiteral('a2').should.equal(false); + }); + + it('should test none variable', function() { + identifier.isVariable("a.").should.equal(false); + identifier.isVariable(".b").should.equal(false); + identifier.isVariable(".").should.equal(false); + identifier.isVariable("0a").should.equal(false); + identifier.isVariable("[0][12].bar[0]").should.equal(false); + }); + + it('should parse boolean literal', function() { + identifier.parseLiteral('true').should.equal(true); + identifier.parseLiteral('TrUE').should.equal(true); + identifier.parseLiteral('false').should.equal(false); + }); + + it('should parse number literal', function() { + identifier.parseLiteral('2.3').should.equal(2.3); + identifier.parseLiteral('.32').should.equal(0.32); + identifier.parseLiteral('23.').should.equal(23); + identifier.parseLiteral('23').should.equal(23); + }); + + it('should parse string literal', function() { + identifier.parseLiteral('"ab\'c"').should.equal("ab\'c"); + }); + + it("should parse range literal", function() { + var arr = identifier.parseLiteral("(12..32)"); + arr.length.should.equal(20); + arr[0].should.equal(12); + arr[1].should.equal(13); + arr[19].should.equal(31); + }); +}); diff --git a/test/tokenizer.js b/test/tokenizer.js index 8ce69bc49..2b2374f2d 100644 --- a/test/tokenizer.js +++ b/test/tokenizer.js @@ -29,7 +29,7 @@ describe('tokenizer', function() { tokens[1].type.should.equal('output'); tokens[1].value.should.equal('foo | date: "%Y-%m-%d"'); }); - it('should handle successive output and tags', function(){ + it('should handle successive outputs and tags', function(){ var html = '{{foo}}{{bar}}{%foo%}{%bar%}'; var tokens = tokenizer.parse(html);