├── .gitignore ├── .travis.yml ├── MIT-License ├── README.md ├── benchmark ├── compile.js ├── index.js ├── tpl.ejs └── tpl.liquid ├── lib ├── context.js ├── filters.js ├── index.js ├── md5.js ├── opcode.js ├── parser.js ├── utils.js └── vm.js ├── other └── ejs.js ├── package.json ├── target ├── tinyliquid.js └── tinyliquid.min.js └── test ├── async_filter.js ├── async_locals.js ├── clear_blank_line.js ├── common.js ├── context.js ├── custom_tag.js ├── filters.js ├── locals.js ├── module_exports.js ├── parse_filters.js ├── tag_assign.js ├── tag_capture.js ├── tag_case.js ├── tag_comment.js ├── tag_cycle.js ├── tag_extends_block.js ├── tag_for.js ├── tag_if.js ├── tag_include.js ├── tag_raw.js ├── tag_tablerow.js ├── tag_unless.js ├── test.html ├── throws_error.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | examples 3 | lib-cov 4 | coverage.html 5 | *.log 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | - 0.8 5 | - 0.6 -------------------------------------------------------------------------------- /MIT-License: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Lei Zongmin(雷宗民) 2 | http://ucdok.com 3 | 4 | The MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ------ 2 | 3 | **Notice: this project will never add any new features, if you are the first time to this project, 4 | you may consider other Liquid template engine implement, such as [Nunjucks](https://mozilla.github.io/nunjucks/).** 5 | 6 | ------ 7 | 8 | TinyLiquid [![Build Status](https://secure.travis-ci.org/leizongmin/tinyliquid.png?branch=master)](http://travis-ci.org/leizongmin/tinyliquid) [![Dependencies Status](https://david-dm.org/leizongmin/tinyliquid.png)](http://david-dm.org/leizongmin/tinyliquid) 9 | ============== 10 | 11 | A Liquid syntax template engine. 12 | 13 | __Notes__: The new version 0.2 is almost a full rewrite. Version 0.1 will continue to be maintained for fixing show-stopper bugs, but no new features should be expected. 14 | **The new version is not compatible with the old version**. The version 0.1 documents: https://github.com/leizongmin/tinyliquid/blob/v0.1/README_en.md 15 | 16 | ![tinyliquid](https://nodei.co/npm/tinyliquid.png?downloads=true&stars=true) 17 | 18 | Features 19 | ======== 20 | 21 | * Support asynchronous-locals and asynchronous-filter 22 | 23 | * Easy to add your custom tags and custom filters 24 | 25 | * Almost fully support the liquid syntax 26 | 27 | * Support the Express 3.x framework 28 | 29 | * High test coverage (92% coverage) 30 | 31 | 32 | Installation 33 | ============ 34 | 35 | ```bash 36 | $ npm install tinyliquid 37 | ``` 38 | 39 | 40 | Quick Start 41 | =========== 42 | 43 | TinyLiquid Wiki: https://github.com/leizongmin/tinyliquid/wiki 44 | 45 | The Liquid Templating language: http://liquidmarkup.org/ 46 | 47 | Using on the Express 3.x: [the express-liquid module](https://github.com/leizongmin/express-liquid) 48 | 49 | Using on the browser side: 50 | 51 | ```HTML 52 | 53 | reference the file "test/test.html" from the source directory 54 | ``` 55 | 56 | 57 | The difference with Liquid language 58 | ============== 59 | 60 | TinyLiquid not support the locals variables like this: `a[0]`, `a["b"]`, `a[0]["b"]` and so on. 61 | 62 | Only support to use `.` as the separator: `a.b`, `a.b.c` 63 | 64 | 65 | Running Tests 66 | ============= 67 | 68 | To run the test suite first invoke the following command within the repo, installing the development dependencies: 69 | 70 | ```bash 71 | $ npm install 72 | ``` 73 | 74 | then run the tests: 75 | 76 | ```bash 77 | $ npm test 78 | ``` 79 | 80 | Get the test coverage report: open the file "./coverage.html" in your browser. 81 | 82 | 83 | License 84 | ======= 85 | 86 | ``` 87 | Copyright (c) 2012-2016 Zongmin Lei (雷宗民) 88 | http://ucdok.com 89 | 90 | The MIT License 91 | 92 | Permission is hereby granted, free of charge, to any person obtaining 93 | a copy of this software and associated documentation files (the 94 | "Software"), to deal in the Software without restriction, including 95 | without limitation the rights to use, copy, modify, merge, publish, 96 | distribute, sublicense, and/or sell copies of the Software, and to 97 | permit persons to whom the Software is furnished to do so, subject to 98 | the following conditions: 99 | 100 | The above copyright notice and this permission notice shall be 101 | included in all copies or substantial portions of the Software. 102 | 103 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 104 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 105 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 106 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 107 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 108 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 109 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 110 | ``` 111 | -------------------------------------------------------------------------------- /benchmark/compile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var async = require('async'); 3 | var tinyliquid = require('../'); 4 | var ejs = require('ejs'); 5 | 6 | var TPL_EJS = fs.readFileSync(__dirname + '/tpl.ejs').toString(); 7 | var TPL_LIQUID = fs.readFileSync(__dirname + '/tpl.liquid').toString(); 8 | 9 | for (var i = 0; i < 10; i++) { 10 | TPL_LIQUID += '\n' + TPL_LIQUID; 11 | TPL_EJS += '\n' + TPL_EJS; 12 | } 13 | 14 | 15 | var LOOP_NUM = 100; 16 | 17 | var RESULTS_EJS = []; 18 | var RESULTS_LIQUID = []; 19 | 20 | function getAverage (list) { 21 | var sum = list.reduce(function (a, b) { 22 | return a + b; 23 | }); 24 | return sum / list.length; 25 | } 26 | 27 | 28 | function compileLiquid (callback) { 29 | 30 | for (var i = 0; i < LOOP_NUM; i++) { 31 | var timestamp = Date.now(); 32 | var ast = tinyliquid.parse(TPL_LIQUID); 33 | var spent = Date.now() - timestamp; 34 | RESULTS_LIQUID.push(spent); 35 | } 36 | 37 | callback && callback(); 38 | } 39 | 40 | function compileEjs (callback) { 41 | 42 | for (var i = 0; i < LOOP_NUM; i++) { 43 | var timestamp = Date.now(); 44 | var render = ejs.compile(TPL_EJS); 45 | var spent = Date.now() - timestamp; 46 | RESULTS_EJS.push(spent); 47 | } 48 | 49 | callback && callback(); 50 | } 51 | 52 | 53 | async.series([ 54 | function (done) { 55 | compileLiquid(function () { 56 | console.log('Average: %sms', getAverage(RESULTS_LIQUID)); 57 | console.log('---------'); 58 | done(); 59 | }); 60 | }, 61 | function (done) { 62 | compileEjs(function () { 63 | console.log('Average: %sms', getAverage(RESULTS_EJS)); 64 | console.log('---------'); 65 | done(); 66 | }); 67 | } 68 | ], function (err) { 69 | if (err) throw err; 70 | console.log('tinyliquid Average: %sms', getAverage(RESULTS_LIQUID)); 71 | console.log('EJS Average: %sms', getAverage(RESULTS_EJS)); 72 | console.log('tinyliquid is %sx slower than EJS', (getAverage(RESULTS_LIQUID) / getAverage(RESULTS_EJS)).toFixed(1)); 73 | }); 74 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var async = require('async'); 3 | var tinyliquid = require('../'); 4 | var ejs = require('ejs'); 5 | 6 | var TPL_EJS = fs.readFileSync(__dirname + '/tpl.ejs').toString(); 7 | var TPL_LIQUID = fs.readFileSync(__dirname + '/tpl.liquid').toString(); 8 | 9 | 10 | var _data = Object.keys(tinyliquid).map(function (k) { 11 | return {name: k, value: tinyliquid[k]}; 12 | }).slice(0, 10); 13 | var data = []; 14 | for (var i = 0; i < 2000; i++) { 15 | data = data.concat(_data); 16 | } 17 | 18 | var loop = []; 19 | for (var i = 1; i <= 20; i++) { 20 | loop.push(i); 21 | } 22 | 23 | var RESULTS_EJS = []; 24 | var RESULTS_LIQUID = []; 25 | 26 | function getAverage (list) { 27 | var sum = list.reduce(function (a, b) { 28 | return a + b; 29 | }); 30 | return sum / list.length; 31 | } 32 | 33 | 34 | function renderLiquid (callback) { 35 | 36 | var ast = tinyliquid.parse(TPL_LIQUID); 37 | var c = tinyliquid.newContext(); 38 | c.setLocals('list', data); 39 | c.setFilter('to_string', function (v) { 40 | return typeof v === 'function' ? '[Function]' : v; 41 | }); 42 | 43 | var timestamp = Date.now(); 44 | 45 | tinyliquid.run(ast, c, function (err) { 46 | if (err) throw err; 47 | 48 | var spent = Date.now() - timestamp; 49 | RESULTS_LIQUID.push(spent); 50 | 51 | console.log('tinyliquid: total ' + data.length + ' items, spent ' + spent + 'ms'); 52 | 53 | callback && callback(); 54 | }); 55 | } 56 | 57 | function renderEjs (callback) { 58 | 59 | var render = ejs.compile(TPL_EJS); 60 | 61 | var timestamp = Date.now(); 62 | 63 | var html = render({ 64 | data: data, 65 | to_string: function (v) { 66 | return typeof v === 'function' ? '[Function]' : v; 67 | } 68 | }); 69 | 70 | var spent = Date.now() - timestamp; 71 | RESULTS_EJS.push(spent); 72 | 73 | console.log('EJS: total ' + data.length + ' items, spent ' + spent + 'ms'); 74 | 75 | callback && callback(); 76 | } 77 | 78 | 79 | async.series([ 80 | function (done) { 81 | async.eachSeries(loop, function (item, next) { 82 | renderLiquid(next); 83 | }, function (err) { 84 | if (err) return next(err); 85 | console.log('Average: %sms', getAverage(RESULTS_LIQUID)); 86 | console.log('---------'); 87 | done(); 88 | }); 89 | }, 90 | function (done) { 91 | async.eachSeries(loop, function (item, next) { 92 | renderEjs(next); 93 | }, function (err) { 94 | if (err) return next(err); 95 | console.log('Average: %sms', getAverage(RESULTS_EJS)); 96 | console.log('---------'); 97 | done(); 98 | }); 99 | } 100 | ], function (err) { 101 | if (err) throw err; 102 | console.log('tinyliquid Average: %sms', getAverage(RESULTS_LIQUID)); 103 | console.log('EJS Average: %sms', getAverage(RESULTS_EJS)); 104 | console.log('tinyliquid is %sx slower than EJS', (getAverage(RESULTS_LIQUID) / getAverage(RESULTS_EJS)).toFixed(1)); 105 | }); 106 | -------------------------------------------------------------------------------- /benchmark/tpl.ejs: -------------------------------------------------------------------------------- 1 | 6 |