art-template 4.13.2 → 4.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/README.md +6 -12
- package/index.d.ts +2 -2
- package/lib/template-web.js +2 -2
- package/package.json +3 -3
- package/CHANGELOG.md +0 -216
- package/lib/compile/adapter/caches.js +0 -19
- package/lib/compile/adapter/extend.js +0 -37
- package/lib/compile/adapter/html-minifier.js +0 -29
- package/lib/compile/adapter/include.js +0 -21
- package/lib/compile/adapter/loader.js +0 -22
- package/lib/compile/adapter/onerror.js +0 -13
- package/lib/compile/adapter/resolve-filename.js +0 -36
- package/lib/compile/adapter/rule.art.js +0 -187
- package/lib/compile/adapter/rule.native.js +0 -34
- package/lib/compile/compiler.js +0 -470
- package/lib/compile/defaults.js +0 -95
- package/lib/compile/error.js +0 -60
- package/lib/compile/es-tokenizer.js +0 -26
- package/lib/compile/index.js +0 -136
- package/lib/compile/runtime.js +0 -99
- package/lib/compile/tpl-tokenizer.js +0 -98
- package/lib/defaults.js +0 -3
- package/lib/extension.js +0 -21
- package/lib/index.js +0 -26
- package/lib/precompile.js +0 -242
- package/lib/render.js +0 -16
- package/lib/runtime.js +0 -3
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 简洁模板语法规则
|
|
5
|
-
*/
|
|
6
|
-
var artRule = {
|
|
7
|
-
test: /{{([@#]?)[ \t]*(\/?)([\w\W]*?)[ \t]*}}/,
|
|
8
|
-
use: function use(match, raw, close, code) {
|
|
9
|
-
var compiler = this;
|
|
10
|
-
var options = compiler.options;
|
|
11
|
-
var esTokens = compiler.getEsTokens(code);
|
|
12
|
-
var values = esTokens.map(function (token) {
|
|
13
|
-
return token.value;
|
|
14
|
-
});
|
|
15
|
-
var result = {};
|
|
16
|
-
|
|
17
|
-
var group = void 0;
|
|
18
|
-
var output = raw ? 'raw' : false;
|
|
19
|
-
var key = close + values.shift();
|
|
20
|
-
|
|
21
|
-
// 旧版语法升级提示
|
|
22
|
-
var warn = function warn(oldSyntax, newSyntax) {
|
|
23
|
-
console.warn((options.filename || 'anonymous') + ':' + (match.line + 1) + ':' + (match.start + 1) + '\n' + ('Template upgrade: {{' + oldSyntax + '}} -> {{' + newSyntax + '}}'));
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// v3 compat: #value
|
|
27
|
-
if (raw === '#') {
|
|
28
|
-
warn('#value', '@value');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
switch (key) {
|
|
32
|
-
case 'set':
|
|
33
|
-
code = 'var ' + values.join('').trim();
|
|
34
|
-
break;
|
|
35
|
-
|
|
36
|
-
case 'if':
|
|
37
|
-
code = 'if(' + values.join('').trim() + '){';
|
|
38
|
-
|
|
39
|
-
break;
|
|
40
|
-
|
|
41
|
-
case 'else':
|
|
42
|
-
var indexIf = values.indexOf('if');
|
|
43
|
-
|
|
44
|
-
if (~indexIf) {
|
|
45
|
-
values.splice(0, indexIf + 1);
|
|
46
|
-
code = '}else if(' + values.join('').trim() + '){';
|
|
47
|
-
} else {
|
|
48
|
-
code = '}else{';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
break;
|
|
52
|
-
|
|
53
|
-
case '/if':
|
|
54
|
-
code = '}';
|
|
55
|
-
break;
|
|
56
|
-
|
|
57
|
-
case 'each':
|
|
58
|
-
group = artRule._split(esTokens);
|
|
59
|
-
group.shift();
|
|
60
|
-
|
|
61
|
-
if (group[1] === 'as') {
|
|
62
|
-
// ... v3 compat ...
|
|
63
|
-
warn('each object as value index', 'each object value index');
|
|
64
|
-
group.splice(1, 1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
var object = group[0] || '$data';
|
|
68
|
-
var value = group[1] || '$value';
|
|
69
|
-
var index = group[2] || '$index';
|
|
70
|
-
|
|
71
|
-
code = '$each(' + object + ',function(' + value + ',' + index + '){';
|
|
72
|
-
|
|
73
|
-
break;
|
|
74
|
-
|
|
75
|
-
case '/each':
|
|
76
|
-
code = '})';
|
|
77
|
-
break;
|
|
78
|
-
|
|
79
|
-
case 'block':
|
|
80
|
-
group = artRule._split(esTokens);
|
|
81
|
-
group.shift();
|
|
82
|
-
code = 'block(' + group.join(',').trim() + ',function(){';
|
|
83
|
-
break;
|
|
84
|
-
|
|
85
|
-
case '/block':
|
|
86
|
-
code = '})';
|
|
87
|
-
break;
|
|
88
|
-
|
|
89
|
-
case 'echo':
|
|
90
|
-
key = 'print';
|
|
91
|
-
warn('echo value', 'value');
|
|
92
|
-
case 'print':
|
|
93
|
-
case 'include':
|
|
94
|
-
case 'extend':
|
|
95
|
-
if (values.join('').trim().indexOf('(') !== 0) {
|
|
96
|
-
// 执行函数省略 `()` 与 `,`
|
|
97
|
-
group = artRule._split(esTokens);
|
|
98
|
-
group.shift();
|
|
99
|
-
code = key + '(' + group.join(',') + ')';
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
default:
|
|
104
|
-
if (~values.indexOf('|')) {
|
|
105
|
-
var v3split = ':'; // ... v3 compat ...
|
|
106
|
-
|
|
107
|
-
// 将过滤器解析成二维数组
|
|
108
|
-
var _group = esTokens.reduce(function (group, token) {
|
|
109
|
-
var value = token.value,
|
|
110
|
-
type = token.type;
|
|
111
|
-
|
|
112
|
-
if (value === '|') {
|
|
113
|
-
group.push([]);
|
|
114
|
-
} else if (type !== 'whitespace' && type !== 'comment') {
|
|
115
|
-
if (!group.length) {
|
|
116
|
-
group.push([]);
|
|
117
|
-
}
|
|
118
|
-
if (value === v3split && group[group.length - 1].length === 1) {
|
|
119
|
-
warn('value | filter: argv', 'value | filter argv');
|
|
120
|
-
} else {
|
|
121
|
-
group[group.length - 1].push(token);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return group;
|
|
125
|
-
}, []).map(function (g) {
|
|
126
|
-
return artRule._split(g);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// 将过滤器管道化
|
|
130
|
-
code = _group.reduce(function (accumulator, filter) {
|
|
131
|
-
var name = filter.shift();
|
|
132
|
-
filter.unshift(accumulator);
|
|
133
|
-
|
|
134
|
-
return '$imports.' + name + '(' + filter.join(',') + ')';
|
|
135
|
-
}, _group.shift().join(' ').trim());
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
output = output || 'escape';
|
|
139
|
-
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
result.code = code;
|
|
144
|
-
result.output = output;
|
|
145
|
-
|
|
146
|
-
return result;
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
// 将多个 javascript 表达式拆分成组
|
|
150
|
-
// 支持基本运算、三元表达式、取值、运行函数,不支持 `typeof value` 操作
|
|
151
|
-
// 只支持 string、number、boolean、null、undefined 这几种类型声明,不支持 function、object、array
|
|
152
|
-
_split: function _split(esTokens) {
|
|
153
|
-
esTokens = esTokens.filter(function (_ref) {
|
|
154
|
-
var type = _ref.type;
|
|
155
|
-
|
|
156
|
-
return type !== 'whitespace' && type !== 'comment';
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
var current = 0;
|
|
160
|
-
var lastToken = esTokens.shift();
|
|
161
|
-
var punctuator = 'punctuator';
|
|
162
|
-
var close = /\]|\)/;
|
|
163
|
-
var group = [[lastToken]];
|
|
164
|
-
|
|
165
|
-
while (current < esTokens.length) {
|
|
166
|
-
var esToken = esTokens[current];
|
|
167
|
-
|
|
168
|
-
if (esToken.type === punctuator || lastToken.type === punctuator && !close.test(lastToken.value)) {
|
|
169
|
-
group[group.length - 1].push(esToken);
|
|
170
|
-
} else {
|
|
171
|
-
group.push([esToken]);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
lastToken = esToken;
|
|
175
|
-
|
|
176
|
-
current++;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return group.map(function (g) {
|
|
180
|
-
return g.map(function (g) {
|
|
181
|
-
return g.value;
|
|
182
|
-
}).join('');
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
module.exports = artRule;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 原生模板语法规则
|
|
5
|
-
*/
|
|
6
|
-
var nativeRule = {
|
|
7
|
-
test: /<%(#?)((?:==|=#|[=-])?)[ \t]*([\w\W]*?)[ \t]*(-?)%>/,
|
|
8
|
-
use: function use(match, comment, output, code /*, trimMode*/) {
|
|
9
|
-
output = {
|
|
10
|
-
'-': 'raw',
|
|
11
|
-
'=': 'escape',
|
|
12
|
-
'': false,
|
|
13
|
-
// v3 compat: raw output
|
|
14
|
-
'==': 'raw',
|
|
15
|
-
'=#': 'raw'
|
|
16
|
-
}[output];
|
|
17
|
-
|
|
18
|
-
// ejs compat: comment tag
|
|
19
|
-
if (comment) {
|
|
20
|
-
code = '/*' + code + '*/';
|
|
21
|
-
output = false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ejs compat: trims following newline
|
|
25
|
-
// if (trimMode) {}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
code: code,
|
|
29
|
-
output: output
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
module.exports = nativeRule;
|
package/lib/compile/compiler.js
DELETED
|
@@ -1,470 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
4
|
-
|
|
5
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
|
|
7
|
-
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
8
|
-
|
|
9
|
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
10
|
-
|
|
11
|
-
var esTokenizer = require('./es-tokenizer');
|
|
12
|
-
var tplTokenizer = require('./tpl-tokenizer');
|
|
13
|
-
|
|
14
|
-
/** 传递给模板的数据引用 */
|
|
15
|
-
var DATA = '$data';
|
|
16
|
-
|
|
17
|
-
/** 外部导入的所有全局变量引用 */
|
|
18
|
-
var IMPORTS = '$imports';
|
|
19
|
-
|
|
20
|
-
/** $imports.$escape */
|
|
21
|
-
var ESCAPE = '$escape';
|
|
22
|
-
|
|
23
|
-
/** $imports.$each */
|
|
24
|
-
var EACH = '$each';
|
|
25
|
-
|
|
26
|
-
/** 文本输出函数 */
|
|
27
|
-
var PRINT = 'print';
|
|
28
|
-
|
|
29
|
-
/** 包含子模板函数 */
|
|
30
|
-
var INCLUDE = 'include';
|
|
31
|
-
|
|
32
|
-
/** 继承布局模板函数 */
|
|
33
|
-
var EXTEND = 'extend';
|
|
34
|
-
|
|
35
|
-
/** “模板块”读写函数 */
|
|
36
|
-
var BLOCK = 'block';
|
|
37
|
-
|
|
38
|
-
/** 字符串拼接变量 */
|
|
39
|
-
var OUT = '$$out';
|
|
40
|
-
|
|
41
|
-
/** 运行时逐行调试记录变量 [line, start, source] */
|
|
42
|
-
var LINE = '$$line';
|
|
43
|
-
|
|
44
|
-
/** 所有“模板块”变量 */
|
|
45
|
-
var BLOCKS = '$$blocks';
|
|
46
|
-
|
|
47
|
-
/** 截取模版输出“流”的函数 */
|
|
48
|
-
var SLICE = '$$slice';
|
|
49
|
-
|
|
50
|
-
/** 继承的布局模板的文件地址变量 */
|
|
51
|
-
var FROM = '$$from';
|
|
52
|
-
|
|
53
|
-
/** 编译设置变量 */
|
|
54
|
-
var OPTIONS = '$$options';
|
|
55
|
-
|
|
56
|
-
var has = function has(object, key) {
|
|
57
|
-
return Object.hasOwnProperty.call(object, key);
|
|
58
|
-
};
|
|
59
|
-
var stringify = JSON.stringify;
|
|
60
|
-
|
|
61
|
-
var Compiler = function () {
|
|
62
|
-
/**
|
|
63
|
-
* 模板编译器
|
|
64
|
-
* @param {Object} options
|
|
65
|
-
*/
|
|
66
|
-
function Compiler(options) {
|
|
67
|
-
var _internal,
|
|
68
|
-
_dependencies,
|
|
69
|
-
_this = this;
|
|
70
|
-
|
|
71
|
-
_classCallCheck(this, Compiler);
|
|
72
|
-
|
|
73
|
-
var source = options.source;
|
|
74
|
-
var minimize = options.minimize;
|
|
75
|
-
var htmlMinifier = options.htmlMinifier;
|
|
76
|
-
|
|
77
|
-
// 编译选项
|
|
78
|
-
this.options = options;
|
|
79
|
-
|
|
80
|
-
// 所有语句堆栈
|
|
81
|
-
this.stacks = [];
|
|
82
|
-
|
|
83
|
-
// 运行时注入的上下文
|
|
84
|
-
this.context = [];
|
|
85
|
-
|
|
86
|
-
// 模板语句编译后的代码
|
|
87
|
-
this.scripts = [];
|
|
88
|
-
|
|
89
|
-
// context map
|
|
90
|
-
this.CONTEXT_MAP = {};
|
|
91
|
-
|
|
92
|
-
// 忽略的变量名单
|
|
93
|
-
this.ignore = [DATA, IMPORTS, OPTIONS].concat(_toConsumableArray(options.ignore));
|
|
94
|
-
|
|
95
|
-
// 按需编译到模板渲染函数的内置变量
|
|
96
|
-
this.internal = (_internal = {}, _defineProperty(_internal, OUT, '\'\''), _defineProperty(_internal, LINE, '[0,0]'), _defineProperty(_internal, BLOCKS, 'arguments[1]||{}'), _defineProperty(_internal, FROM, 'null'), _defineProperty(_internal, PRINT, 'function(){var s=\'\'.concat.apply(\'\',arguments);' + OUT + '+=s;return s}'), _defineProperty(_internal, INCLUDE, 'function(src,data){var s=' + OPTIONS + '.include(src,data||' + DATA + ',arguments[2]||' + BLOCKS + ',' + OPTIONS + ');' + OUT + '+=s;return s}'), _defineProperty(_internal, EXTEND, 'function(from){' + FROM + '=from}'), _defineProperty(_internal, SLICE, 'function(c,p,s){p=' + OUT + ';' + OUT + '=\'\';c();s=' + OUT + ';' + OUT + '=p+s;return s}'), _defineProperty(_internal, BLOCK, 'function(){var a=arguments,s;if(typeof a[0]===\'function\'){return ' + SLICE + '(a[0])}else if(' + FROM + '){if(!' + BLOCKS + '[a[0]]){' + BLOCKS + '[a[0]]=' + SLICE + '(a[1])}else{' + OUT + '+=' + BLOCKS + '[a[0]]}}else{s=' + BLOCKS + '[a[0]];if(typeof s===\'string\'){' + OUT + '+=s}else{s=' + SLICE + '(a[1])}return s}}'), _internal);
|
|
97
|
-
|
|
98
|
-
// 内置函数依赖关系声明
|
|
99
|
-
this.dependencies = (_dependencies = {}, _defineProperty(_dependencies, PRINT, [OUT]), _defineProperty(_dependencies, INCLUDE, [OUT, OPTIONS, DATA, BLOCKS]), _defineProperty(_dependencies, EXTEND, [FROM, /*[*/INCLUDE /*]*/]), _defineProperty(_dependencies, BLOCK, [SLICE, FROM, OUT, BLOCKS]), _dependencies);
|
|
100
|
-
|
|
101
|
-
this.importContext(OUT);
|
|
102
|
-
|
|
103
|
-
if (options.compileDebug) {
|
|
104
|
-
this.importContext(LINE);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (minimize) {
|
|
108
|
-
try {
|
|
109
|
-
source = htmlMinifier(source, options);
|
|
110
|
-
} catch (error) {}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.source = source;
|
|
114
|
-
this.getTplTokens(source, options.rules, this).forEach(function (tokens) {
|
|
115
|
-
if (tokens.type === tplTokenizer.TYPE_STRING) {
|
|
116
|
-
_this.parseString(tokens);
|
|
117
|
-
} else {
|
|
118
|
-
_this.parseExpression(tokens);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* 将模板代码转换成 tplToken 数组
|
|
125
|
-
* @param {string} source
|
|
126
|
-
* @return {Object[]}
|
|
127
|
-
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
_createClass(Compiler, [{
|
|
131
|
-
key: 'getTplTokens',
|
|
132
|
-
value: function getTplTokens() {
|
|
133
|
-
return tplTokenizer.apply(undefined, arguments);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 将模板表达式转换成 esToken 数组
|
|
138
|
-
* @param {string} source
|
|
139
|
-
* @return {Object[]}
|
|
140
|
-
*/
|
|
141
|
-
|
|
142
|
-
}, {
|
|
143
|
-
key: 'getEsTokens',
|
|
144
|
-
value: function getEsTokens(source) {
|
|
145
|
-
return esTokenizer(source);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 获取变量列表
|
|
150
|
-
* @param {Object[]} esTokens
|
|
151
|
-
* @return {string[]}
|
|
152
|
-
*/
|
|
153
|
-
|
|
154
|
-
}, {
|
|
155
|
-
key: 'getVariables',
|
|
156
|
-
value: function getVariables(esTokens) {
|
|
157
|
-
var ignore = false;
|
|
158
|
-
return esTokens.filter(function (esToken) {
|
|
159
|
-
return esToken.type !== 'whitespace' && esToken.type !== 'comment';
|
|
160
|
-
}).filter(function (esToken) {
|
|
161
|
-
if (esToken.type === 'name' && !ignore) {
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
ignore = esToken.type === 'punctuator' && esToken.value === '.';
|
|
166
|
-
|
|
167
|
-
return false;
|
|
168
|
-
}).map(function (tooken) {
|
|
169
|
-
return tooken.value;
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* 导入模板上下文
|
|
175
|
-
* @param {string} name
|
|
176
|
-
*/
|
|
177
|
-
|
|
178
|
-
}, {
|
|
179
|
-
key: 'importContext',
|
|
180
|
-
value: function importContext(name) {
|
|
181
|
-
var _this2 = this;
|
|
182
|
-
|
|
183
|
-
var value = '';
|
|
184
|
-
var internal = this.internal;
|
|
185
|
-
var dependencies = this.dependencies;
|
|
186
|
-
var ignore = this.ignore;
|
|
187
|
-
var context = this.context;
|
|
188
|
-
var options = this.options;
|
|
189
|
-
var imports = options.imports;
|
|
190
|
-
var contextMap = this.CONTEXT_MAP;
|
|
191
|
-
|
|
192
|
-
if (!has(contextMap, name) && ignore.indexOf(name) === -1) {
|
|
193
|
-
if (has(internal, name)) {
|
|
194
|
-
value = internal[name];
|
|
195
|
-
|
|
196
|
-
if (has(dependencies, name)) {
|
|
197
|
-
dependencies[name].forEach(function (name) {
|
|
198
|
-
return _this2.importContext(name);
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// imports 继承了 Global,但是继承的属性不分配到顶级变量中,避免占用了模板内部的变量名称
|
|
203
|
-
} else if (name === ESCAPE || name === EACH || has(imports, name)) {
|
|
204
|
-
value = IMPORTS + '.' + name;
|
|
205
|
-
} else {
|
|
206
|
-
value = DATA + '.' + name;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
contextMap[name] = value;
|
|
210
|
-
context.push({
|
|
211
|
-
name: name,
|
|
212
|
-
value: value
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* 解析字符串(HTML)直接输出语句
|
|
219
|
-
* @param {Object} tplToken
|
|
220
|
-
*/
|
|
221
|
-
|
|
222
|
-
}, {
|
|
223
|
-
key: 'parseString',
|
|
224
|
-
value: function parseString(tplToken) {
|
|
225
|
-
var source = tplToken.value;
|
|
226
|
-
|
|
227
|
-
if (!source) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
var code = OUT + '+=' + stringify(source);
|
|
232
|
-
this.scripts.push({
|
|
233
|
-
source: source,
|
|
234
|
-
tplToken: tplToken,
|
|
235
|
-
code: code
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* 解析逻辑表达式语句
|
|
241
|
-
* @param {Object} tplToken
|
|
242
|
-
*/
|
|
243
|
-
|
|
244
|
-
}, {
|
|
245
|
-
key: 'parseExpression',
|
|
246
|
-
value: function parseExpression(tplToken) {
|
|
247
|
-
var _this3 = this;
|
|
248
|
-
|
|
249
|
-
var source = tplToken.value;
|
|
250
|
-
var script = tplToken.script;
|
|
251
|
-
var output = script.output;
|
|
252
|
-
var escape = this.options.escape;
|
|
253
|
-
var code = script.code;
|
|
254
|
-
|
|
255
|
-
if (output) {
|
|
256
|
-
if (escape === false || output === tplTokenizer.TYPE_RAW) {
|
|
257
|
-
code = OUT + '+=' + script.code;
|
|
258
|
-
} else {
|
|
259
|
-
code = OUT + '+=' + ESCAPE + '(' + script.code + ')';
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
var esToken = this.getEsTokens(code);
|
|
264
|
-
this.getVariables(esToken).forEach(function (name) {
|
|
265
|
-
return _this3.importContext(name);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
this.scripts.push({
|
|
269
|
-
source: source,
|
|
270
|
-
tplToken: tplToken,
|
|
271
|
-
code: code
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* 检查解析后的模板语句是否存在语法错误
|
|
277
|
-
* @param {string} script
|
|
278
|
-
* @return {boolean}
|
|
279
|
-
*/
|
|
280
|
-
|
|
281
|
-
}, {
|
|
282
|
-
key: 'checkExpression',
|
|
283
|
-
value: function checkExpression(script) {
|
|
284
|
-
// 没有闭合的块级模板语句规则
|
|
285
|
-
// 基于正则规则来补全语法不能保证 100% 准确,
|
|
286
|
-
// 但是在绝大多数情况下足以满足辅助开发调试的需要
|
|
287
|
-
var rules = [
|
|
288
|
-
// <% } %>
|
|
289
|
-
// <% }else{ %>
|
|
290
|
-
// <% }else if(a){ %>
|
|
291
|
-
[/^\s*}[\w\W]*?{?[\s;]*$/, ''],
|
|
292
|
-
|
|
293
|
-
// <% fn(c,function(a,b){ %>
|
|
294
|
-
// <% fn(c, a=>{ %>
|
|
295
|
-
// <% fn(c,(a,b)=>{ %>
|
|
296
|
-
[/(^[\w\W]*?\([\w\W]*?(?:=>|\([\w\W]*?\))\s*{[\s;]*$)/, '$1})'],
|
|
297
|
-
|
|
298
|
-
// <% if(a){ %>
|
|
299
|
-
// <% for(var i in d){ %>
|
|
300
|
-
[/(^[\w\W]*?\([\w\W]*?\)\s*{[\s;]*$)/, '$1}']];
|
|
301
|
-
|
|
302
|
-
var index = 0;
|
|
303
|
-
while (index < rules.length) {
|
|
304
|
-
if (rules[index][0].test(script)) {
|
|
305
|
-
var _script;
|
|
306
|
-
|
|
307
|
-
script = (_script = script).replace.apply(_script, _toConsumableArray(rules[index]));
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
index++;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
new Function(script);
|
|
315
|
-
return true;
|
|
316
|
-
} catch (e) {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* 编译
|
|
323
|
-
* @return {function}
|
|
324
|
-
*/
|
|
325
|
-
|
|
326
|
-
}, {
|
|
327
|
-
key: 'build',
|
|
328
|
-
value: function build() {
|
|
329
|
-
var options = this.options;
|
|
330
|
-
var context = this.context;
|
|
331
|
-
var scripts = this.scripts;
|
|
332
|
-
var stacks = this.stacks;
|
|
333
|
-
var source = this.source;
|
|
334
|
-
var filename = options.filename;
|
|
335
|
-
var imports = options.imports;
|
|
336
|
-
var mappings = [];
|
|
337
|
-
var extendMode = has(this.CONTEXT_MAP, EXTEND);
|
|
338
|
-
|
|
339
|
-
var offsetLine = 0;
|
|
340
|
-
|
|
341
|
-
// Create SourceMap: mapping
|
|
342
|
-
var mapping = function mapping(code, _ref) {
|
|
343
|
-
var line = _ref.line,
|
|
344
|
-
start = _ref.start;
|
|
345
|
-
|
|
346
|
-
var node = {
|
|
347
|
-
generated: {
|
|
348
|
-
line: stacks.length + offsetLine + 1,
|
|
349
|
-
column: 1
|
|
350
|
-
},
|
|
351
|
-
original: {
|
|
352
|
-
line: line + 1,
|
|
353
|
-
column: start + 1
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
offsetLine += code.split(/\n/).length - 1;
|
|
358
|
-
return node;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
// Trim code
|
|
362
|
-
var trim = function trim(code) {
|
|
363
|
-
return code.replace(/^[\t ]+|[\t ]$/g, '');
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
stacks.push('function(' + DATA + '){');
|
|
367
|
-
stacks.push('\'use strict\'');
|
|
368
|
-
stacks.push(DATA + '=' + DATA + '||{}');
|
|
369
|
-
stacks.push('var ' + context.map(function (_ref2) {
|
|
370
|
-
var name = _ref2.name,
|
|
371
|
-
value = _ref2.value;
|
|
372
|
-
return name + '=' + value;
|
|
373
|
-
}).join(','));
|
|
374
|
-
|
|
375
|
-
if (options.compileDebug) {
|
|
376
|
-
stacks.push('try{');
|
|
377
|
-
|
|
378
|
-
scripts.forEach(function (script) {
|
|
379
|
-
if (script.tplToken.type === tplTokenizer.TYPE_EXPRESSION) {
|
|
380
|
-
stacks.push(LINE + '=[' + [script.tplToken.line, script.tplToken.start].join(',') + ']');
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
mappings.push(mapping(script.code, script.tplToken));
|
|
384
|
-
stacks.push(trim(script.code));
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
stacks.push('}catch(error){');
|
|
388
|
-
|
|
389
|
-
stacks.push('throw {' + ['name:\'RuntimeError\'', 'path:' + stringify(filename), 'message:error.message', 'line:' + LINE + '[0]+1', 'column:' + LINE + '[1]+1', 'source:' + stringify(source), 'stack:error.stack'].join(',') + '}');
|
|
390
|
-
|
|
391
|
-
stacks.push('}');
|
|
392
|
-
} else {
|
|
393
|
-
scripts.forEach(function (script) {
|
|
394
|
-
mappings.push(mapping(script.code, script.tplToken));
|
|
395
|
-
stacks.push(trim(script.code));
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (extendMode) {
|
|
400
|
-
stacks.push(OUT + '=\'\'');
|
|
401
|
-
stacks.push(INCLUDE + '(' + FROM + ',' + DATA + ',' + BLOCKS + ')');
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
stacks.push('return ' + OUT);
|
|
405
|
-
stacks.push('}');
|
|
406
|
-
|
|
407
|
-
var renderCode = stacks.join('\n');
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
var result = new Function(IMPORTS, OPTIONS, 'return ' + renderCode)(imports, options);
|
|
411
|
-
result.mappings = mappings;
|
|
412
|
-
result.sourcesContent = [source];
|
|
413
|
-
return result;
|
|
414
|
-
} catch (error) {
|
|
415
|
-
var index = 0;
|
|
416
|
-
var line = 0;
|
|
417
|
-
var start = 0;
|
|
418
|
-
var generated = void 0;
|
|
419
|
-
|
|
420
|
-
while (index < scripts.length) {
|
|
421
|
-
var current = scripts[index];
|
|
422
|
-
if (!this.checkExpression(current.code)) {
|
|
423
|
-
line = current.tplToken.line;
|
|
424
|
-
start = current.tplToken.start;
|
|
425
|
-
generated = current.code;
|
|
426
|
-
break;
|
|
427
|
-
}
|
|
428
|
-
index++;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
throw {
|
|
432
|
-
name: 'CompileError',
|
|
433
|
-
path: filename,
|
|
434
|
-
message: error.message,
|
|
435
|
-
line: line + 1,
|
|
436
|
-
column: start + 1,
|
|
437
|
-
source: source,
|
|
438
|
-
generated: generated,
|
|
439
|
-
stack: error.stack
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}]);
|
|
444
|
-
|
|
445
|
-
return Compiler;
|
|
446
|
-
}();
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* 模板内置常量
|
|
450
|
-
*/
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
Compiler.CONSTS = {
|
|
454
|
-
DATA: DATA,
|
|
455
|
-
IMPORTS: IMPORTS,
|
|
456
|
-
PRINT: PRINT,
|
|
457
|
-
INCLUDE: INCLUDE,
|
|
458
|
-
EXTEND: EXTEND,
|
|
459
|
-
BLOCK: BLOCK,
|
|
460
|
-
OPTIONS: OPTIONS,
|
|
461
|
-
OUT: OUT,
|
|
462
|
-
LINE: LINE,
|
|
463
|
-
BLOCKS: BLOCKS,
|
|
464
|
-
SLICE: SLICE,
|
|
465
|
-
FROM: FROM,
|
|
466
|
-
ESCAPE: ESCAPE,
|
|
467
|
-
EACH: EACH
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
module.exports = Compiler;
|