Skip to content

Commit b2a3c82

Browse files
committed
tmod loader init
1 parent f92a15f commit b2a3c82

File tree

6 files changed

+600
-0
lines changed

6 files changed

+600
-0
lines changed

index.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
var Tmod = require('./src/tmod');
4+
5+
module.exports = function (content) {
6+
7+
this.cacheable && this.cacheable();
8+
9+
var options = this.query ? JSON.parse(this.query.replace(/\?/g, "")) : {};
10+
11+
var tmod = new Tmod('./', options)
12+
var path = this.resourcePath.replace(/\\/g, "/");
13+
var output = tmod.compile(path);
14+
15+
if (!output) throw new Error(output); ;
16+
return output;
17+
}

package.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "tmod-loader",
3+
"version": "0.0.1",
4+
"description": "artTemplate loader module for webpack",
5+
"keywords": [
6+
"template",
7+
"artTemplate",
8+
"CommonJS",
9+
"TmodJS"
10+
],
11+
"author": "FlashHK",
12+
"dependencies": {
13+
"art-template": "3.0.x"
14+
}
15+
}

src/AOTcompile.js

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/*!
2+
* TmodJS - AOT Template Compiler
3+
* https://github.com/aui/tmodjs
4+
* Released under the MIT, BSD, and GPL Licenses
5+
*/
6+
7+
'use strict';
8+
9+
module.exports = function (template) {
10+
/**
11+
* 模板预编译器,根据设置生成不同格式的 javascript 模块
12+
* @param {String} 模板源代码
13+
* @param {Object} 选项
14+
*/
15+
template.AOTcompile = function (source, options) {
16+
17+
var uri = './' + options.filename;
18+
19+
// 是否为编译为调试版本
20+
var debug = options.debug;
21+
22+
// 模板名(不能以 . 或者 / 开头)
23+
var filename = options.filename;
24+
25+
// 编译的模块类型
26+
var type = options.type;
27+
28+
// 运行时模块别名。设置此后 runtime 的路径将被写死
29+
var alias = options.alias;
30+
31+
// 运行时名
32+
var runtime = options.runtime;
33+
34+
// 是否压缩 HTML 多余空白字符
35+
var compress = debug ? true : options.compress;
36+
37+
// 模板文件后缀
38+
var suffix = options.suffix || '';
39+
40+
options.cache = false;
41+
42+
var render = compile(source, options);
43+
var requires = parseDependencies(render);
44+
var isLogic = testTemplateSyntax(source, options);
45+
var dir = dirname(uri);
46+
var code = '';
47+
48+
49+
if (!isLogic) {
50+
code = compress ? compressHTML(source) : source;
51+
code = stringify(code);
52+
53+
} else {
54+
55+
code = render.replace(ANONYMOUS_RE, 'function');
56+
}
57+
58+
59+
// 计算主入口相对于当前模板路径
60+
var getRuntime = function () {
61+
if (alias) {
62+
return alias;
63+
}
64+
var prefix = './';
65+
var length = dir.split('/').length - 2;
66+
if (length) {
67+
prefix = (new Array(length + 1)).join('../');
68+
}
69+
70+
return prefix + runtime.replace(/\.js$/, '');
71+
};
72+
73+
74+
// 生成 require 函数依赖声明代码
75+
var getRequireCode = function () {
76+
var requiresCode = [];
77+
78+
requires.forEach(function (uri) {
79+
requiresCode.push("require('" + uri + options.suffix +"');");
80+
});
81+
82+
return requiresCode.join('\n');
83+
};
84+
85+
86+
code
87+
= "var template=require('" + getRuntime() + "');"
88+
+ getRequireCode()
89+
+ "module.exports=template('" + filename + "'," + code + ");";
90+
91+
92+
return {
93+
94+
// 编译结果
95+
code: code,
96+
97+
// 依赖的子模板
98+
requires: requires.map(function (subUir) {
99+
testUri(subUir, uri, source);
100+
return resolve(dir + subUir);
101+
})
102+
103+
};
104+
105+
};
106+
107+
108+
109+
template.config('cache', false);
110+
template.onerror = function (e) {
111+
throw e;
112+
};
113+
114+
115+
var SLASH_RE = /\\\\/g;
116+
var DOT_RE = /\/\.\//g;
117+
var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/;
118+
var DIRNAME_RE = /[^/]+$/;
119+
var ANONYMOUS_RE = /^function\s+anonymous/;
120+
var EXTNAME_RE = /\.(html|htm|tpl)$/i;
121+
var INCLUDE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*include|(?:^|[^$])\binclude\s*\(\s*(["'])(.+?)\1/g; //"
122+
123+
124+
// 编译模板
125+
var compile = function (source, options) {
126+
var render = template.compile(source, options);
127+
128+
return render
129+
.toString()
130+
.replace(ANONYMOUS_RE, 'function');
131+
};
132+
133+
134+
// 检查模板是否有逻辑语法
135+
var testTemplateSyntax = function (source, options) {
136+
return source.indexOf(options.openTag) !== -1;
137+
};
138+
139+
140+
// 模板 filename 规范检查
141+
// 保证 include 语法引用的是相对路径
142+
var testUri = function (uri, fromUri, source) {
143+
if (!/^\./.test(uri) || EXTNAME_RE.test(uri)) {
144+
145+
var line;
146+
147+
// 如果只出现一次这个字符串,很容易确认模板错误行
148+
if (source.split(uri).length === 2) {
149+
source.split(/\n/).forEach(function (code, index) {
150+
if (code.indexOf(uri) !== -1) {
151+
line = index + 1;
152+
source = code.trim();
153+
}
154+
});
155+
}
156+
157+
var error = {
158+
name: 'Syntax Error',
159+
line: line,
160+
source: source,
161+
message: 'Template must be a relative path, and can not have a suffix.'
162+
};
163+
164+
if (fromUri) {
165+
error.filename = fromUri;
166+
}
167+
168+
throw error;
169+
}
170+
};
171+
172+
173+
// 依赖分析
174+
var parseDependencies = function (code) {
175+
var list = [];
176+
var uniq = {};
177+
178+
code
179+
.replace(SLASH_RE, '')
180+
.replace(INCLUDE_RE, function(m, m1, m2) {
181+
if (m2 && !uniq.hasOwnProperty(m2)) {
182+
list.push(m2);
183+
uniq[m2] = true;
184+
}
185+
});
186+
187+
return list;
188+
};
189+
190+
191+
// 获取上一层 uri
192+
var dirname = function (uri) {
193+
return uri.replace(DIRNAME_RE, '');
194+
};
195+
196+
197+
// 分解为标准化 uri
198+
var resolve = function (uri) {
199+
uri = uri.replace(DOT_RE, '/');
200+
while (uri.match(DOUBLE_DOT_RE)) {
201+
uri = uri.replace(DOUBLE_DOT_RE, '/');
202+
}
203+
return uri;
204+
};
205+
206+
207+
// 构造字符串表达式
208+
var stringify = function (code) {
209+
return "'" + code
210+
.replace(/('|\\)/g, '\\$1')
211+
.replace(/\r/g, '\\r')
212+
.replace(/\n/g, '\\n')
213+
+ "'";
214+
};
215+
216+
217+
// 压缩 HTML 字符串
218+
var compressHTML = function (code) {
219+
code = code
220+
.replace(/\s+/g, ' ')
221+
.replace(/<!--[\w\W]*?-->/g, '');
222+
223+
return code;
224+
};
225+
226+
return template;
227+
};
228+
229+
230+
231+
232+
233+

src/defaults.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module.exports = {
2+
3+
// 模板根目录
4+
//base: './',
5+
6+
// 编译输出目录设置
7+
output: './build',
8+
9+
// 模板使用的编码。(只支持 utf-8)
10+
charset: 'utf-8',
11+
12+
// 定义模板采用哪种语法,内置可选:
13+
// simple: 默认语法,易于读写。可参看语法文档
14+
// native: 功能丰富,灵活多变。语法类似微型模板引擎 tmpl
15+
syntax: 'simple',
16+
17+
// 自定义辅助方法路径
18+
helpers: null,
19+
20+
// 是否过滤 XSS
21+
// 如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率
22+
escape: true,
23+
24+
// 是否压缩 HTML 多余空白字符
25+
compress: true,
26+
27+
// 输出的模块类型,可选:
28+
29+
// default: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。
30+
// cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果)
31+
// amd: 支持 RequireJS 等流行加载器
32+
// commonjs: 编译为 NodeJS 模块
33+
type: 'commonjs',
34+
35+
// 设置输出的运行时文件名
36+
runtime: 'template.js',
37+
38+
// 设置模块依赖的运行时路径
39+
// 仅针对于非``type:'default'``的模块配置字段。如果不指定模块内部会自动使用相对``runtime``的路径
40+
alias: null,
41+
42+
// 是否合并模板
43+
// 仅针对于``type:'default'``的模块
44+
combo: false,
45+
46+
// 是否输出为压缩的格式
47+
minify: false,
48+
49+
// 是否开启编译缓存
50+
cache: true,
51+
52+
// 模板文件后缀
53+
suffix: '.tpl'
54+
};

src/path.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*!
2+
* NodeJS path 跨平台支持(让 windows 路径分隔与 linux 保持一致,统一为:“/”)
3+
* https://github.com/aui/tmodjs
4+
* Released under the MIT, BSD, and GPL Licenses
5+
*/
6+
7+
'use strict';
8+
9+
var path = require('path');
10+
11+
if (!/\\/.test(path.resolve())) {
12+
module.exports = path;
13+
} else {
14+
var oldPath = path;
15+
var newPath = Object.create(oldPath);
16+
var proxy = function (name) {
17+
return function () {
18+
var value = oldPath[name].apply(oldPath, arguments);
19+
if (typeof value === 'string') {
20+
value = value.split(oldPath.sep).join('/');
21+
}
22+
return value;
23+
};
24+
};
25+
26+
for (var name in newPath) {
27+
if (typeof oldPath[name] === 'function') {
28+
newPath[name] = proxy(name);
29+
}
30+
}
31+
32+
module.exports = newPath;
33+
}
34+

0 commit comments

Comments
 (0)