├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── dom_parser.js
├── gulpfile.js
├── index.js
├── package.json
└── test
├── condition.test.js
├── databinding.test.js
├── list.test.js
├── test.html
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | dist
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - "4"
5 | before_script:
6 | - npm install -g gulp
7 | script:
8 | - gulp
9 | - npm test
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | This software is licensed under the MIT License.
2 |
3 | Copyright(c) Long Xiang (seanlong@outlook.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | wxml-parser
2 | =======
3 |
4 | [](https://travis-ci.org/seanlong/wxml-parser)
5 |
6 | ## What
7 | 这是一个微信小程序[WXML](http://mp.weixin.qq.com/debug/wxadoc/dev/framework/view/wxml/)文件的JavaScript parser实现。输出微信官方提供的native可执行文件类似的数据结构。
8 | 该数据可以被后续微信小程序的Virtual DOM generator生成真实DOM。
9 |
10 | 目前版本支持除了模板和模板引用外的基本语法。
11 |
12 | ## Run
13 | ```
14 | var parser = require('wxml-parser);
15 | console.log(JSON.stringify(parser(' {{ message }} ', {message: 'Hello MINA!'}), null, 2));
16 | ```
17 | output:
18 | ```
19 | {
20 | "tag": "wx-body",
21 | "attr": {},
22 | "children": [{
23 | "tag": "wx-view",
24 | "attr": {},
25 | "children": ["Hello MINA!"]
26 | }]
27 | }
28 | ```
29 | ## License
30 |
31 | [MIT](LICENSE.txt)
32 |
--------------------------------------------------------------------------------
/dom_parser.js:
--------------------------------------------------------------------------------
1 | /*
2 | * DOMParser HTML extension
3 | * 2012-09-04
4 | *
5 | * By Eli Grey, http://eligrey.com
6 | * Public domain.
7 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 | */
9 |
10 | /*! @source https://gist.github.com/1129031 */
11 | /*global document, DOMParser*/
12 |
13 | (function(DOMParser) {
14 | "use strict";
15 |
16 | var
17 | DOMParser_proto = DOMParser.prototype
18 | , real_parseFromString = DOMParser_proto.parseFromString
19 | ;
20 |
21 | // Firefox/Opera/IE throw errors on unsupported types
22 | try {
23 | // WebKit returns null on unsupported types
24 | if ((new DOMParser).parseFromString("", "text/html")) {
25 | // text/html parsing is natively supported
26 | return;
27 | }
28 | } catch (ex) {}
29 |
30 | DOMParser_proto.parseFromString = function(markup, type) {
31 | if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
32 | var
33 | doc = document.implementation.createHTMLDocument("")
34 | ;
35 | if (markup.toLowerCase().indexOf(' -1) {
36 | doc.documentElement.innerHTML = markup;
37 | }
38 | else {
39 | doc.body.innerHTML = markup;
40 | }
41 | return doc;
42 | } else {
43 | return real_parseFromString.apply(this, arguments);
44 | }
45 | };
46 | }(DOMParser));
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var babelify = require('babelify');
2 | var browserify = require('browserify');
3 | var uglify = require('gulp-uglify');
4 | var watchify = require('watchify');
5 |
6 | var gulp = require('gulp');
7 | var source = require('vinyl-source-stream');
8 | var buffer = require('vinyl-buffer');
9 | var gutil = require('gulp-util');
10 | var sourcemaps = require('gulp-sourcemaps');
11 | var assign = require('lodash.assign');
12 | var plugins = require('gulp-load-plugins')();
13 |
14 | var options = {
15 | debug: false,
16 | fullPaths: false,
17 | standalone: 'wxmlparser'
18 | };
19 |
20 | function createBundler(entry) {
21 | options = plugins.util.env.production ? options :
22 | assign(options, { debug: true, fullPaths: true, cache: {}, packageCache: {} });
23 | options.entries = [entry];
24 | var b = browserify(options);
25 | b.transform('babelify', { presets: ['es2015'] });
26 | return b;
27 | }
28 |
29 | function bundle(b, out) {
30 | return b.bundle()
31 | .on('error', plugins.util.log.bind(plugins.util, 'Browserify Error'))
32 | .pipe(source(out))
33 | .pipe(buffer())
34 | .pipe(plugins.util.env.production ? uglify() : gutil.noop())
35 | .pipe(sourcemaps.init({ loadMaps: true }))
36 | .pipe(sourcemaps.write('./'))
37 | .pipe(gulp.dest('./dist'));
38 | }
39 |
40 | gulp.task('build', function() {
41 | var bundler = createBundler('index.js');
42 | return bundle(bundler, 'wxml-parser.js')
43 | });
44 |
45 | gulp.task('test', function() {
46 | var bundler = createBundler('test/test.js');
47 | bundler.plugin(watchify);
48 | bundler.on('update', function() {
49 | bundle(bundler, 'test.js');
50 | gutil.log('Rebundle...');
51 | });
52 | bundle(bundler, 'test.js');
53 | });
54 |
55 | gulp.task('default', function() {
56 | var bundler = createBundler('test/test.js');
57 | return bundle(bundler, 'test.js')
58 | });
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./dom_parser.js')
4 |
5 | function XML2DataTree(root, context, newRoot, layerOps, layerIdx) {
6 | if (!newRoot)
7 | newRoot = { tag: 'body', children: [] };
8 | else if (!newRoot.children)
9 | throw new Error('cannot add child to leaf node');
10 |
11 | if (!root.attributes && root.nodeType == 3) {
12 | var newNode = parseTextNode(root, context);
13 | } else {
14 | var newNode = parseElementNode(root, context, layerOps, layerIdx);
15 | }
16 | if (newNode == null)
17 | return;
18 |
19 | if (newNode.tag !== 'wx-block') {
20 | if (newNode.items && newNode.array && newNode.array.length)
21 | newNode.array.forEach(n => newRoot.children.push(n));
22 | else
23 | newRoot.children.push(newNode);
24 | }
25 | var newLayerOps = [];
26 | var newLayerIdx = 0;
27 |
28 | if (newNode.items && newNode.array && newNode.array.length) {
29 | newNode.array.forEach((n, idx) => {
30 | n = n.tag === 'wx-block' ? newRoot : n;
31 | var newContext = JSON.parse(JSON.stringify(context));
32 | newContext[newNode.itemName] = newNode.items[idx];
33 | newContext[newNode.indexName] = idx;
34 | for (var i = 0; i < root.childNodes.length; ++i) {
35 | var child = root.childNodes[i];
36 | if (child.nodeType != 1 && child.nodeType != 3)
37 | continue;
38 | if (child.nodeType == 3 && !child.nodeValue.trim().length)
39 | continue;
40 | XML2DataTree(child, newContext, n, newLayerOps, newLayerIdx++);
41 | }
42 | });
43 | } else {
44 | newNode = newNode.tag === 'wx-block' ? newRoot : newNode;
45 | for (var i = 0; i < root.childNodes.length; ++i) {
46 | var child = root.childNodes[i];
47 | if (child.nodeType != 1 && child.nodeType != 3)
48 | continue;
49 | if (child.nodeType == 3 && !child.nodeValue.trim().length)
50 | continue;
51 | XML2DataTree(child, context, newNode, newLayerOps, newLayerIdx++);
52 | }
53 | }
54 |
55 | return newRoot;
56 | }
57 |
58 | function parseTextNode(node, data) {
59 | return stringEvaluate(node.nodeValue.trim(), data, false);
60 | }
61 |
62 | function parseElementNode(node, data, layerOps, layerIdx) {
63 | var ret = {};
64 | ret.tag = 'wx-' + (node.tagName == 'IMG' ? 'image' : node.tagName.toLowerCase());
65 | ret.attr = {};
66 | ret.children = [];
67 | for (var i = 0; i < node.attributes.length; ++i) {
68 | var attr = node.attributes[i];
69 | var value = stringEvaluate(attr.value.trim(), data, attr.name === 'data');
70 | var name = attr.name;
71 | if (name.split('-').length > 1)
72 | name = name.split('-')
73 | .map((p, i) => i != 0 ? p[0].toUpperCase() + p.substring(1) : p)
74 | .join('');
75 | if (ret.items && ret.array) {
76 | ret.array.forEach(c => c.attr[name] = value);
77 | } else {
78 | ret.attr[name] = value;
79 | }
80 | //if (name.startsWith('wx:')) {
81 | if (name.substring(0, 3) === 'wx:') {
82 | ret = processWxAttribute(name, value, ret, layerOps, layerIdx);
83 | if (!ret)
84 | break;
85 | }
86 | }
87 | return ret;
88 | }
89 |
90 | function processWxAttribute(name, value, node, operations, idx) {
91 | switch (name) {
92 | case 'wx:if':
93 | var condition = !!value;
94 | operations[idx] = ['if', condition];
95 | if (condition == false)
96 | return;
97 | break;
98 | case 'wx:elif':
99 | var condition = !!value;
100 | operations[idx] = ['elif', condition];
101 | if (operations[idx-1] == undefined || operations[idx-1][0] != 'if')
102 | throw new Error('no corresponding wx:if found');
103 | if (operations[idx-1][1] == true || condition == false)
104 | return;
105 | break;
106 | case 'wx:else':
107 | if (operations[idx-1] == undefined || (operations[idx-1][0] != 'if' && operations[idx-1][0] != 'elif'))
108 | throw new Error('no corresponding wx:if/elif found');
109 | if (operations[idx-1][1] == true || (operations[idx-1][0] == 'elif' && operations[idx-2][1] == true))
110 | return;
111 | break;
112 | case 'wx:for':
113 | var items = Array.isArray(value) ? value : [];
114 | delete node.attr[name];
115 | node = {
116 | tag: node.tag,
117 | indexName: node.indexName ? node.indexName : 'index',
118 | itemName: node.itemName ? node.itemName : 'item',
119 | items: items,
120 | array: items.map(i => { return { tag: node.tag, attr: node.attr, children: [] } })
121 | };
122 | break;
123 | case 'wx:forIndex':
124 | node.indexName = value;
125 | if (node.items && node.array)
126 | node.array.forEach(c => delete c.attr[name]);
127 | else if (node.attr)
128 | node.attr[name];
129 | break;
130 | case 'wx:forItem':
131 | node.itemName = value;
132 | if (node.items && node.array)
133 | node.array.forEach(c => delete c.attr[name]);
134 | else if (node.attr)
135 | node.attr[name];
136 | break;
137 | }
138 | return node;
139 | }
140 |
141 | function stringEvaluate(str, data, toObject) {
142 | if (str.match(/^{{[^{}]+}}$/)) {
143 | return mustacheEvaluate(str.replace(/^{{/, '').replace(/}}$/, ''), data, toObject);
144 | } else {
145 | return str.replace(
146 | /({{[^{}]+}})/g,
147 | v => mustacheEvaluate(v.replace(/^{{/, '').replace(/}}$/, ''), data, toObject));
148 | }
149 | }
150 |
151 | function mustacheEvaluate(str, data, toObject) {
152 | var s = '(function() { ';
153 | for (var k in data) {
154 | s += 'var ' + k + ' = JSON.parse(\'' + JSON.stringify(data[k]) + '\');';
155 | }
156 | s += 'return ';
157 | var exp = toObject ? s + '{' + str + '}})()' : s + str + '})()';
158 | try {
159 | var ret = eval(exp);
160 | } catch (e) {
161 | console.log(exp)
162 | console.error(e)
163 | }
164 | return ret;
165 | }
166 |
167 | function parser(input, data) {
168 | var doc = new DOMParser().parseFromString(input, 'text/html');
169 | var tree = XML2DataTree(doc.body, data, null, []);
170 | return tree.children[0];
171 | }
172 |
173 | module.exports = parser;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wxml-parser",
3 | "version": "0.0.3",
4 | "description": "wxapp WXML(HTML like layout file) parser",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "node_modules/mocha-phantomjs/bin/mocha-phantomjs ./test/test.html"
8 | },
9 | "keywords": [
10 | "wx",
11 | "wx-app",
12 | "wxml",
13 | "virtual-dom"
14 | ],
15 | "author": {
16 | "name": "Long Xiang",
17 | "email": "seanlong@outlook.com"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/seanlong/wxml-parser.git"
22 | },
23 | "license": "MIT",
24 | "devDependencies": {
25 | "babel-preset-es2015": "^6.18.0",
26 | "babelify": "^7.3.0",
27 | "browserify": "^13.1.1",
28 | "gulp": "^3.9.1",
29 | "gulp-load-plugins": "^1.3.0",
30 | "gulp-sourcemaps": "^2.2.0",
31 | "gulp-uglify": "^2.0.0",
32 | "gulp-util": "^3.0.7",
33 | "lodash.assign": "^4.2.0",
34 | "mocha": "^3.1.2",
35 | "mocha-phantomjs": "^4.1.0",
36 | "phantomjs": "^2.1.7",
37 | "vinyl-buffer": "^1.0.0",
38 | "vinyl-source-stream": "^1.1.0",
39 | "watchify": "^3.7.0"
40 | },
41 | "dependencies": {}
42 | }
43 |
--------------------------------------------------------------------------------
/test/condition.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 | var parser = require('../index.js')
3 |
4 | function assertObject(result, expect) {
5 | return assert.equal(JSON.stringify(result), JSON.stringify(expect))
6 | }
7 |
8 | describe('Conditional rendering', function() {
9 |
10 | it('wx:if', function() {
11 | var data = {
12 | condition: 'true'
13 | }
14 | var input = `
15 | True
16 | `
17 | var expect = {
18 | "tag": "wx-body",
19 | "attr": {},
20 | "children": [
21 | {
22 | "tag": "wx-view",
23 | "attr": {
24 | "wx:if": "true"
25 | },
26 | "children": [
27 | "True"
28 | ]
29 | }
30 | ]
31 | }
32 | assertObject(parser(input, data), expect)
33 | });
34 |
35 | it('wx:if/else', function() {
36 | var data = {
37 | length: 7
38 | }
39 | var input = `
40 | 1
41 | 3
42 | `
43 | var expect = {
44 | "tag": "wx-body",
45 | "attr": {},
46 | "children": [
47 | {
48 | "tag": "wx-view",
49 | "attr": {
50 | "wx:if": true
51 | },
52 | "children": [
53 | "1"
54 | ]
55 | }
56 | ]
57 | }
58 | assertObject(parser(input, data), expect)
59 | });
60 |
61 | it('wx:if/else embedding', function() {
62 | var data = {
63 | length: 7
64 | }
65 | var input = `
66 | 1
67 |
68 | 3
69 | 4
70 | 5
71 |
72 | `
73 | var expect = {
74 | "tag": "wx-body",
75 | "attr": {},
76 | "children": [
77 | {
78 | "tag": "wx-view",
79 | "attr": {
80 | "wx:else": ""
81 | },
82 | "children": [
83 | "3",
84 | {
85 | "tag": "wx-view",
86 | "attr": {
87 | "wx:if": true
88 | },
89 | "children": [
90 | "4"
91 | ]
92 | }
93 | ]
94 | }
95 | ]
96 | }
97 | assertObject(parser(input, data), expect)
98 | });
99 |
100 | it('wx:if/elif/else', function() {
101 | var data = {
102 | length: 3
103 | }
104 | var input = `
105 | 1
106 | 2
107 | 3
108 | `
109 | var expect = {
110 | "tag": "wx-body",
111 | "attr": {},
112 | "children": [
113 | {
114 | "tag": "wx-view",
115 | "attr": {
116 | "wx:elif": true
117 | },
118 | "children": [
119 | "2"
120 | ]
121 | }
122 | ]
123 | }
124 | assertObject(parser(input, data), expect)
125 | });
126 |
127 | it('block wx:if', function() {
128 | var data = {}
129 | var input = `
130 |
131 | view1
132 | view2
133 |
134 | `
135 | var expect = {
136 | "tag": "wx-body",
137 | "attr": {},
138 | "children": [
139 | {
140 | "tag": "wx-view",
141 | "attr": {},
142 | "children": [
143 | "view1"
144 | ]
145 | },
146 | {
147 | "tag": "wx-view",
148 | "attr": {},
149 | "children": [
150 | "view2"
151 | ]
152 | }
153 | ]
154 | }
155 | assertObject(parser(input, data), expect)
156 | });
157 |
158 |
159 | it('block wx:else', function() {
160 | var data = {}
161 | var input = `
162 |
163 | view1
164 | view2
165 |
166 |
167 | view3
168 | view4
169 |
170 | `
171 | var expect = {
172 | "tag": "wx-body",
173 | "attr": {},
174 | "children": [
175 | {
176 | "tag": "wx-view",
177 | "attr": {},
178 | "children": [
179 | "view3"
180 | ]
181 | },
182 | {
183 | "tag": "wx-view",
184 | "attr": {},
185 | "children": [
186 | "view4"
187 | ]
188 | }
189 | ]
190 | }
191 | assertObject(parser(input, data), expect)
192 | });
193 |
194 | })
--------------------------------------------------------------------------------
/test/databinding.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 | var parser = require('../index.js')
3 |
4 | function assertObject(result, expect) {
5 | return assert.equal(JSON.stringify(result), JSON.stringify(expect))
6 | }
7 |
8 | describe('Data binding', function() {
9 |
10 | describe('Simple binding', function() {
11 |
12 | it('data content', function() {
13 | var data = {
14 | message: 'Hello MINA!'
15 | }
16 | var input = `
17 | {{ message }}
18 | `
19 | var expect = {
20 | "tag": "wx-body",
21 | "attr": {},
22 | "children": [
23 | {
24 | "tag": "wx-view",
25 | "attr": {},
26 | "children": [
27 | "Hello MINA!"
28 | ]
29 | }
30 | ]
31 | }
32 | assertObject(parser(input, data), expect)
33 | });
34 |
35 | it('data content no single root', function() {
36 | var data = {
37 | message: 'Hello MINA!'
38 | }
39 | var input = `
40 | {{ message }}
41 | {{ message }}
42 | `
43 | var expect = {
44 | "tag": "wx-body",
45 | "attr": {},
46 | "children": [
47 | {
48 | "tag": "wx-view",
49 | "attr": {},
50 | "children": [
51 | "Hello MINA!"
52 | ]
53 | },
54 | {
55 | "tag": "wx-view",
56 | "attr": {},
57 | "children": [
58 | "Hello MINA!"
59 | ]
60 | }
61 | ]
62 | }
63 | assertObject(parser(input, data), expect)
64 | });
65 |
66 | it('widget attribute', function() {
67 | var data = {
68 | id: 0
69 | }
70 | var input = `
71 |
72 | `
73 | var expect = {
74 | "tag": "wx-body",
75 | "attr": {},
76 | "children": [
77 | {
78 | "tag": "wx-view",
79 | "attr": {
80 | "id": "item-0"
81 | },
82 | "children": []
83 | }
84 | ]
85 | }
86 | assertObject(parser(input, data), expect)
87 | });
88 |
89 | it('control attribute: true', function() {
90 | var data = {
91 | condition: true
92 | }
93 | var input = `
94 |
95 | `
96 | var expect = {
97 | "tag": "wx-body",
98 | "attr": {},
99 | "children": [
100 | {
101 | "tag": "wx-view",
102 | "attr": {
103 | "wx:if": true
104 | },
105 | "children": []
106 | }
107 | ]
108 | }
109 | assertObject(parser(input, data), expect)
110 | });
111 |
112 | it('control attribute: false', function() {
113 | var data = {
114 | condition: false
115 | }
116 | var input = `
117 |
118 | `
119 | var expect = {
120 | "tag": "wx-body",
121 | "attr": {},
122 | "children": []
123 | }
124 | assertObject(parser(input, data), expect)
125 | });
126 |
127 | })
128 |
129 |
130 | describe('Computation', function() {
131 |
132 | it('ternary operation', function() {
133 | var data = {
134 | flag: 1
135 | }
136 | var input = `
137 | Hidden
138 | `
139 | var expect = {
140 | "tag": "wx-body",
141 | "attr": {},
142 | "children": [
143 | {
144 | "tag": "wx-view",
145 | "attr": {
146 | "hidden": true
147 | },
148 | "children": [
149 | "Hidden"
150 | ]
151 | }
152 | ]
153 | }
154 | assertObject(parser(input, data), expect)
155 | });
156 |
157 | it('arithmetic operation', function() {
158 | var data = {
159 | a: 1,
160 | b: 2,
161 | c: 3
162 | }
163 | var input = `
164 | {{a + b}} + {{c}} + d
165 | `
166 | var expect = {
167 | "tag": "wx-body",
168 | "attr": {},
169 | "children": [
170 | {
171 | "tag": "wx-view",
172 | "attr": {},
173 | "children": [
174 | "3 + 3 + d"
175 | ]
176 | }
177 | ]
178 | }
179 | assertObject(parser(input, data), expect)
180 | });
181 |
182 | it('logical operation', function() {
183 | var data = {
184 | length: 4
185 | }
186 | var input = `
187 |
188 | `
189 | var expect = {
190 | "tag": "wx-body",
191 | "attr": {},
192 | "children": []
193 | }
194 | assertObject(parser(input, data), expect)
195 | });
196 |
197 | it('string operation', function() {
198 | var data = {
199 | name: 'MINA'
200 | }
201 | var input = `
202 | {{"hello" + name}}
203 | `
204 | var expect = {
205 | "tag": "wx-body",
206 | "attr": {},
207 | "children": [
208 | {
209 | "tag": "wx-view",
210 | "attr": {},
211 | "children": [
212 | "helloMINA"
213 | ]
214 | }
215 | ]
216 | }
217 | assertObject(parser(input, data), expect)
218 | });
219 |
220 | })
221 |
222 | describe('Compositing', function() {
223 |
224 | it('array', function() {
225 | var data = {
226 | zero: 0
227 | }
228 | var input = `
229 | {{item}}
230 | `
231 | var expect = {
232 | "tag": "wx-body",
233 | "attr": {},
234 | "children": [
235 | {
236 | "tag": "wx-view",
237 | "attr": {},
238 | "children": [
239 | 0
240 | ]
241 | },
242 | {
243 | "tag": "wx-view",
244 | "attr": {},
245 | "children": [
246 | 1
247 | ]
248 | },
249 | {
250 | "tag": "wx-view",
251 | "attr": {},
252 | "children": [
253 | 2
254 | ]
255 | },
256 | {
257 | "tag": "wx-view",
258 | "attr": {},
259 | "children": [
260 | 3
261 | ]
262 | },
263 | {
264 | "tag": "wx-view",
265 | "attr": {},
266 | "children": [
267 | 4
268 | ]
269 | }
270 | ]
271 | }
272 | assertObject(parser(input, data), expect)
273 | });
274 |
275 | it('object', function() {
276 | var data = {
277 | a: 1,
278 | b: 2
279 | }
280 | var input = `
281 |
282 | `
283 | var expect = {
284 | "tag": "wx-body",
285 | "attr": {},
286 | "children": [
287 | {
288 | "tag": "wx-xxx",
289 | "attr": {
290 | "is": "objectCombine",
291 | "data": {
292 | "foo": 1,
293 | "bar": 2
294 | }
295 | },
296 | "children": []
297 | }
298 | ]
299 | }
300 | assertObject(parser(input, data), expect)
301 | });
302 |
303 | // TODO add babel eval transform support
304 | // it('object expand', function() {
305 | // var data = {
306 | // obj1: {
307 | // a: 1,
308 | // b: 2
309 | // }
310 | // }
311 | // var input = `
312 | //
313 | // `
314 | // var expect = {
315 | // }
316 | // console.log(JSON.stringify(parser(input, data)))
317 | // assertObject(parser(input, data), expect)
318 | // });
319 |
320 | })
321 |
322 | });
--------------------------------------------------------------------------------
/test/list.test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 | var parser = require('../index.js')
3 |
4 | function assertObject(result, expect) {
5 | return assert.equal(JSON.stringify(result), JSON.stringify(expect))
6 | }
7 |
8 | describe('List rendering', function() {
9 |
10 | it('wx:for', function() {
11 | var data = {
12 | items: [{
13 | message: 'foo',
14 | }, {
15 | message: 'bar'
16 | }]
17 | }
18 | var input = `
19 |
20 | {{index}}: {{item.message}}
21 |
22 | `
23 | var expect = {
24 | "tag": "wx-body",
25 | "attr": {},
26 | "children": [
27 | {
28 | "tag": "wx-view",
29 | "attr": {},
30 | "children": [
31 | "0: foo"
32 | ]
33 | },
34 | {
35 | "tag": "wx-view",
36 | "attr": {},
37 | "children": [
38 | "1: bar"
39 | ]
40 | }
41 | ]
42 | }
43 | assertObject(parser(input, data), expect)
44 | });
45 |
46 | it('wx:for-item wx:for-index', function() {
47 | var data = {
48 | array: [
49 | { message: 'a' },
50 | { message: 'b' }
51 | ]
52 | }
53 | var input = `
54 |
55 | {{idx}}: {{itemName.message}}
56 |
57 | `
58 | var expect = {
59 | "tag": "wx-body",
60 | "attr": {},
61 | "children": [
62 | {
63 | "tag": "wx-view",
64 | "attr": {},
65 | "children": [
66 | "0: a"
67 | ]
68 | },
69 | {
70 | "tag": "wx-view",
71 | "attr": {},
72 | "children": [
73 | "1: b"
74 | ]
75 | }
76 | ]
77 | }
78 | assertObject(parser(input, data), expect)
79 | });
80 |
81 | it('wx:for embedding', function() {
82 | var data = {}
83 | var input = `
84 |
85 |
86 |
87 | {{i}} * {{j}} = {{i * j}}
88 |
89 |
90 |
91 | `
92 | var expect = {
93 | "tag": "wx-body",
94 | "attr": {},
95 | "children": [
96 | {
97 | "tag": "wx-view",
98 | "attr": {},
99 | "children": [
100 | {
101 | "tag": "wx-view",
102 | "attr": {},
103 | "children": [
104 | {
105 | "tag": "wx-view",
106 | "attr": {
107 | "wx:if": true
108 | },
109 | "children": [
110 | "1 * 1 = 1"
111 | ]
112 | }
113 | ]
114 | },
115 | {
116 | "tag": "wx-view",
117 | "attr": {},
118 | "children": [
119 | {
120 | "tag": "wx-view",
121 | "attr": {
122 | "wx:if": true
123 | },
124 | "children": [
125 | "1 * 2 = 2"
126 | ]
127 | }
128 | ]
129 | },
130 | {
131 | "tag": "wx-view",
132 | "attr": {},
133 | "children": [
134 | {
135 | "tag": "wx-view",
136 | "attr": {
137 | "wx:if": true
138 | },
139 | "children": [
140 | "1 * 3 = 3"
141 | ]
142 | }
143 | ]
144 | }
145 | ]
146 | },
147 | {
148 | "tag": "wx-view",
149 | "attr": {},
150 | "children": [
151 | {
152 | "tag": "wx-view",
153 | "attr": {},
154 | "children": []
155 | },
156 | {
157 | "tag": "wx-view",
158 | "attr": {},
159 | "children": [
160 | {
161 | "tag": "wx-view",
162 | "attr": {
163 | "wx:if": true
164 | },
165 | "children": [
166 | "2 * 2 = 4"
167 | ]
168 | }
169 | ]
170 | },
171 | {
172 | "tag": "wx-view",
173 | "attr": {},
174 | "children": [
175 | {
176 | "tag": "wx-view",
177 | "attr": {
178 | "wx:if": true
179 | },
180 | "children": [
181 | "2 * 3 = 6"
182 | ]
183 | }
184 | ]
185 | }
186 | ]
187 | },
188 | {
189 | "tag": "wx-view",
190 | "attr": {},
191 | "children": [
192 | {
193 | "tag": "wx-view",
194 | "attr": {},
195 | "children": []
196 | },
197 | {
198 | "tag": "wx-view",
199 | "attr": {},
200 | "children": []
201 | },
202 | {
203 | "tag": "wx-view",
204 | "attr": {},
205 | "children": [
206 | {
207 | "tag": "wx-view",
208 | "attr": {
209 | "wx:if": true
210 | },
211 | "children": [
212 | "3 * 3 = 9"
213 | ]
214 | }
215 | ]
216 | }
217 | ]
218 | }
219 | ]
220 | }
221 | assertObject(parser(input, data), expect)
222 | });
223 |
224 | it('block wx:for', function() {
225 | var data = {}
226 | var input = `
227 |
228 | {{index}}:
229 | {{item}}
230 |
231 | `
232 | var expect = {
233 | "tag": "wx-body",
234 | "attr": {},
235 | "children": [
236 | {
237 | "tag": "wx-view",
238 | "attr": {},
239 | "children": [
240 | "0:"
241 | ]
242 | },
243 | {
244 | "tag": "wx-view",
245 | "attr": {},
246 | "children": [
247 | 1
248 | ]
249 | },
250 | {
251 | "tag": "wx-view",
252 | "attr": {},
253 | "children": [
254 | "1:"
255 | ]
256 | },
257 | {
258 | "tag": "wx-view",
259 | "attr": {},
260 | "children": [
261 | 2
262 | ]
263 | },
264 | {
265 | "tag": "wx-view",
266 | "attr": {},
267 | "children": [
268 | "2:"
269 | ]
270 | },
271 | {
272 | "tag": "wx-view",
273 | "attr": {},
274 | "children": [
275 | 3
276 | ]
277 | }
278 | ]
279 | }
280 | //console.log(JSON.stringify(parser(input, data), null, 2));
281 | assertObject(parser(input, data), expect)
282 | });
283 |
284 | })
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mocha Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | require('./databinding.test.js')
2 | require('./condition.test.js')
3 | require('./list.test.js')
--------------------------------------------------------------------------------