From 373364e193d121684f2afba0aa6589b7008fe423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 6 Dec 2013 01:34:40 +0800 Subject: [PATCH 01/87] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 83ea958..76eac24 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ TmodJS(原名 atc)是一款前端模板编译工具,它可以让前端模 ## 安装 -先安装 [NodeJS](http://nodejs.org) 与 npm (最新版 NodeJS 已经附带 npm),执行: +先安装 [NodeJS](http://nodejs.org) 与 Npm (最新版 NodeJS 已经附带 Npm),执行: ``` $ npm install tmodjs -g @@ -50,7 +50,7 @@ $ npm install tmodjs -g ## 快速入门 -学习 TmodJS 只需要理解这四个关键点就好,7分钟可入门: +学习 TmodJS 只需要理解这四个关键点就好,5分钟可入门: 1. 建立模板目录:TmodJS 是基于目录编译,你至少要给项目建立一个专用的前端模板目录 2. 编写模板:你需要了解 TmodJS 的模板语句,如输出变量、条件判断、循环等 @@ -318,12 +318,14 @@ NodeJS 版本: ### 使用 TmodJS 的项目 -* Spa(迅雷) -* MicroTrend(腾讯) -* Tracker(腾讯) -* Qzone(腾讯) +* [Qzone](http://qzone.qq.com)([腾讯](http://qq.com)) +* Spa([迅雷](http://xunlei.com)) +* [爱拍原创](http://m.aipai.com)([爱拍网络](http://www.aipai.com)) +* MicroTrend([腾讯](http://qq.com)) +* Tracker([腾讯](http://qq.com)) +* …… -[提交项目](https://github.com/aui/tmodjs/issues/1) +[提交项目展示到 TmodJS 主页](https://github.com/aui/tmodjs/issues/1) ### 贡献者 From b054e9bcfeae5b4b29fa0ea44c51e6a1c5eafa73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 6 Dec 2013 01:38:05 +0800 Subject: [PATCH 02/87] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/tmod | 2 +- lib/template.js | 4 ++-- test/test/syntax/.debug.js | 11 +++++++++++ test/test/syntax/build/each.js | 2 ++ test/test/syntax/build/template.js | 3 +++ test/test/syntax/each.html | 8 ++++++++ test/test/syntax/package.json | 20 ++++++++++++++++++++ test/tpl/build/template.js | 2 +- 8 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 test/test/syntax/.debug.js create mode 100644 test/test/syntax/build/each.js create mode 100644 test/test/syntax/build/template.js create mode 100644 test/test/syntax/each.html create mode 100644 test/test/syntax/package.json diff --git a/bin/tmod b/bin/tmod index 4996c37..aad4bbf 100644 --- a/bin/tmod +++ b/bin/tmod @@ -23,7 +23,7 @@ var help = function () { '\x1B[90m debugging Template\x1B[39m', ' --type', - '\x1B[90m optional: templatejs (default) | cmd (RequireJS/SeaJS) | amd (RequireJS) | commonjs (NodeJS)\x1B[39m', + '\x1B[90m optional: templatejs (Default) | cmd (RequireJS/SeaJS) | amd (RequireJS) | commonjs (NodeJS)\x1B[39m', ' --output value', '\x1B[90m defining an output directory\x1B[39m', diff --git a/lib/template.js b/lib/template.js index 3c5ff4a..7117c52 100644 --- a/lib/template.js +++ b/lib/template.js @@ -529,7 +529,7 @@ var _compile = (function () { } variables += name + "=" + value + ","; - }; + } // 字符串转义 @@ -540,7 +540,7 @@ var _compile = (function () { // 换行符转义(windows + linux) .replace(/\r/g, '\\r') .replace(/\n/g, '\\n') + "'"; - }; + } }; diff --git a/test/test/syntax/.debug.js b/test/test/syntax/.debug.js new file mode 100644 index 0000000..a41be45 --- /dev/null +++ b/test/test/syntax/.debug.js @@ -0,0 +1,11 @@ +/*! */ +function anonymous($data,$id) {var $helpers=this,$each=$helpers.$each,normsName=$data.normsName,feature=$data.feature,index=$data.index,$escape=$helpers.$escape,$index=$data.$index,$out='';$each(normsName['user_feature'],function(feature,index){ +$out+=' '; +$out+=$escape(normsName['user_feature_name'][index]); +$out+=' '; +$each([],function(feature],$index){ +$out+=' '; +}); +$out+=' '; +}); +return new String($out);} \ No newline at end of file diff --git a/test/test/syntax/build/each.js b/test/test/syntax/build/each.js new file mode 100644 index 0000000..5eae765 --- /dev/null +++ b/test/test/syntax/build/each.js @@ -0,0 +1,2 @@ +/*! */ +template("./each",function(e){var t=this,n=t.$each,r=e.normsName,a=(e.feature,e.index,t.$escape),u=(e.$value,e.$index,t.$string),s="";return n(r.user_feature,function(e,t){s+=' ',s+=a(r.user_feature_name[t]),s+=" ",n(r["user_norm_"+e],function(){s+=" "}),s+=" ",s+=u(n()),s+=" "}),new String(s)}); \ No newline at end of file diff --git a/test/test/syntax/build/template.js b/test/test/syntax/build/template.js new file mode 100644 index 0000000..6a081b5 --- /dev/null +++ b/test/test/syntax/build/template.js @@ -0,0 +1,3 @@ +/*! */ +!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=r.helpers={$string:function(e){var r=typeof e;return/string|number/.test(r)||(e="function"===r?t.$string(e()):""),e+""},$escape:function(e){var r={"<":"<",">":">",'"':""","'":"'","&":"&"};return t.$string(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return r[e]})},$each:function(e,r){var n=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)};if(n(e))for(var t=0,u=e.length;u>t;t++)r.call(e,e[t],t,e);else for(t in e)r.call(e,e[t],t)},$resolve:function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/[^/]+$/,""),u=t+r;for(u=u.replace(/\/\.\//g,"/");u.match(n);)u=u.replace(n,"/");return u},$include:function(e,n,u){var o=t.$resolve(u,e);return r.render(o,n)}},u=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||u({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var o="function"==typeof r,a=n[e]=function(n){try{return o?new r(n,e)+"":r}catch(t){return u(t)()}};return a.prototype=r.prototype=t,a.toString=function(){return r+""},a},r.get=function(e){return n[e.replace(/^([^.])/,"./$1")]},r.helper=function(e,r){t[e]=r},/**/ +r("./each",function(e){var r=this,n=r.$each,t=e.normsName,u=(e.feature,e.index,r.$escape),o=(e.$value,e.$index,r.$string),a="";return n(t.user_feature,function(e,r){a+=' ',a+=u(t.user_feature_name[r]),a+=" ",n(t["user_norm_"+e],function(){a+=" "}),a+=" ",a+=o(n()),a+=" "}),new String(a)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/test/syntax/each.html b/test/test/syntax/each.html new file mode 100644 index 0000000..5d56052 --- /dev/null +++ b/test/test/syntax/each.html @@ -0,0 +1,8 @@ +{{each normsName['user_feature'] as feature index}} + + {{normsName['user_feature_name'][index]}} + + {{each normsName['user_norm_'+feature]}} + {{/each}} + {{$each}} +{{/each}} \ No newline at end of file diff --git a/test/test/syntax/package.json b/test/test/syntax/package.json new file mode 100644 index 0000000..6bdb78b --- /dev/null +++ b/test/test/syntax/package.json @@ -0,0 +1,20 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.2" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [ + "*" + ], + "syntax": "simple", + "helpers": null, + "minify": true, + "async": false, + "engine": false, + "type": "templatejs" + } +} \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 6da88c5..7eba94b 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ !function(e){var t=function(e,i){return t[/string|function/.test(typeof i)?"compile":"render"].apply(t,arguments)},i=t.cache={},r=t.helpers={$string:function(e){var t=typeof e;return/string|number/.test(t)||(e="function"===t?r.$string(e()):""),e+""},$escape:function(e){var t={"<":"<",">":">",'"':""","'":"'","&":"&"};return r.$string(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return t[e]})},$each:function(e,t){var i=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)};if(i(e))for(var r=0,n=e.length;n>r;r++)t.call(e,e[r],r,e);else for(r in e)t.call(e,e[r],r)},$resolve:function(e,t){var i=/(\/)[^/]+\1\.\.\1/,r=e.replace(/[^/]+$/,""),n=r+t;for(n=n.replace(/\/\.\//g,"/");n.match(i);)n=n.replace(i,"/");return n},$include:function(e,i,n){var o=r.$resolve(n,e);return t.render(o,i)}},n=function(t){var i="";for(var r in t)i+="<"+r+">\n"+t[r]+"\n\n";return i&&e.console&&console.error("Template Error\n\n"+i),function(){return"{Template Error}"}};t.render=function(e,i){var r=t.get(e)||n({id:e,name:"Render Error",message:"No Template"});return i?r(i):r},t.compile=function(e,t){var o="function"==typeof t,a=i[e]=function(i){try{return o?new t(i,e)+"":t}catch(r){return n(r)()}};return a.prototype=t.prototype=r,a.toString=function(){return t+""},a},t.get=function(e){return i[e.replace(/^([^.])/,"./$1")]},t.helper=function(e,t){r[e]=t},/**/ t("./copyright","(c) 2013"),/**/ t("./index",function(e,t){var i=this,include=function(r,n){n=n||e;var o=i.$include(r,n,t);return void 0!==o?(l+=o,o):void 0},r=i.$escape,n=e.title,o=i.$each,a=e.list,l=(e.$value,e.$index,"");return include("./public/header"),l+='

',l+=r(n),l+="

",include("./public/footer"),new String(l)}),/**/ From f1f71be2e10234ad49ba180978da918dafa5db05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 21 Dec 2013 15:39:05 +0800 Subject: [PATCH 03/87] v0.0.3 --- README.md | 19 +- lib/{template-runtime.js => runtime/basic.js} | 112 +++--- lib/{template-full.js => runtime/full.js} | 13 +- lib/template-AOTcompile.js | 9 +- lib/template.js | 66 ++-- lib/uglify.js | 4 +- package.json | 4 +- test/test-all/amd.html | 51 +++ test/test-all/amd/build/copyright.js | 4 + test/test-all/amd/build/index.js | 27 ++ test/test-all/amd/build/public/footer.js | 23 ++ test/test-all/amd/build/public/header.js | 17 + test/test-all/amd/build/public/logo.js | 4 + test/test-all/amd/build/template.js | 107 ++++++ test/test-all/amd/copyright.html | 1 + test/test-all/amd/index.html | 12 + test/test-all/amd/package.json | 20 + test/test-all/amd/public/footer.html | 6 + test/test-all/amd/public/header.html | 11 + test/test-all/amd/public/logo.html | 7 + test/test-all/cmd.html | 51 +++ test/test-all/cmd/build/copyright.js | 4 + test/test-all/cmd/build/index.js | 29 ++ test/test-all/cmd/build/public/footer.js | 24 ++ test/test-all/cmd/build/public/header.js | 18 + test/test-all/cmd/build/public/logo.js | 4 + test/test-all/cmd/build/template.js | 107 ++++++ test/test-all/cmd/copyright.html | 1 + test/test-all/cmd/index.html | 12 + test/test-all/cmd/package.json | 20 + test/test-all/cmd/public/footer.html | 6 + test/test-all/cmd/public/header.html | 11 + test/test-all/cmd/public/logo.html | 7 + .../include/build/include.js | 0 .../include/build/template.js | 0 test/{test => test-all}/include/include.html | 0 test/{test => test-all}/include/package.json | 0 test/test-all/mix-cmd.html | 78 ++++ test/test-all/mix-cmd/build/copyright.js | 4 + test/test-all/mix-cmd/build/index.js | 6 + test/test-all/mix-cmd/build/public/footer.js | 5 + test/test-all/mix-cmd/build/public/header.js | 5 + test/test-all/mix-cmd/build/public/logo.js | 4 + test/test-all/mix-cmd/build/template.js | 353 ++++++++++++++++++ test/test-all/mix-cmd/copyright.html | 1 + test/test-all/mix-cmd/index.html | 12 + test/test-all/mix-cmd/package.json | 18 + test/test-all/mix-cmd/public/footer.html | 6 + test/test-all/mix-cmd/public/header.html | 11 + test/test-all/mix-cmd/public/logo.html | 7 + test/test-all/mix.html | 83 ++++ test/test-all/mix/build/copyright.js | 2 + test/test-all/mix/build/index.js | 2 + test/test-all/mix/build/public/footer.js | 2 + test/test-all/mix/build/public/header.js | 2 + test/test-all/mix/build/public/logo.js | 2 + test/test-all/mix/build/template.js | 353 ++++++++++++++++++ test/test-all/mix/copyright.html | 1 + test/test-all/mix/index.html | 12 + test/test-all/mix/package.json | 18 + test/test-all/mix/public/footer.html | 6 + test/test-all/mix/public/header.html | 11 + test/test-all/mix/public/logo.html | 7 + test/{test => test-all}/syntax/.debug.js | 0 test/{test => test-all}/syntax/build/each.js | 0 .../syntax/build/template.js | 0 test/{test => test-all}/syntax/each.html | 0 test/{test => test-all}/syntax/package.json | 0 test/tpl/build/copyright.js | 4 +- test/tpl/build/index.js | 4 +- test/tpl/build/public/footer.js | 4 +- test/tpl/build/public/header.js | 4 +- test/tpl/build/public/logo.js | 4 +- test/tpl/build/template.js | 14 +- test/tpl/package.json | 2 +- tmod.js | 15 +- 76 files changed, 1757 insertions(+), 116 deletions(-) rename lib/{template-runtime.js => runtime/basic.js} (56%) rename lib/{template-full.js => runtime/full.js} (66%) create mode 100644 test/test-all/amd.html create mode 100644 test/test-all/amd/build/copyright.js create mode 100644 test/test-all/amd/build/index.js create mode 100644 test/test-all/amd/build/public/footer.js create mode 100644 test/test-all/amd/build/public/header.js create mode 100644 test/test-all/amd/build/public/logo.js create mode 100644 test/test-all/amd/build/template.js create mode 100644 test/test-all/amd/copyright.html create mode 100644 test/test-all/amd/index.html create mode 100644 test/test-all/amd/package.json create mode 100644 test/test-all/amd/public/footer.html create mode 100644 test/test-all/amd/public/header.html create mode 100644 test/test-all/amd/public/logo.html create mode 100644 test/test-all/cmd.html create mode 100644 test/test-all/cmd/build/copyright.js create mode 100644 test/test-all/cmd/build/index.js create mode 100644 test/test-all/cmd/build/public/footer.js create mode 100644 test/test-all/cmd/build/public/header.js create mode 100644 test/test-all/cmd/build/public/logo.js create mode 100644 test/test-all/cmd/build/template.js create mode 100644 test/test-all/cmd/copyright.html create mode 100644 test/test-all/cmd/index.html create mode 100644 test/test-all/cmd/package.json create mode 100644 test/test-all/cmd/public/footer.html create mode 100644 test/test-all/cmd/public/header.html create mode 100644 test/test-all/cmd/public/logo.html rename test/{test => test-all}/include/build/include.js (100%) rename test/{test => test-all}/include/build/template.js (100%) rename test/{test => test-all}/include/include.html (100%) rename test/{test => test-all}/include/package.json (100%) create mode 100644 test/test-all/mix-cmd.html create mode 100644 test/test-all/mix-cmd/build/copyright.js create mode 100644 test/test-all/mix-cmd/build/index.js create mode 100644 test/test-all/mix-cmd/build/public/footer.js create mode 100644 test/test-all/mix-cmd/build/public/header.js create mode 100644 test/test-all/mix-cmd/build/public/logo.js create mode 100644 test/test-all/mix-cmd/build/template.js create mode 100644 test/test-all/mix-cmd/copyright.html create mode 100644 test/test-all/mix-cmd/index.html create mode 100644 test/test-all/mix-cmd/package.json create mode 100644 test/test-all/mix-cmd/public/footer.html create mode 100644 test/test-all/mix-cmd/public/header.html create mode 100644 test/test-all/mix-cmd/public/logo.html create mode 100644 test/test-all/mix.html create mode 100644 test/test-all/mix/build/copyright.js create mode 100644 test/test-all/mix/build/index.js create mode 100644 test/test-all/mix/build/public/footer.js create mode 100644 test/test-all/mix/build/public/header.js create mode 100644 test/test-all/mix/build/public/logo.js create mode 100644 test/test-all/mix/build/template.js create mode 100644 test/test-all/mix/copyright.html create mode 100644 test/test-all/mix/index.html create mode 100644 test/test-all/mix/package.json create mode 100644 test/test-all/mix/public/footer.html create mode 100644 test/test-all/mix/public/header.html create mode 100644 test/test-all/mix/public/logo.html rename test/{test => test-all}/syntax/.debug.js (100%) rename test/{test => test-all}/syntax/build/each.js (100%) rename test/{test => test-all}/syntax/build/template.js (100%) rename test/{test => test-all}/syntax/each.html (100%) rename test/{test => test-all}/syntax/package.json (100%) diff --git a/README.md b/README.md index 76eac24..e0bb418 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,9 @@ $ tmod [path] [options] ### 四、调用模板 -模板编译后,模板目录会生成 build 子目录,里面包含了所有的模板编译版本。其中 build/template.js 是压缩后的模板包,通常情况下你只需要在页面中引入它就好。例如: +模板编译后,模板目录会生成 build 子目录,里面包含了所有的模板编译版本。其中 build/template.js 是压缩后的模板包,通常情况下你只需要在页面中引入它就好(其余的文件可以暂时忽略)。例如: -``` - -``` + 这是默认的加载方式,除此之外还支持 RequireJS、SeaJS、NodeJS 加载。[示例](http://aui.github.io/tmodjs/test/index.html) @@ -115,7 +113,7 @@ document.getElementById('list').innerHTML = html; ## 编译演示项目 -源码包中 ./test 是一个演示项目,./test/tpl 是项目的模板目录,包含了若干模板。你可以通过这个演示项目快速了解 TmodJS 用法以及模板语法、模板加载方式。 +源码包中``./test``是一个演示项目,``./test/tpl``是项目的模板目录,包含了若干模板。你可以通过这个演示项目快速了解 TmodJS 用法以及模板语法、模板加载方式。 首先,使用 cd 命令切换到 TmodJS 目录后,你可以编译这个目录模板: @@ -254,6 +252,12 @@ TmodJS.on('compile', function (data) {}); ## 更新日志 +### TmodJS v0.0.3 + +* 修复``combo``配置不能为空数组的 BUG +* 进一步精简模块 ID +* 运行时性能优化 + ### TmodJS v0.0.2 修复极其特殊情况下 TmodJS 无法为 AMD/CMD 模块正确声明依赖的问题[#14](https://github.com/aui/tmodjs/issues/14) @@ -329,5 +333,10 @@ NodeJS 版本: ### 贡献者 +* [@aui](https://github.com/aui) +* [@Jsonzhang](https://github.com/Jsonzhang) * [@TooBug](https://github.com/TooBug) + +**特别感谢** + * [@warmhug](https://github.com/warmhug)(在工具雏形阶段的热心的测试与反馈) diff --git a/lib/template-runtime.js b/lib/runtime/basic.js similarity index 56% rename from lib/template-runtime.js rename to lib/runtime/basic.js index 7edd584..7cdee18 100644 --- a/lib/template-runtime.js +++ b/lib/runtime/basic.js @@ -10,70 +10,86 @@ var cache = template.cache = {}; - var helpers = template.helpers = { - $string: function (value) { - var type = typeof value; + var toString = function (value, type) { + + if (typeof value !== 'string') { - if (!/string|number/.test(type)) { - value = type === 'function' - ? helpers.$string(value()) : ''; + type = typeof value; + if (type === 'number') { + value += ''; + } else if (type === 'function') { + value = toString(value.call(value)); + } else { + value = ''; } + } - return value + ''; - }, + return value; + }; - $escape: function (content) { - var m = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - return helpers.$string(content) - .replace(/&(?![\w#]+;)|[<>"']/g, function (s) { - return m[s]; - }); - }, + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + + + var escapeHTML = function (content) { + return toString(content) + .replace(/&(?![\w#]+;)|[<>"']/g, function (s) { + return escapeMap[s]; + }); + }; - $each: function (data, callback) { - var isArray = Array.isArray || function (obj) { - return ({}).toString.call(obj) === '[object Array]'; - }; - - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } - } else { - for (i in data) { - callback.call(data, data[i], i); - } - } - }, + var isArray = Array.isArray || function (obj) { + return ({}).toString.call(obj) === '[object Array]'; + }; - $resolve: function (from, to) { - var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/[^/]+$/, ''); - var id = dirname + to; - id = id.replace(/\/\.\//g, '/'); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, '/'); + var each = function (data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); } - return id; - }, + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, './$1').replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + + + var helpers = template.helpers = { $include: function (path, data, from) { - var id = helpers.$resolve(from, path); + var id = resolve(from, path); return template.render(id, data); - } + }, + + $string: toString, + + $escape: escapeHTML, + + $each: each }; @@ -126,7 +142,7 @@ template.get = function (id) { - return cache[id.replace(/^([^.])/, './$1')]; + return cache[id.replace(/^\.\//, '')]; }; diff --git a/lib/template-full.js b/lib/runtime/full.js similarity index 66% rename from lib/template-full.js rename to lib/runtime/full.js index dc0fdca..8aa5d21 100644 --- a/lib/template-full.js +++ b/lib/runtime/full.js @@ -6,25 +6,24 @@ var get = template.get; var helpers = template.helpers; - helpers.$resolve = function (from, to) { + var resolve = function(from, to) { var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/[^/]+$/, ''); + var dirname = from.replace(/^([^.])/, './$1').replace(/[^/]+$/, ""); var id = dirname + to; - - id = id.replace(/\/\.\//g, '/'); + id = id.replace(/\/\.\//g, "/"); while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, '/'); + id = id.replace(DOUBLE_DOT_RE, "/"); } return id; }; helpers.$include = function (path, data, from) { - var id = helpers.$resolve(from, path); + var id = resolve(from, path); return template.render(id, data); }; template.get = function (id) { - return get(id.replace(/^([^.])/, './$1')); + return get(id.replace(/^\.\//, '')); }; '<:syntax:>' diff --git a/lib/template-AOTcompile.js b/lib/template-AOTcompile.js index 0c1998c..859b3bf 100644 --- a/lib/template-AOTcompile.js +++ b/lib/template-AOTcompile.js @@ -142,6 +142,7 @@ template.AOTcompile = function (id, source, options) { var requires = parseDependencies(render); var isLogic = testTemplateSyntax(source); var dir = dirname(id); + var tid = id.replace(/^\.\//, ''); var isHTML = isEngine || !isLogic; @@ -186,7 +187,7 @@ template.AOTcompile = function (id, source, options) { case '': case 'templatejs': - code = "template('" + id + "'," + code + ");"; + code = "template('" + tid + "'," + code + ");"; break; @@ -197,7 +198,7 @@ template.AOTcompile = function (id, source, options) { = "define(function(require){" + getRequireCode() + "return require('" + getRuntime() + "')" - + "('" + id + "', " + code + ");" + + "('" + tid + "', " + code + ");" + "});"; break; @@ -209,7 +210,7 @@ template.AOTcompile = function (id, source, options) { = "define(" + "['" + getRuntime() + "','" + requires.join("','") + "']," + "function(template){" - + "return template('" + id + "', " + code + ");" + + "return template('" + tid + "', " + code + ");" + "});"; break; @@ -220,7 +221,7 @@ template.AOTcompile = function (id, source, options) { code = "var template=require('" + getRuntime() + "');" + getRequireCode() - + "module.exports=template('" + id + "'," + code + ");"; + + "module.exports=template('" + tid + "'," + code + ");"; break; diff --git a/lib/template.js b/lib/template.js index 7117c52..6254f8b 100644 --- a/lib/template.js +++ b/lib/template.js @@ -129,11 +129,9 @@ var _cache = template.cache = {}; // 辅助方法集合 -var _helpers = template.helpers = { +var _helpers = template.helpers = (function () { - $include: template.render, - - $string: function (value, type) { + var toString = function (value, type) { if (typeof value !== 'string') { @@ -141,7 +139,7 @@ var _helpers = template.helpers = { if (type === 'number') { value += ''; } else if (type === 'function') { - value = _helpers.$string(value()); + value = toString(value.call(value)); } else { value = ''; } @@ -149,27 +147,32 @@ var _helpers = template.helpers = { return value; - }, - - $escape: function (content) { - var m = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - return _helpers.$string(content) + }; + + + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + + + var escapeHTML = function (content) { + return toString(content) .replace(/&(?![\w#]+;)|[<>"']/g, function (s) { - return m[s]; + return escapeMap[s]; }); - }, + }; + + + var isArray = Array.isArray || function (obj) { + return ({}).toString.call(obj) === '[object Array]'; + }; + - $each: function (data, callback) { - var isArray = Array.isArray || function (obj) { - return ({}).toString.call(obj) === '[object Array]'; - }; - + var each = function (data, callback) { if (isArray(data)) { for (var i = 0, len = data.length; i < len; i++) { callback.call(data, data[i], i, data); @@ -179,8 +182,21 @@ var _helpers = template.helpers = { callback.call(data, data[i], i); } } - } -}; + }; + + + return { + + $include: template.render, + + $string: toString, + + $escape: escapeHTML, + + $each: each + + }; +})(); diff --git a/lib/uglify.js b/lib/uglify.js index 9f81947..f87dd6f 100644 --- a/lib/uglify.js +++ b/lib/uglify.js @@ -170,7 +170,7 @@ var P_RELATIVE = ARGS.p && ARGS.p == "relative"; var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({ file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE, root: ARGS.source_map_root, - orig: ORIG_MAP, + orig: ORIG_MAP }) : null; OUTPUT_OPTIONS.source_map = SOURCE_MAP; @@ -221,7 +221,7 @@ async.eachLimit(files, 1, function (file, cb) { TOPLEVEL = UglifyJS.parse(code, { filename : file, toplevel : TOPLEVEL, - expression : ARGS.expr, + expression : ARGS.expr }); }; }); diff --git a/package.json b/package.json index e35d84c..02038d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "0.0.2", + "version": "0.0.3", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", @@ -25,7 +25,7 @@ }, "scripts": { "test": "tmod ./test/tpl", - "test2": "tmod ./test/test/include" + "test2": "tmod ./test/test-all/*" }, "dependencies": { "uglify-js": "~2.4.0", diff --git a/test/test-all/amd.html b/test/test-all/amd.html new file mode 100644 index 0000000..fa763bb --- /dev/null +++ b/test/test-all/amd.html @@ -0,0 +1,51 @@ + + + + +RequireJS - 调用模板演示 + + + + + +
loading..
+ + + + + + + diff --git a/test/test-all/amd/build/copyright.js b/test/test-all/amd/build/copyright.js new file mode 100644 index 0000000..f3618c2 --- /dev/null +++ b/test/test-all/amd/build/copyright.js @@ -0,0 +1,4 @@ +/*! */ +define([ "./template", "" ], function(template) { + return template("copyright", "(c) 2013"); +}); \ No newline at end of file diff --git a/test/test-all/amd/build/index.js b/test/test-all/amd/build/index.js new file mode 100644 index 0000000..6541e24 --- /dev/null +++ b/test/test-all/amd/build/index.js @@ -0,0 +1,27 @@ +/*! */ +define([ "./template", "./public/header", "./public/footer" ], function(template) { + return template("index", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + include("./public/header"); + $out += '

'; + $out += $escape(title); + $out += "

"; + include("./public/footer"); + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/amd/build/public/footer.js b/test/test-all/amd/build/public/footer.js new file mode 100644 index 0000000..78db41b --- /dev/null +++ b/test/test-all/amd/build/public/footer.js @@ -0,0 +1,23 @@ +/*! */ +define([ "../template", "../copyright" ], function(template) { + return template("public/footer", function($data, $id) { + var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += '"; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/amd/build/public/header.js b/test/test-all/amd/build/public/header.js new file mode 100644 index 0000000..56d132b --- /dev/null +++ b/test/test-all/amd/build/public/header.js @@ -0,0 +1,17 @@ +/*! */ +define([ "../template", "./logo" ], function(template) { + return template("public/header", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += ' '; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/amd/build/public/logo.js b/test/test-all/amd/build/public/logo.js new file mode 100644 index 0000000..d0e2abf --- /dev/null +++ b/test/test-all/amd/build/public/logo.js @@ -0,0 +1,4 @@ +/*! */ +define([ "../template", "" ], function(template) { + return template("public/logo", '

腾讯网

'); +}); \ No newline at end of file diff --git a/test/test-all/amd/build/template.js b/test/test-all/amd/build/template.js new file mode 100644 index 0000000..98be325 --- /dev/null +++ b/test/test-all/amd/build/template.js @@ -0,0 +1,107 @@ +/*! */ +!function(global) { + var template = function(path, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var helpers = template.helpers = { + $string: function(value) { + var type = typeof value; + if (!/string|number/.test(type)) { + value = type === "function" ? helpers.$string(value()) : ""; + } + return value + ""; + }, + $escape: function(content) { + var m = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + return helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return m[s]; + }); + }, + $each: function(data, callback) { + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }, + $resolve: function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }, + $include: function(path, data, from) { + var id = helpers.$resolve(from, path); + return template.render(id, data); + } + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(path, data) { + var fn = template.get(path) || debug({ + id: path, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(path, fn) { + var isFunction = typeof fn === "function"; + var render = cache[path] = function(data) { + try { + return isFunction ? new fn(data, path) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = fn.prototype = helpers; + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + return cache[id]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/test-all/amd/copyright.html b/test/test-all/amd/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/amd/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/amd/index.html b/test/test-all/amd/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/amd/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
+

{{title}}

+ +
+ +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/amd/package.json b/test/test-all/amd/package.json new file mode 100644 index 0000000..64d7b99 --- /dev/null +++ b/test/test-all/amd/package.json @@ -0,0 +1,20 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.2" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [ + "*" + ], + "syntax": "simple", + "helpers": null, + "minify": false, + "async": false, + "engine": false, + "type": "amd" + } +} \ No newline at end of file diff --git a/test/test-all/amd/public/footer.html b/test/test-all/amd/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/amd/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/amd/public/header.html b/test/test-all/amd/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/amd/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/amd/public/logo.html b/test/test-all/amd/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/amd/public/logo.html @@ -0,0 +1,7 @@ + +

+ + 腾讯网 + +

+ \ No newline at end of file diff --git a/test/test-all/cmd.html b/test/test-all/cmd.html new file mode 100644 index 0000000..61e2bfe --- /dev/null +++ b/test/test-all/cmd.html @@ -0,0 +1,51 @@ + + + + +SeaJS - 调用模板演示 + + + + + +
loading..
+ + + + + + + diff --git a/test/test-all/cmd/build/copyright.js b/test/test-all/cmd/build/copyright.js new file mode 100644 index 0000000..ee0bbeb --- /dev/null +++ b/test/test-all/cmd/build/copyright.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("./template")("copyright", "(c) 2013"); +}); \ No newline at end of file diff --git a/test/test-all/cmd/build/index.js b/test/test-all/cmd/build/index.js new file mode 100644 index 0000000..7065c9e --- /dev/null +++ b/test/test-all/cmd/build/index.js @@ -0,0 +1,29 @@ +/*! */ +define(function(require) { + require("./public/header"); + require("./public/footer"); + return require("./template")("index", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + include("./public/header"); + $out += '

'; + $out += $escape(title); + $out += "

"; + include("./public/footer"); + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd/build/public/footer.js b/test/test-all/cmd/build/public/footer.js new file mode 100644 index 0000000..869b7f4 --- /dev/null +++ b/test/test-all/cmd/build/public/footer.js @@ -0,0 +1,24 @@ +/*! */ +define(function(require) { + require("../copyright"); + return require("../template")("public/footer", function($data, $id) { + var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += '"; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd/build/public/header.js b/test/test-all/cmd/build/public/header.js new file mode 100644 index 0000000..3e0d80d --- /dev/null +++ b/test/test-all/cmd/build/public/header.js @@ -0,0 +1,18 @@ +/*! */ +define(function(require) { + require("./logo"); + return require("../template")("public/header", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += ' '; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd/build/public/logo.js b/test/test-all/cmd/build/public/logo.js new file mode 100644 index 0000000..df41b0a --- /dev/null +++ b/test/test-all/cmd/build/public/logo.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("../template")("public/logo", '

腾讯网

'); +}); \ No newline at end of file diff --git a/test/test-all/cmd/build/template.js b/test/test-all/cmd/build/template.js new file mode 100644 index 0000000..9db0be9 --- /dev/null +++ b/test/test-all/cmd/build/template.js @@ -0,0 +1,107 @@ +/*! */ +!function(global) { + var template = function(path, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var helpers = template.helpers = { + $string: function(value) { + var type = typeof value; + if (!/string|number/.test(type)) { + value = type === "function" ? helpers.$string(value()) : ""; + } + return value + ""; + }, + $escape: function(content) { + var m = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + return helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return m[s]; + }); + }, + $each: function(data, callback) { + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }, + $resolve: function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }, + $include: function(path, data, from) { + var id = helpers.$resolve(from, path); + return template.render(id, data); + } + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(path, data) { + var fn = template.get(path) || debug({ + id: path, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(path, fn) { + var isFunction = typeof fn === "function"; + var render = cache[path] = function(data) { + try { + return isFunction ? new fn(data, path) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = fn.prototype = helpers; + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + return cache[id]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/test-all/cmd/copyright.html b/test/test-all/cmd/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/cmd/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/cmd/index.html b/test/test-all/cmd/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/cmd/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
+

{{title}}

+ +
+ +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/cmd/package.json b/test/test-all/cmd/package.json new file mode 100644 index 0000000..db04965 --- /dev/null +++ b/test/test-all/cmd/package.json @@ -0,0 +1,20 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.2" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [ + "*" + ], + "syntax": "simple", + "helpers": null, + "minify": false, + "async": false, + "engine": false, + "type": "cmd" + } +} \ No newline at end of file diff --git a/test/test-all/cmd/public/footer.html b/test/test-all/cmd/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/cmd/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/cmd/public/header.html b/test/test-all/cmd/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/cmd/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/cmd/public/logo.html b/test/test-all/cmd/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/cmd/public/logo.html @@ -0,0 +1,7 @@ + +

+ + 腾讯网 + +

+ \ No newline at end of file diff --git a/test/test/include/build/include.js b/test/test-all/include/build/include.js similarity index 100% rename from test/test/include/build/include.js rename to test/test-all/include/build/include.js diff --git a/test/test/include/build/template.js b/test/test-all/include/build/template.js similarity index 100% rename from test/test/include/build/template.js rename to test/test-all/include/build/template.js diff --git a/test/test/include/include.html b/test/test-all/include/include.html similarity index 100% rename from test/test/include/include.html rename to test/test-all/include/include.html diff --git a/test/test/include/package.json b/test/test-all/include/package.json similarity index 100% rename from test/test/include/package.json rename to test/test-all/include/package.json diff --git a/test/test-all/mix-cmd.html b/test/test-all/mix-cmd.html new file mode 100644 index 0000000..8919127 --- /dev/null +++ b/test/test-all/mix-cmd.html @@ -0,0 +1,78 @@ + + + + +SeaJS - 调用模板演示 + + + + + + + + +
loading..
+
+ + + + + + + + + + + + diff --git a/test/test-all/mix-cmd/build/copyright.js b/test/test-all/mix-cmd/build/copyright.js new file mode 100644 index 0000000..7653387 --- /dev/null +++ b/test/test-all/mix-cmd/build/copyright.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("./template")("copyright", "(c) 2013"); +}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/index.js b/test/test-all/mix-cmd/build/index.js new file mode 100644 index 0000000..076a22b --- /dev/null +++ b/test/test-all/mix-cmd/build/index.js @@ -0,0 +1,6 @@ +/*! */ +define(function(require) { + require("./public/header"); + require("./public/footer"); + return require("./template")("index", "{{include './public/header'}}

{{title}}

{{include './public/footer'}}"); +}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/footer.js b/test/test-all/mix-cmd/build/public/footer.js new file mode 100644 index 0000000..f5311c6 --- /dev/null +++ b/test/test-all/mix-cmd/build/public/footer.js @@ -0,0 +1,5 @@ +/*! */ +define(function(require) { + require("../copyright"); + return require("../template")("public/footer", "
{{if time}}

{{time}}

{{/if}} {{include '../copyright'}}
"); +}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/header.js b/test/test-all/mix-cmd/build/public/header.js new file mode 100644 index 0000000..da67412 --- /dev/null +++ b/test/test-all/mix-cmd/build/public/header.js @@ -0,0 +1,5 @@ +/*! */ +define(function(require) { + require("./logo"); + return require("../template")("public/header", ' '); +}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/logo.js b/test/test-all/mix-cmd/build/public/logo.js new file mode 100644 index 0000000..4825f42 --- /dev/null +++ b/test/test-all/mix-cmd/build/public/logo.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("../template")("public/logo", '

腾讯网

'); +}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/template.js b/test/test-all/mix-cmd/build/template.js new file mode 100644 index 0000000..17109be --- /dev/null +++ b/test/test-all/mix-cmd/build/template.js @@ -0,0 +1,353 @@ +/*! */ +(function(global) { + "use strict"; + var template = function(id, content) { + return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); + }; + template.version = "2.0.2"; + template.openTag = "<%"; + template.closeTag = "%>"; + template.isEscape = true; + template.isCompress = false; + template.parser = null; + template.render = function(id, data) { + var cache = template.get(id) || _debug({ + id: id, + name: "Render Error", + message: "No Template" + }); + return cache(data); + }; + template.compile = function(id, source) { + var params = arguments; + var isDebug = params[2]; + var anonymous = "anonymous"; + if (typeof source !== "string") { + isDebug = params[1]; + source = params[0]; + id = anonymous; + } + try { + var Render = _compile(id, source, isDebug); + } catch (e) { + e.id = id || source; + e.name = "Syntax Error"; + return _debug(e); + } + function render(data) { + try { + return new Render(data, id) + ""; + } catch (e) { + if (!isDebug) { + return template.compile(id, source, true)(data); + } + return _debug(e)(); + } + } + render.prototype = Render.prototype; + render.toString = function() { + return Render.toString(); + }; + if (id !== anonymous) { + _cache[id] = render; + } + return render; + }; + var _cache = template.cache = {}; + var _helpers = template.helpers = { + $include: template.render, + $string: function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = _helpers.$string(value()); + } else { + value = ""; + } + } + return value; + }, + $escape: function(content) { + var m = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + return _helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return m[s]; + }); + }, + $each: function(data, callback) { + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + } + }; + template.helper = function(name, helper) { + _helpers[name] = helper; + }; + template.onerror = function(e) { + var message = "Template Error\n\n"; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (global.console) { + console.error(message); + } + }; + template.get = function(id) { + var cache; + if (_cache.hasOwnProperty(id)) { + cache = _cache[id]; + } else if ("document" in global) { + var elem = document.getElementById(id); + if (elem) { + var source = elem.value || elem.innerHTML; + cache = template.compile(id, source.replace(/^\s*|\s*$/g, "")); + } + } + return cache; + }; + var _debug = function(e) { + template.onerror(e); + return function() { + return "{Template Error}"; + }; + }; + var _compile = function() { + var forEach = _helpers.$each; + var KEYWORDS = "break,case,catch,continue,debugger,default,delete,do,else,false" + ",finally,for,function,if,in,instanceof,new,null,return,switch,this" + ",throw,true,try,typeof,var,void,while,with" + ",abstract,boolean,byte,char,class,const,double,enum,export,extends" + ",final,float,goto,implements,import,int,interface,long,native" + ",package,private,protected,public,short,static,super,synchronized" + ",throws,transient,volatile" + ",arguments,let,yield" + ",undefined"; + var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; + var SPLIT_RE = /[^\w$]+/g; + var KEYWORDS_RE = new RegExp([ "\\b" + KEYWORDS.replace(/,/g, "\\b|\\b") + "\\b" ].join("|"), "g"); + var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g; + var BOUNDARY_RE = /^,+|,+$/g; + var getVariable = function(code) { + return code.replace(REMOVE_RE, "").replace(SPLIT_RE, ",").replace(KEYWORDS_RE, "").replace(NUMBER_RE, "").replace(BOUNDARY_RE, "").split(/^$|,+/); + }; + return function(id, source, isDebug) { + var openTag = template.openTag; + var closeTag = template.closeTag; + var parser = template.parser; + var code = source; + var tempCode = ""; + var line = 1; + var uniq = { + $data: 1, + $id: 1, + $helpers: 1, + $out: 1, + $line: 1 + }; + var prototype = {}; + var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); + var isNewEngine = "".trim; + var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; + var concat = isNewEngine ? "if(content!==undefined){$out+=content;return content;}" : "$out.push(content);"; + var print = "function(content){" + concat + "}"; + var include = "function(id,data){" + "data=data||$data;" + "var content=$helpers.$include(id,data,$id);" + concat + "}"; + forEach(code.split(openTag), function(code, i) { + code = code.split(closeTag); + var $0 = code[0]; + var $1 = code[1]; + if (code.length === 1) { + tempCode += html($0); + } else { + tempCode += logic($0); + if ($1) { + tempCode += html($1); + } + } + }); + code = tempCode; + if (isDebug) { + code = "try{" + code + "}catch(e){" + "throw {" + "id:$id," + "name:'Render Error'," + "message:e.message," + "line:$line," + "source:" + stringify(source) + ".split(/\\n/)[$line-1].replace(/^[\\s\\t]+/,'')" + "};" + "}"; + } + code = variables + replaces[0] + code + "return new String(" + replaces[3] + ");"; + try { + var Render = new Function("$data", "$id", code); + Render.prototype = prototype; + return Render; + } catch (e) { + e.temp = "function anonymous($data,$id) {" + code + "}"; + throw e; + } + function html(code) { + line += code.split(/\n/).length - 1; + if (template.isCompress) { + code = code.replace(/[\n\r\t\s]+/g, " ").replace(//g, ""); + } + if (code) { + code = replaces[1] + stringify(code) + replaces[2] + "\n"; + } + return code; + } + function logic(code) { + var thisLine = line; + if (parser) { + code = parser(code); + } else if (isDebug) { + code = code.replace(/\n/g, function() { + line++; + return "$line=" + line + ";"; + }); + } + if (code.indexOf("=") === 0) { + var isEscape = code.indexOf("==") !== 0; + code = code.replace(/^=*|[\s;]*$/g, ""); + if (isEscape && template.isEscape) { + var name = code.replace(/\s*\([^\)]+\)/, ""); + if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { + code = "$escape(" + code + ")"; + } + } else { + code = "$string(" + code + ")"; + } + code = replaces[1] + code + replaces[2]; + } + if (isDebug) { + code = "$line=" + thisLine + ";" + code; + } + getKey(code); + return code + "\n"; + } + function getKey(code) { + code = getVariable(code); + forEach(code, function(name) { + if (!uniq.hasOwnProperty(name)) { + setValue(name); + uniq[name] = true; + } + }); + } + function setValue(name) { + var value; + if (name === "print") { + value = print; + } else if (name === "include") { + prototype["$include"] = _helpers["$include"]; + value = include; + } else { + value = "$data." + name; + if (_helpers.hasOwnProperty(name)) { + prototype[name] = _helpers[name]; + if (name.indexOf("$") === 0) { + value = "$helpers." + name; + } else { + value = value + "===undefined?$helpers." + name + ":" + value; + } + } + } + variables += name + "=" + value + ","; + } + function stringify(code) { + return "'" + code.replace(/('|\\)/g, "\\$1").replace(/\r/g, "\\r").replace(/\n/g, "\\n") + "'"; + } + }; + }(); + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } + global.template = template; +})(this); + +!function(global, template) { + var get = template.get; + var helpers = template.helpers; + helpers.$resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + helpers.$include = function(path, data, from) { + var id = helpers.$resolve(from, path); + return template.render(id, data); + }; + template.get = function(id) { + return get(id.replace(/^\.\//, "")); + }; + (function(exports) { + exports.openTag = "{{"; + exports.closeTag = "}}"; + exports.parser = function(code) { + code = code.replace(/^\s/, ""); + var split = code.split(" "); + var key = split.shift(); + var args = split.join(" "); + switch (key) { + case "if": + code = "if(" + args + "){"; + break; + + case "else": + if (split.shift() === "if") { + split = " if(" + split.join(" ") + ")"; + } else { + split = ""; + } + code = "}else" + split + "{"; + break; + + case "/if": + code = "}"; + break; + + case "each": + var object = split[0] || "$data"; + var as = split[1] || "as"; + var value = split[2] || "$value"; + var index = split[3] || "$index"; + var param = value + "," + index; + if (as !== "as") { + object = "[]"; + } + code = "$each(" + object + ",function(" + param + "){"; + break; + + case "/each": + code = "});"; + break; + + case "echo": + code = "print(" + args + ");"; + break; + + case "include": + code = "include(" + split.join(",") + ");"; + break; + + default: + if (exports.helpers.hasOwnProperty(key)) { + code = "==" + key + "(" + split.join(",") + ");"; + } else { + code = code.replace(/[\s;]*$/, ""); + code = "=" + code; + } + break; + } + return code; + }; + })(template); +}(this, this.template); \ No newline at end of file diff --git a/test/test-all/mix-cmd/copyright.html b/test/test-all/mix-cmd/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/mix-cmd/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/mix-cmd/index.html b/test/test-all/mix-cmd/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/mix-cmd/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
+

{{title}}

+ +
+ +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/mix-cmd/package.json b/test/test-all/mix-cmd/package.json new file mode 100644 index 0000000..21fa32f --- /dev/null +++ b/test/test-all/mix-cmd/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.3" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [], + "syntax": "simple", + "helpers": null, + "minify": false, + "async": false, + "engine": true, + "type": "cmd" + } +} \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/footer.html b/test/test-all/mix-cmd/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/mix-cmd/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/header.html b/test/test-all/mix-cmd/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/mix-cmd/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/logo.html b/test/test-all/mix-cmd/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/mix-cmd/public/logo.html @@ -0,0 +1,7 @@ + +

+ + 腾讯网 + +

+ \ No newline at end of file diff --git a/test/test-all/mix.html b/test/test-all/mix.html new file mode 100644 index 0000000..ef884d8 --- /dev/null +++ b/test/test-all/mix.html @@ -0,0 +1,83 @@ + + + + +TemplateJS - 调用模板演示 + + + + + +
loading..
+
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test-all/mix/build/copyright.js b/test/test-all/mix/build/copyright.js new file mode 100644 index 0000000..f38fe34 --- /dev/null +++ b/test/test-all/mix/build/copyright.js @@ -0,0 +1,2 @@ +/*! */ +template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/mix/build/index.js b/test/test-all/mix/build/index.js new file mode 100644 index 0000000..f17d8a4 --- /dev/null +++ b/test/test-all/mix/build/index.js @@ -0,0 +1,2 @@ +/*! */ +template("index", "{{include './public/header'}}

{{title}}

{{include './public/footer'}}"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/footer.js b/test/test-all/mix/build/public/footer.js new file mode 100644 index 0000000..e9e4d65 --- /dev/null +++ b/test/test-all/mix/build/public/footer.js @@ -0,0 +1,2 @@ +/*! */ +template("public/footer", "
{{if time}}

{{time}}

{{/if}} {{include '../copyright'}}
"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/header.js b/test/test-all/mix/build/public/header.js new file mode 100644 index 0000000..08271ef --- /dev/null +++ b/test/test-all/mix/build/public/header.js @@ -0,0 +1,2 @@ +/*! */ +template("public/header", ' '); \ No newline at end of file diff --git a/test/test-all/mix/build/public/logo.js b/test/test-all/mix/build/public/logo.js new file mode 100644 index 0000000..3f2b672 --- /dev/null +++ b/test/test-all/mix/build/public/logo.js @@ -0,0 +1,2 @@ +/*! */ +template("public/logo", '

腾讯网

'); \ No newline at end of file diff --git a/test/test-all/mix/build/template.js b/test/test-all/mix/build/template.js new file mode 100644 index 0000000..fd4e4b8 --- /dev/null +++ b/test/test-all/mix/build/template.js @@ -0,0 +1,353 @@ +/*! */ +(function(global) { + "use strict"; + var template = function(id, content) { + return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); + }; + template.version = "2.0.2"; + template.openTag = "<%"; + template.closeTag = "%>"; + template.isEscape = true; + template.isCompress = false; + template.parser = null; + template.render = function(id, data) { + var cache = template.get(id) || _debug({ + id: id, + name: "Render Error", + message: "No Template" + }); + return cache(data); + }; + template.compile = function(id, source) { + var params = arguments; + var isDebug = params[2]; + var anonymous = "anonymous"; + if (typeof source !== "string") { + isDebug = params[1]; + source = params[0]; + id = anonymous; + } + try { + var Render = _compile(id, source, isDebug); + } catch (e) { + e.id = id || source; + e.name = "Syntax Error"; + return _debug(e); + } + function render(data) { + try { + return new Render(data, id) + ""; + } catch (e) { + if (!isDebug) { + return template.compile(id, source, true)(data); + } + return _debug(e)(); + } + } + render.prototype = Render.prototype; + render.toString = function() { + return Render.toString(); + }; + if (id !== anonymous) { + _cache[id] = render; + } + return render; + }; + var _cache = template.cache = {}; + var _helpers = template.helpers = { + $include: template.render, + $string: function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = _helpers.$string(value()); + } else { + value = ""; + } + } + return value; + }, + $escape: function(content) { + var m = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + return _helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return m[s]; + }); + }, + $each: function(data, callback) { + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + } + }; + template.helper = function(name, helper) { + _helpers[name] = helper; + }; + template.onerror = function(e) { + var message = "Template Error\n\n"; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (global.console) { + console.error(message); + } + }; + template.get = function(id) { + var cache; + if (_cache.hasOwnProperty(id)) { + cache = _cache[id]; + } else if ("document" in global) { + var elem = document.getElementById(id); + if (elem) { + var source = elem.value || elem.innerHTML; + cache = template.compile(id, source.replace(/^\s*|\s*$/g, "")); + } + } + return cache; + }; + var _debug = function(e) { + template.onerror(e); + return function() { + return "{Template Error}"; + }; + }; + var _compile = function() { + var forEach = _helpers.$each; + var KEYWORDS = "break,case,catch,continue,debugger,default,delete,do,else,false" + ",finally,for,function,if,in,instanceof,new,null,return,switch,this" + ",throw,true,try,typeof,var,void,while,with" + ",abstract,boolean,byte,char,class,const,double,enum,export,extends" + ",final,float,goto,implements,import,int,interface,long,native" + ",package,private,protected,public,short,static,super,synchronized" + ",throws,transient,volatile" + ",arguments,let,yield" + ",undefined"; + var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; + var SPLIT_RE = /[^\w$]+/g; + var KEYWORDS_RE = new RegExp([ "\\b" + KEYWORDS.replace(/,/g, "\\b|\\b") + "\\b" ].join("|"), "g"); + var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g; + var BOUNDARY_RE = /^,+|,+$/g; + var getVariable = function(code) { + return code.replace(REMOVE_RE, "").replace(SPLIT_RE, ",").replace(KEYWORDS_RE, "").replace(NUMBER_RE, "").replace(BOUNDARY_RE, "").split(/^$|,+/); + }; + return function(id, source, isDebug) { + var openTag = template.openTag; + var closeTag = template.closeTag; + var parser = template.parser; + var code = source; + var tempCode = ""; + var line = 1; + var uniq = { + $data: 1, + $id: 1, + $helpers: 1, + $out: 1, + $line: 1 + }; + var prototype = {}; + var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); + var isNewEngine = "".trim; + var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; + var concat = isNewEngine ? "if(content!==undefined){$out+=content;return content;}" : "$out.push(content);"; + var print = "function(content){" + concat + "}"; + var include = "function(id,data){" + "data=data||$data;" + "var content=$helpers.$include(id,data,$id);" + concat + "}"; + forEach(code.split(openTag), function(code, i) { + code = code.split(closeTag); + var $0 = code[0]; + var $1 = code[1]; + if (code.length === 1) { + tempCode += html($0); + } else { + tempCode += logic($0); + if ($1) { + tempCode += html($1); + } + } + }); + code = tempCode; + if (isDebug) { + code = "try{" + code + "}catch(e){" + "throw {" + "id:$id," + "name:'Render Error'," + "message:e.message," + "line:$line," + "source:" + stringify(source) + ".split(/\\n/)[$line-1].replace(/^[\\s\\t]+/,'')" + "};" + "}"; + } + code = variables + replaces[0] + code + "return new String(" + replaces[3] + ");"; + try { + var Render = new Function("$data", "$id", code); + Render.prototype = prototype; + return Render; + } catch (e) { + e.temp = "function anonymous($data,$id) {" + code + "}"; + throw e; + } + function html(code) { + line += code.split(/\n/).length - 1; + if (template.isCompress) { + code = code.replace(/[\n\r\t\s]+/g, " ").replace(//g, ""); + } + if (code) { + code = replaces[1] + stringify(code) + replaces[2] + "\n"; + } + return code; + } + function logic(code) { + var thisLine = line; + if (parser) { + code = parser(code); + } else if (isDebug) { + code = code.replace(/\n/g, function() { + line++; + return "$line=" + line + ";"; + }); + } + if (code.indexOf("=") === 0) { + var isEscape = code.indexOf("==") !== 0; + code = code.replace(/^=*|[\s;]*$/g, ""); + if (isEscape && template.isEscape) { + var name = code.replace(/\s*\([^\)]+\)/, ""); + if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { + code = "$escape(" + code + ")"; + } + } else { + code = "$string(" + code + ")"; + } + code = replaces[1] + code + replaces[2]; + } + if (isDebug) { + code = "$line=" + thisLine + ";" + code; + } + getKey(code); + return code + "\n"; + } + function getKey(code) { + code = getVariable(code); + forEach(code, function(name) { + if (!uniq.hasOwnProperty(name)) { + setValue(name); + uniq[name] = true; + } + }); + } + function setValue(name) { + var value; + if (name === "print") { + value = print; + } else if (name === "include") { + prototype["$include"] = _helpers["$include"]; + value = include; + } else { + value = "$data." + name; + if (_helpers.hasOwnProperty(name)) { + prototype[name] = _helpers[name]; + if (name.indexOf("$") === 0) { + value = "$helpers." + name; + } else { + value = value + "===undefined?$helpers." + name + ":" + value; + } + } + } + variables += name + "=" + value + ","; + } + function stringify(code) { + return "'" + code.replace(/('|\\)/g, "\\$1").replace(/\r/g, "\\r").replace(/\n/g, "\\n") + "'"; + } + }; + }(); + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } + global.template = template; +})(this); + +!function(global, template) { + var get = template.get; + var helpers = template.helpers; + helpers.$resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + helpers.$include = function(path, data, from) { + var id = helpers.$resolve(from, path); + return template.render(id, data); + }; + template.get = function(id) { + return get(id.replace(/^\.\//, "")); + }; + (function(exports) { + exports.openTag = "{{"; + exports.closeTag = "}}"; + exports.parser = function(code) { + code = code.replace(/^\s/, ""); + var split = code.split(" "); + var key = split.shift(); + var args = split.join(" "); + switch (key) { + case "if": + code = "if(" + args + "){"; + break; + + case "else": + if (split.shift() === "if") { + split = " if(" + split.join(" ") + ")"; + } else { + split = ""; + } + code = "}else" + split + "{"; + break; + + case "/if": + code = "}"; + break; + + case "each": + var object = split[0] || "$data"; + var as = split[1] || "as"; + var value = split[2] || "$value"; + var index = split[3] || "$index"; + var param = value + "," + index; + if (as !== "as") { + object = "[]"; + } + code = "$each(" + object + ",function(" + param + "){"; + break; + + case "/each": + code = "});"; + break; + + case "echo": + code = "print(" + args + ");"; + break; + + case "include": + code = "include(" + split.join(",") + ");"; + break; + + default: + if (exports.helpers.hasOwnProperty(key)) { + code = "==" + key + "(" + split.join(",") + ");"; + } else { + code = code.replace(/[\s;]*$/, ""); + code = "=" + code; + } + break; + } + return code; + }; + })(template); +}(this, this.template); \ No newline at end of file diff --git a/test/test-all/mix/copyright.html b/test/test-all/mix/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/mix/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/mix/index.html b/test/test-all/mix/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/mix/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
+

{{title}}

+ +
+ +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/mix/package.json b/test/test-all/mix/package.json new file mode 100644 index 0000000..8202b9d --- /dev/null +++ b/test/test-all/mix/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.3" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [], + "syntax": "simple", + "helpers": null, + "minify": false, + "async": false, + "engine": true, + "type": "templatejs" + } +} \ No newline at end of file diff --git a/test/test-all/mix/public/footer.html b/test/test-all/mix/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/mix/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/mix/public/header.html b/test/test-all/mix/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/mix/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/mix/public/logo.html b/test/test-all/mix/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/mix/public/logo.html @@ -0,0 +1,7 @@ + +

+ + 腾讯网 + +

+ \ No newline at end of file diff --git a/test/test/syntax/.debug.js b/test/test-all/syntax/.debug.js similarity index 100% rename from test/test/syntax/.debug.js rename to test/test-all/syntax/.debug.js diff --git a/test/test/syntax/build/each.js b/test/test-all/syntax/build/each.js similarity index 100% rename from test/test/syntax/build/each.js rename to test/test-all/syntax/build/each.js diff --git a/test/test/syntax/build/template.js b/test/test-all/syntax/build/template.js similarity index 100% rename from test/test/syntax/build/template.js rename to test/test-all/syntax/build/template.js diff --git a/test/test/syntax/each.html b/test/test-all/syntax/each.html similarity index 100% rename from test/test/syntax/each.html rename to test/test-all/syntax/each.html diff --git a/test/test/syntax/package.json b/test/test-all/syntax/package.json similarity index 100% rename from test/test/syntax/package.json rename to test/test-all/syntax/package.json diff --git a/test/tpl/build/copyright.js b/test/tpl/build/copyright.js index 17eb557..9b309c3 100644 --- a/test/tpl/build/copyright.js +++ b/test/tpl/build/copyright.js @@ -1,2 +1,2 @@ -/*! */ -template("./copyright","(c) 2013"); \ No newline at end of file +/*! */ +template("copyright","(c) 2013"); \ No newline at end of file diff --git a/test/tpl/build/index.js b/test/tpl/build/index.js index 4036827..595cff1 100644 --- a/test/tpl/build/index.js +++ b/test/tpl/build/index.js @@ -1,2 +1,2 @@ -/*! */ -template("./index",function(i,e){var t=this,include=function(l,n){n=n||i;var r=t.$include(l,n,e);return void 0!==r?(a+=r,r):void 0},l=t.$escape,n=i.title,r=t.$each,u=i.list,a=(i.$value,i.$index,"");return include("./public/header"),a+='

',a+=l(n),a+="

",include("./public/footer"),new String(a)}); \ No newline at end of file +/*! */ +template("index",function(i,e){var t=this,include=function(l,n){n=n||i;var r=t.$include(l,n,e);return void 0!==r?(a+=r,r):void 0},l=t.$escape,n=i.title,r=t.$each,u=i.list,a=(i.$value,i.$index,"");return include("./public/header"),a+='

',a+=l(n),a+="

",include("./public/footer"),new String(a)}); \ No newline at end of file diff --git a/test/tpl/build/public/footer.js b/test/tpl/build/public/footer.js index 18580e2..ca24c66 100644 --- a/test/tpl/build/public/footer.js +++ b/test/tpl/build/public/footer.js @@ -1,2 +1,2 @@ -/*! */ -template("./public/footer",function(i,e){var t=this,n=i.time,r=t.$escape,include=function(n,r){r=r||i;var u=t.$include(n,r,e);return void 0!==u?(l+=u,u):void 0},l="";return l+='",new String(l)}); \ No newline at end of file +/*! */ +template("public/footer",function(i,e){var t=this,n=i.time,r=t.$escape,include=function(n,r){r=r||i;var u=t.$include(n,r,e);return void 0!==u?(l+=u,u):void 0},l="";return l+='",new String(l)}); \ No newline at end of file diff --git a/test/tpl/build/public/header.js b/test/tpl/build/public/header.js index 319f5f8..bef9499 100644 --- a/test/tpl/build/public/header.js +++ b/test/tpl/build/public/header.js @@ -1,2 +1,2 @@ -/*! */ -template("./public/header",function(i,e){var t=this,include=function(n,l){l=l||i;var a=t.$include(n,l,e);return void 0!==a?(r+=a,a):void 0},r="";return r+=' ',new String(r)}); \ No newline at end of file +/*! */ +template("public/header",function(i,e){var t=this,include=function(n,l){l=l||i;var a=t.$include(n,l,e);return void 0!==a?(r+=a,a):void 0},r="";return r+=' ',new String(r)}); \ No newline at end of file diff --git a/test/tpl/build/public/logo.js b/test/tpl/build/public/logo.js index 63da4db..615a87c 100644 --- a/test/tpl/build/public/logo.js +++ b/test/tpl/build/public/logo.js @@ -1,2 +1,2 @@ -/*! */ -template("./public/logo",'

\u817e\u8baf\u7f51

'); \ No newline at end of file +/*! */ +template("public/logo",'

\u817e\u8baf\u7f51

'); \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 7eba94b..6fa0308 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ -/*! */ -!function(e){var t=function(e,i){return t[/string|function/.test(typeof i)?"compile":"render"].apply(t,arguments)},i=t.cache={},r=t.helpers={$string:function(e){var t=typeof e;return/string|number/.test(t)||(e="function"===t?r.$string(e()):""),e+""},$escape:function(e){var t={"<":"<",">":">",'"':""","'":"'","&":"&"};return r.$string(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return t[e]})},$each:function(e,t){var i=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)};if(i(e))for(var r=0,n=e.length;n>r;r++)t.call(e,e[r],r,e);else for(r in e)t.call(e,e[r],r)},$resolve:function(e,t){var i=/(\/)[^/]+\1\.\.\1/,r=e.replace(/[^/]+$/,""),n=r+t;for(n=n.replace(/\/\.\//g,"/");n.match(i);)n=n.replace(i,"/");return n},$include:function(e,i,n){var o=r.$resolve(n,e);return t.render(o,i)}},n=function(t){var i="";for(var r in t)i+="<"+r+">\n"+t[r]+"\n\n";return i&&e.console&&console.error("Template Error\n\n"+i),function(){return"{Template Error}"}};t.render=function(e,i){var r=t.get(e)||n({id:e,name:"Render Error",message:"No Template"});return i?r(i):r},t.compile=function(e,t){var o="function"==typeof t,a=i[e]=function(i){try{return o?new t(i,e)+"":t}catch(r){return n(r)()}};return a.prototype=t.prototype=r,a.toString=function(){return t+""},a},t.get=function(e){return i[e.replace(/^([^.])/,"./$1")]},t.helper=function(e,t){r[e]=t},/**/ -t("./copyright","(c) 2013"),/**/ -t("./index",function(e,t){var i=this,include=function(r,n){n=n||e;var o=i.$include(r,n,t);return void 0!==o?(l+=o,o):void 0},r=i.$escape,n=e.title,o=i.$each,a=e.list,l=(e.$value,e.$index,"");return include("./public/header"),l+='

',l+=r(n),l+="

",include("./public/footer"),new String(l)}),/**/ -t("./public/footer",function(e,t){var i=this,r=e.time,n=i.$escape,include=function(r,n){n=n||e;var a=i.$include(r,n,t);return void 0!==a?(o+=a,a):void 0},o="";return o+='",new String(o)}),/**/ -t("./public/header",function(e,t){var i=this,include=function(n,o){o=o||e;var a=i.$include(n,o,t);return void 0!==a?(r+=a,a):void 0},r="";return r+=' ',new String(r)}),/**/ -t("./public/logo",'

\u817e\u8baf\u7f51

'),"function"==typeof define?define(function(){return t}):"undefined"!=typeof exports?module.exports=t:e.template=t}(this); \ No newline at end of file +/*! */ +!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ +r("copyright","(c) 2013"),/**/ +r("index",function(e,r){var n=this,include=function(t,i){i=i||e;var o=n.$include(t,i,r);return void 0!==o?(u+=o,o):void 0},t=n.$escape,i=e.title,o=n.$each,c=e.list,u=(e.$value,e.$index,"");return include("./public/header"),u+='

',u+=t(i),u+="

",include("./public/footer"),new String(u)}),/**/ +r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,include=function(t,i){i=i||e;var c=n.$include(t,i,r);return void 0!==c?(o+=c,c):void 0},o="";return o+='",new String(o)}),/**/ +r("public/header",function(e,r){var n=this,include=function(i,o){o=o||e;var c=n.$include(i,o,r);return void 0!==c?(t+=c,c):void 0},t="";return t+=' ',new String(t)}),/**/ +r("public/logo",'

\u817e\u8baf\u7f51

'),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index 6bdb78b..8c5da6c 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.2" + "tmodjs": "~0.0.3" }, "tmodjs-config": { "output": "./build", diff --git a/tmod.js b/tmod.js index cfe0c28..870fafa 100644 --- a/tmod.js +++ b/tmod.js @@ -41,7 +41,7 @@ var vm = require("vm"); if (typeof oldPath[name] === 'function') { newPath[name] = proxy(name); } - }; + } path = newPath; })(); @@ -470,7 +470,7 @@ module.exports = { var ignores = []; var isDebug = this.options.debug; var isWrappings = this.options.type !== 'templatejs'; - var runtime = this.options.engine ? '/lib/template-full.js' : '/lib/template-runtime.js'; + var runtime = this.options.engine ? '/lib/runtime/full.js' : '/lib/runtime/basic.js'; var template = fs.readFileSync(__dirname + runtime, 'utf-8'); @@ -492,7 +492,7 @@ module.exports = { templates.push(id); - if (!that.combo.test(id)) { + if (!that.combo || !that.combo.test(id)) { ignores.push(id); return; } @@ -734,7 +734,7 @@ module.exports = { }; for (var name in compileInfo) { e[name] = compileInfo[name]; - }; + } // 模板编译错误事件 this.emit('compileError', e); @@ -807,6 +807,11 @@ module.exports = { var walk = function (dir) { + + if (dir === that.output) { + return; + } + var dirList = fs.readdirSync(dir); dirList.forEach(function (item) { @@ -911,7 +916,7 @@ module.exports = { // 监听模板加载事件 this.on('load', function (data) { - this._log(data.id + '[grey].' + data.extname + '[/grey]'); + this._log(data.id.replace(/^\.\//, '') + '[grey].' + data.extname + '[/grey]'); }); From c3b504dd831abc6823e7cc48689c0290386727b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 21 Dec 2013 21:14:22 +0800 Subject: [PATCH 04/87] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0bb418..66fbb4a 100644 --- a/README.md +++ b/README.md @@ -255,7 +255,7 @@ TmodJS.on('compile', function (data) {}); ### TmodJS v0.0.3 * 修复``combo``配置不能为空数组的 BUG -* 进一步精简模块 ID +* 支持页面内嵌动态编译与预编译两种方案共存(请设置``engine:true``) * 运行时性能优化 ### TmodJS v0.0.2 From f602436046b4d6b349204a18764f7fa84e907f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 21 Dec 2013 21:22:12 +0800 Subject: [PATCH 05/87] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66fbb4a..24e268e 100644 --- a/README.md +++ b/README.md @@ -255,7 +255,7 @@ TmodJS.on('compile', function (data) {}); ### TmodJS v0.0.3 * 修复``combo``配置不能为空数组的 BUG -* 支持页面内嵌动态编译与预编译两种方案共存(请设置``engine:true``) +* 支持页面内嵌动态编译与预编译两种方案共存(请设置``engine:true``,并在页面中中引入 TmodJS 输出的 template.js。如果想让 template.js 不内置合并的模板,可以设置``combo:[]``) * 运行时性能优化 ### TmodJS v0.0.2 From 117734f249c7816d92216b6b8722c59adf2d593b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sun, 22 Dec 2013 14:26:07 +0800 Subject: [PATCH 06/87] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24e268e..3f90002 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TmodJS-前端模板编译工具 -###### 基于文件系统组织维护前端模板 +> 导语:阅读本文档之前,建议先阅读[《进击!前端模板之工程化》](http://aui.github.io/tmodjs/) ## 关于 TmodJS From d5dbe0a5bc9b32a126a4fb930263e085d3d63e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 28 Dec 2013 15:32:59 +0800 Subject: [PATCH 07/87] v0.0.3-rc1 --- README.md | 27 ++-- doc/syntax-simple.md | 7 +- lib/template-AOTcompile.js | 6 +- lib/template-syntax.js | 2 +- lib/template.js | 11 +- package.json | 6 +- test/test-all/cmd-alias.html | 59 +++++++++ test/test-all/cmd-alias/build/copyright.js | 4 + test/test-all/cmd-alias/build/index.js | 29 +++++ .../test-all/cmd-alias/build/public/footer.js | 24 ++++ .../test-all/cmd-alias/build/public/header.js | 18 +++ test/test-all/cmd-alias/build/public/logo.js | 4 + test/test-all/cmd-alias/build/template.js | 115 ++++++++++++++++++ test/test-all/cmd-alias/copyright.html | 1 + test/test-all/cmd-alias/index.html | 12 ++ test/test-all/cmd-alias/package.json | 21 ++++ test/test-all/cmd-alias/public/footer.html | 6 + test/test-all/cmd-alias/public/header.html | 11 ++ test/test-all/cmd-alias/public/logo.html | 7 ++ test/test-all/mix-cmd/build/copyright.js | 2 +- test/test-all/mix-cmd/build/index.js | 2 +- test/test-all/mix-cmd/build/public/footer.js | 2 +- test/test-all/mix-cmd/build/public/header.js | 2 +- test/test-all/mix-cmd/build/public/logo.js | 2 +- test/test-all/mix-cmd/build/template.js | 55 +++++---- test/test-all/mix-cmd/package.json | 3 +- test/tpl/build/copyright.js | 2 +- test/tpl/build/index.js | 2 +- test/tpl/build/public/footer.js | 2 +- test/tpl/build/public/header.js | 2 +- test/tpl/build/public/logo.js | 2 +- test/tpl/build/template.js | 14 +-- test/tpl/package.json | 3 +- tmod.js | 7 +- 34 files changed, 401 insertions(+), 71 deletions(-) create mode 100644 test/test-all/cmd-alias.html create mode 100644 test/test-all/cmd-alias/build/copyright.js create mode 100644 test/test-all/cmd-alias/build/index.js create mode 100644 test/test-all/cmd-alias/build/public/footer.js create mode 100644 test/test-all/cmd-alias/build/public/header.js create mode 100644 test/test-all/cmd-alias/build/public/logo.js create mode 100644 test/test-all/cmd-alias/build/template.js create mode 100644 test/test-all/cmd-alias/copyright.html create mode 100644 test/test-all/cmd-alias/index.html create mode 100644 test/test-all/cmd-alias/package.json create mode 100644 test/test-all/cmd-alias/public/footer.html create mode 100644 test/test-all/cmd-alias/public/header.html create mode 100644 test/test-all/cmd-alias/public/logo.html diff --git a/README.md b/README.md index 3f90002..51c4fe7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # TmodJS-前端模板编译工具 -> 导语:阅读本文档之前,建议先阅读[《进击!前端模板之工程化》](http://aui.github.io/tmodjs/) - ## 关于 TmodJS TmodJS(原名 atc)是一款前端模板编译工具,它可以让前端模板外置、实现类似后端模板一样按文件与目录组织前端模板,并且模板之间可以使用``include``语句相互包含。 @@ -249,6 +247,9 @@ TmodJS.on('compile', function (data) {}); > 请参考:[页面中的模板迁移指南](https://github.com/aui/tmodjs/wiki/页面中的模板迁移指南)。 +**问**:如何不合并输出? + +> 编辑配置文件,设置``combo:[]``。 ## 更新日志 @@ -257,6 +258,7 @@ TmodJS.on('compile', function (data) {}); * 修复``combo``配置不能为空数组的 BUG * 支持页面内嵌动态编译与预编译两种方案共存(请设置``engine:true``,并在页面中中引入 TmodJS 输出的 template.js。如果想让 template.js 不内置合并的模板,可以设置``combo:[]``) * 运行时性能优化 +* 增加``alias``配置字段,在 AMD 与 CMD 模式下可以指定运行时依赖 ID ### TmodJS v0.0.2 @@ -322,21 +324,26 @@ NodeJS 版本: ### 使用 TmodJS 的项目 -* [Qzone](http://qzone.qq.com)([腾讯](http://qq.com)) -* Spa([迅雷](http://xunlei.com)) -* [爱拍原创](http://m.aipai.com)([爱拍网络](http://www.aipai.com)) -* MicroTrend([腾讯](http://qq.com)) -* Tracker([腾讯](http://qq.com)) +* QQ空间 +* 腾讯视频 +* 爱拍原创 +* Spa(迅雷) +* MicroTrend(腾讯) +* Tracker(腾讯) * …… [提交项目展示到 TmodJS 主页](https://github.com/aui/tmodjs/issues/1) -### 贡献者 +### 贡献名单 * [@aui](https://github.com/aui) -* [@Jsonzhang](https://github.com/Jsonzhang) * [@TooBug](https://github.com/TooBug) +* [@Jsonzhang](https://github.com/Jsonzhang)(GruntJS 插件开发者) -**特别感谢** +### 特别感谢 * [@warmhug](https://github.com/warmhug)(在工具雏形阶段的热心的测试与反馈) + +-------------------- + +附:为何要创造 TmodJS?请阅读[《进击!前端模板之工程化》](http://aui.github.io/tmodjs/) \ No newline at end of file diff --git a/doc/syntax-simple.md b/doc/syntax-simple.md index 0fe9724..b46b009 100644 --- a/doc/syntax-simple.md +++ b/doc/syntax-simple.md @@ -16,13 +16,12 @@ TmodJS 默认采用 simple 语法,它非常易于读写。 {{content}} ``` - 默认会对变量中 HTML 字符编码输出,避免 XSS 漏洞。 -输出原始模板变量 - 不编码: - +输出原始模板变量 - 不编码: + ``` -{{echo content}} +{{#content}} ``` ### 条件表达式 diff --git a/lib/template-AOTcompile.js b/lib/template-AOTcompile.js index 859b3bf..eae1566 100644 --- a/lib/template-AOTcompile.js +++ b/lib/template-AOTcompile.js @@ -133,7 +133,7 @@ template.AOTcompile = function (id, source, options) { var type = options.type; // 运行时模块别名 - var runtime = options.runtime; + var alias = options.alias; var RUNTIME = 'template'; @@ -157,8 +157,8 @@ template.AOTcompile = function (id, source, options) { // 计算主入口相对于当前模板路径 var getRuntime = function () { - if (runtime) { - return runtime; + if (alias) { + return alias; } var prefix = './'; var length = dir.split('/').length - 2; diff --git a/lib/template-syntax.js b/lib/template-syntax.js index bb50c1d..d5fa840 100644 --- a/lib/template-syntax.js +++ b/lib/template-syntax.js @@ -75,7 +75,7 @@ if (exports.helpers.hasOwnProperty(key)) { - code = '==' + key + '(' + split.join(',') + ');'; + code = '=#' + key + '(' + split.join(',') + ');'; } else { diff --git a/lib/template.js b/lib/template.js index 6254f8b..3c8f8d0 100644 --- a/lib/template.js +++ b/lib/template.js @@ -23,7 +23,7 @@ var template = function (id, content) { }; -template.version = '2.0.2'; +template.version = '2.0.3'; template.openTag = '<%'; // 设置逻辑语法开始标签 template.closeTag = '%>'; // 设置逻辑语法结束标签 template.isEscape = true; // HTML字符编码输出开关 @@ -348,7 +348,7 @@ var _compile = (function () { // html与逻辑语法分离 - forEach(code.split(openTag), function (code, i) { + forEach(code.split(openTag), function (code) { code = code.split(closeTag); var $0 = code[0]; @@ -453,12 +453,13 @@ var _compile = (function () { } - // 输出语句. 转义: <%=value%> 不转义:<%==value%> + // 输出语句. 转义: <%=value%> 不转义:<%=#value%> + // <%=#value%> 等同 v2.0.3 之前的 <%==value%> if (code.indexOf('=') === 0) { - var isEscape = code.indexOf('==') !== 0; + var isEscape = !/^=[=#]/.test(code); - code = code.replace(/^=*|[\s;]*$/g, ''); + code = code.replace(/^=[=#]?|[\s;]*$/g, ''); if (isEscape && template.isEscape) { diff --git a/package.json b/package.json index 02038d0..6dab32b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "0.0.3", + "version": "0.0.3-rc1", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", @@ -32,4 +32,6 @@ "async": "~0.2.6" }, "license": "BSD" -} \ No newline at end of file +} + + \ No newline at end of file diff --git a/test/test-all/cmd-alias.html b/test/test-all/cmd-alias.html new file mode 100644 index 0000000..f51d8c9 --- /dev/null +++ b/test/test-all/cmd-alias.html @@ -0,0 +1,59 @@ + + + + +SeaJS - 调用模板演示 + + + + + + +
loading..
+ + + + + + + diff --git a/test/test-all/cmd-alias/build/copyright.js b/test/test-all/cmd-alias/build/copyright.js new file mode 100644 index 0000000..9097ee3 --- /dev/null +++ b/test/test-all/cmd-alias/build/copyright.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("template")("copyright", "(c) 2013"); +}); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/index.js b/test/test-all/cmd-alias/build/index.js new file mode 100644 index 0000000..77b9cb5 --- /dev/null +++ b/test/test-all/cmd-alias/build/index.js @@ -0,0 +1,29 @@ +/*! */ +define(function(require) { + require("./public/header"); + require("./public/footer"); + return require("template")("index", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + include("./public/header"); + $out += '

'; + $out += $escape(title); + $out += "

"; + include("./public/footer"); + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/public/footer.js b/test/test-all/cmd-alias/build/public/footer.js new file mode 100644 index 0000000..20a7710 --- /dev/null +++ b/test/test-all/cmd-alias/build/public/footer.js @@ -0,0 +1,24 @@ +/*! */ +define(function(require) { + require("../copyright"); + return require("template")("public/footer", function($data, $id) { + var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += '"; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/public/header.js b/test/test-all/cmd-alias/build/public/header.js new file mode 100644 index 0000000..6ad9788 --- /dev/null +++ b/test/test-all/cmd-alias/build/public/header.js @@ -0,0 +1,18 @@ +/*! */ +define(function(require) { + require("./logo"); + return require("template")("public/header", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var content = $helpers.$include(id, data, $id); + if (content !== undefined) { + $out += content; + return content; + } + }, $out = ""; + $out += ' '; + return new String($out); + }); +}); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/public/logo.js b/test/test-all/cmd-alias/build/public/logo.js new file mode 100644 index 0000000..1f2b04d --- /dev/null +++ b/test/test-all/cmd-alias/build/public/logo.js @@ -0,0 +1,4 @@ +/*! */ +define(function(require) { + return require("template")("public/logo", '

腾讯网

'); +}); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/template.js b/test/test-all/cmd-alias/build/template.js new file mode 100644 index 0000000..8eedcf8 --- /dev/null +++ b/test/test-all/cmd-alias/build/template.js @@ -0,0 +1,115 @@ +/*! */ +!function(global) { + var template = function(path, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var toString = function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = toString(value.call(value)); + } else { + value = ""; + } + } + return value; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; + }); + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + var helpers = template.helpers = { + $include: function(path, data, from) { + var id = resolve(from, path); + return template.render(id, data); + }, + $string: toString, + $escape: escapeHTML, + $each: each + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(path, data) { + var fn = template.get(path) || debug({ + id: path, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(path, fn) { + var isFunction = typeof fn === "function"; + var render = cache[path] = function(data) { + try { + return isFunction ? new fn(data, path) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = fn.prototype = helpers; + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/test-all/cmd-alias/copyright.html b/test/test-all/cmd-alias/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/cmd-alias/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/cmd-alias/index.html b/test/test-all/cmd-alias/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/cmd-alias/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
+

{{title}}

+ +
+ +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/cmd-alias/package.json b/test/test-all/cmd-alias/package.json new file mode 100644 index 0000000..86db517 --- /dev/null +++ b/test/test-all/cmd-alias/package.json @@ -0,0 +1,21 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.3-rc1" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "combo": [ + "*" + ], + "syntax": "simple", + "helpers": null, + "alias": "template", + "minify": false, + "async": false, + "engine": false, + "type": "cmd" + } +} \ No newline at end of file diff --git a/test/test-all/cmd-alias/public/footer.html b/test/test-all/cmd-alias/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/cmd-alias/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/cmd-alias/public/header.html b/test/test-all/cmd-alias/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/cmd-alias/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/cmd-alias/public/logo.html b/test/test-all/cmd-alias/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/cmd-alias/public/logo.html @@ -0,0 +1,7 @@ + +

+ + 腾讯网 + +

+ \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/copyright.js b/test/test-all/mix-cmd/build/copyright.js index 7653387..1cef0d2 100644 --- a/test/test-all/mix-cmd/build/copyright.js +++ b/test/test-all/mix-cmd/build/copyright.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ define(function(require) { return require("./template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/index.js b/test/test-all/mix-cmd/build/index.js index 076a22b..4bf2eda 100644 --- a/test/test-all/mix-cmd/build/index.js +++ b/test/test-all/mix-cmd/build/index.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/mix-cmd/build/public/footer.js b/test/test-all/mix-cmd/build/public/footer.js index f5311c6..5cca7ea 100644 --- a/test/test-all/mix-cmd/build/public/footer.js +++ b/test/test-all/mix-cmd/build/public/footer.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ define(function(require) { require("../copyright"); return require("../template")("public/footer", "
{{if time}}

{{time}}

{{/if}} {{include '../copyright'}}
"); diff --git a/test/test-all/mix-cmd/build/public/header.js b/test/test-all/mix-cmd/build/public/header.js index da67412..d1ec4cb 100644 --- a/test/test-all/mix-cmd/build/public/header.js +++ b/test/test-all/mix-cmd/build/public/header.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ define(function(require) { require("./logo"); return require("../template")("public/header", ' '); diff --git a/test/test-all/mix-cmd/build/public/logo.js b/test/test-all/mix-cmd/build/public/logo.js index 4825f42..a75fc6a 100644 --- a/test/test-all/mix-cmd/build/public/logo.js +++ b/test/test-all/mix-cmd/build/public/logo.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ define(function(require) { return require("../template")("public/logo", '

腾讯网

'); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/template.js b/test/test-all/mix-cmd/build/template.js index 17109be..9fa1ed3 100644 --- a/test/test-all/mix-cmd/build/template.js +++ b/test/test-all/mix-cmd/build/template.js @@ -1,4 +1,4 @@ -/*! */ +/*! */ (function(global) { "use strict"; var template = function(id, content) { @@ -54,37 +54,36 @@ return render; }; var _cache = template.cache = {}; - var _helpers = template.helpers = { - $include: template.render, - $string: function(value, type) { + var _helpers = template.helpers = function() { + var toString = function(value, type) { if (typeof value !== "string") { type = typeof value; if (type === "number") { value += ""; } else if (type === "function") { - value = _helpers.$string(value()); + value = toString(value.call(value)); } else { value = ""; } } return value; - }, - $escape: function(content) { - var m = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - return _helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return m[s]; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; }); - }, - $each: function(data, callback) { - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { if (isArray(data)) { for (var i = 0, len = data.length; i < len; i++) { callback.call(data, data[i], i, data); @@ -94,8 +93,14 @@ callback.call(data, data[i], i); } } - } - }; + }; + return { + $include: template.render, + $string: toString, + $escape: escapeHTML, + $each: each + }; + }(); template.helper = function(name, helper) { _helpers[name] = helper; }; @@ -271,7 +276,7 @@ !function(global, template) { var get = template.get; var helpers = template.helpers; - helpers.$resolve = function(from, to) { + var resolve = function(from, to) { var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); var id = dirname + to; @@ -282,7 +287,7 @@ return id; }; helpers.$include = function(path, data, from) { - var id = helpers.$resolve(from, path); + var id = resolve(from, path); return template.render(id, data); }; template.get = function(id) { diff --git a/test/test-all/mix-cmd/package.json b/test/test-all/mix-cmd/package.json index 21fa32f..2d90e8f 100644 --- a/test/test-all/mix-cmd/package.json +++ b/test/test-all/mix-cmd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.3" + "tmodjs": "~0.0.3-rc1" }, "tmodjs-config": { "output": "./build", @@ -10,6 +10,7 @@ "combo": [], "syntax": "simple", "helpers": null, + "alias": null, "minify": false, "async": false, "engine": true, diff --git a/test/tpl/build/copyright.js b/test/tpl/build/copyright.js index 9b309c3..cd05c29 100644 --- a/test/tpl/build/copyright.js +++ b/test/tpl/build/copyright.js @@ -1,2 +1,2 @@ -/*! */ +/*! */ template("copyright","(c) 2013"); \ No newline at end of file diff --git a/test/tpl/build/index.js b/test/tpl/build/index.js index 595cff1..c72ce33 100644 --- a/test/tpl/build/index.js +++ b/test/tpl/build/index.js @@ -1,2 +1,2 @@ -/*! */ +/*! */ template("index",function(i,e){var t=this,include=function(l,n){n=n||i;var r=t.$include(l,n,e);return void 0!==r?(a+=r,r):void 0},l=t.$escape,n=i.title,r=t.$each,u=i.list,a=(i.$value,i.$index,"");return include("./public/header"),a+='

',a+=l(n),a+="

",include("./public/footer"),new String(a)}); \ No newline at end of file diff --git a/test/tpl/build/public/footer.js b/test/tpl/build/public/footer.js index ca24c66..cf641c4 100644 --- a/test/tpl/build/public/footer.js +++ b/test/tpl/build/public/footer.js @@ -1,2 +1,2 @@ -/*! */ +/*! */ template("public/footer",function(i,e){var t=this,n=i.time,r=t.$escape,include=function(n,r){r=r||i;var u=t.$include(n,r,e);return void 0!==u?(l+=u,u):void 0},l="";return l+='",new String(l)}); \ No newline at end of file diff --git a/test/tpl/build/public/header.js b/test/tpl/build/public/header.js index bef9499..d6177db 100644 --- a/test/tpl/build/public/header.js +++ b/test/tpl/build/public/header.js @@ -1,2 +1,2 @@ -/*! */ +/*! */ template("public/header",function(i,e){var t=this,include=function(n,l){l=l||i;var a=t.$include(n,l,e);return void 0!==a?(r+=a,a):void 0},r="";return r+=' ',new String(r)}); \ No newline at end of file diff --git a/test/tpl/build/public/logo.js b/test/tpl/build/public/logo.js index 615a87c..7e7f637 100644 --- a/test/tpl/build/public/logo.js +++ b/test/tpl/build/public/logo.js @@ -1,2 +1,2 @@ -/*! */ +/*! */ template("public/logo",'

\u817e\u8baf\u7f51

'); \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 6fa0308..a8eb38a 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ -/*! */ -!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ -r("copyright","(c) 2013"),/**/ -r("index",function(e,r){var n=this,include=function(t,i){i=i||e;var o=n.$include(t,i,r);return void 0!==o?(u+=o,o):void 0},t=n.$escape,i=e.title,o=n.$each,c=e.list,u=(e.$value,e.$index,"");return include("./public/header"),u+='

',u+=t(i),u+="

",include("./public/footer"),new String(u)}),/**/ -r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,include=function(t,i){i=i||e;var c=n.$include(t,i,r);return void 0!==c?(o+=c,c):void 0},o="";return o+='",new String(o)}),/**/ -r("public/header",function(e,r){var n=this,include=function(i,o){o=o||e;var c=n.$include(i,o,r);return void 0!==c?(t+=c,c):void 0},t="";return t+=' ',new String(t)}),/**/ -r("public/logo",'

\u817e\u8baf\u7f51

'),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/*! */ +!function(e){var t=function(e,i){return t[/string|function/.test(typeof i)?"compile":"render"].apply(t,arguments)},i=t.cache={},r=function(e,t){return"string"!=typeof e&&(t=typeof e,"number"===t?e+="":e="function"===t?r(e.call(e)):""),e},n={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return r(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return n[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,t){if(c(e))for(var i=0,r=e.length;r>i;i++)t.call(e,e[i],i,e);else for(i in e)t.call(e,e[i],i)},a=function(e,t){var i=/(\/)[^/]+\1\.\.\1/,r=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),n=r+t;for(n=n.replace(/\/\.\//g,"/");n.match(i);)n=n.replace(i,"/");return n},u=t.helpers={$include:function(e,i,r){var n=a(r,e);return t.render(n,i)},$string:r,$escape:o,$each:l},p=function(t){var i="";for(var r in t)i+="<"+r+">\n"+t[r]+"\n\n";return i&&e.console&&console.error("Template Error\n\n"+i),function(){return"{Template Error}"}};t.render=function(e,i){var r=t.get(e)||p({id:e,name:"Render Error",message:"No Template"});return i?r(i):r},t.compile=function(e,t){var r="function"==typeof t,n=i[e]=function(i){try{return r?new t(i,e)+"":t}catch(n){return p(n)()}};return n.prototype=t.prototype=u,n.toString=function(){return t+""},n},t.get=function(e){return i[e.replace(/^\.\//,"")]},t.helper=function(e,t){u[e]=t},/**/ +t("copyright","(c) 2013"),/**/ +t("index",function(e,t){var i=this,include=function(r,n){n=n||e;var o=i.$include(r,n,t);return void 0!==o?(l+=o,o):void 0},r=i.$escape,n=e.title,o=i.$each,c=e.list,l=(e.$value,e.$index,"");return include("./public/header"),l+='

',l+=r(n),l+="

",include("./public/footer"),new String(l)}),/**/ +t("public/footer",function(e,t){var i=this,r=e.time,n=i.$escape,include=function(r,n){n=n||e;var c=i.$include(r,n,t);return void 0!==c?(o+=c,c):void 0},o="";return o+='",new String(o)}),/**/ +t("public/header",function(e,t){var i=this,include=function(n,o){o=o||e;var c=i.$include(n,o,t);return void 0!==c?(r+=c,c):void 0},r="";return r+=' ',new String(r)}),/**/ +t("public/logo",'

\u817e\u8baf\u7f51

'),"function"==typeof define?define(function(){return t}):"undefined"!=typeof exports?module.exports=t:e.template=t}(this); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index 8c5da6c..a6e51e5 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.3" + "tmodjs": "~0.0.3-rc1" }, "tmodjs-config": { "output": "./build", @@ -12,6 +12,7 @@ ], "syntax": "simple", "helpers": null, + "alias": null, "minify": true, "async": false, "engine": false, diff --git a/tmod.js b/tmod.js index 870fafa..09f12ba 100644 --- a/tmod.js +++ b/tmod.js @@ -80,6 +80,9 @@ module.exports = { // 自定义辅助方法路径 helpers: null, + // 运行时别名 + alias: null, + // 是否输出为压缩的格式 minify: true, @@ -554,7 +557,7 @@ module.exports = { }); - var target = path.join(this.output, (this.options.runtime || RUNTIME) + '.js'); + var target = path.join(this.output, RUNTIME + '.js'); this._fsWrite(target, template); @@ -677,7 +680,7 @@ module.exports = { if (isChange) { modObject = template.AOTcompile(id, source, { - runtime: this.options.runtime, + alias: this.options.alias, engine: this.options.engine, type: this.options.type, debug: isDebug From c8ff4c816bfd3173261eaa4ee4301e8a18af165e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 3 Jan 2014 00:40:26 +0800 Subject: [PATCH 08/87] v0.0.4 --- README.md | 161 ++--- bin/tmod | 38 +- doc/why-tmodjs.md | 243 ++++++++ lib/{template-AOTcompile.js => AOTcompile.js} | 2 +- lib/runtime/basic.js | 5 +- lib/runtime/full.js | 3 +- lib/template-async.js | 163 ----- lib/template.js | 583 ------------------ lib/uglify.js | 4 +- package.json | 10 +- test/test-all/amd/build/copyright.js | 2 +- test/test-all/amd/build/index.js | 10 +- test/test-all/amd/build/public/footer.js | 10 +- test/test-all/amd/build/public/header.js | 10 +- test/test-all/amd/build/public/logo.js | 2 +- test/test-all/amd/build/template.js | 98 +-- test/test-all/amd/package.json | 12 +- test/test-all/cmd-alias/build/copyright.js | 2 +- test/test-all/cmd-alias/build/index.js | 10 +- .../test-all/cmd-alias/build/public/footer.js | 10 +- .../test-all/cmd-alias/build/public/header.js | 10 +- test/test-all/cmd-alias/build/public/logo.js | 2 +- test/test-all/cmd-alias/build/template.js | 2 +- test/test-all/cmd-alias/package.json | 13 +- test/test-all/cmd/build/copyright.js | 2 +- test/test-all/cmd/build/index.js | 10 +- test/test-all/cmd/build/public/footer.js | 10 +- test/test-all/cmd/build/public/header.js | 10 +- test/test-all/cmd/build/public/logo.js | 2 +- test/test-all/cmd/build/template.js | 98 +-- test/test-all/cmd/package.json | 12 +- test/test-all/helper.html | 49 ++ test/test-all/helper/build/index.js | 2 + test/test-all/helper/build/template.js | 3 + test/test-all/helper/index.html | 8 + test/test-all/helper/package.json | 18 + test/test-all/helper/template-helpers.js | 9 + test/test-all/include/build/include.js | 12 +- test/test-all/include/build/template.js | 99 +-- test/test-all/include/package.json | 12 +- test/test-all/mix/build/copyright.js | 2 +- test/test-all/mix/build/index.js | 2 +- test/test-all/mix/build/public/footer.js | 2 +- test/test-all/mix/build/public/header.js | 2 +- test/test-all/mix/build/public/logo.js | 2 +- test/test-all/mix/build/template.js | 71 ++- test/test-all/mix/package.json | 10 +- test/{node.js => test-all/syntax-native.html} | 23 +- test/test-all/syntax-native/.debug.js | 13 + test/test-all/syntax-native/build/index.js | 2 + test/test-all/syntax-native/build/template.js | 3 + test/test-all/syntax-native/index.html | 8 + test/test-all/syntax-native/package.json | 18 + test/test-all/syntax.html | 49 ++ test/test-all/syntax/.debug.js | 20 +- test/test-all/syntax/build/each.js | 2 - test/test-all/syntax/build/index.js | 2 + test/test-all/syntax/build/template.js | 6 +- test/test-all/syntax/each.html | 8 - test/test-all/syntax/index.html | 11 + test/test-all/syntax/package.json | 14 +- .../test-all/syntax}/template-syntax.js | 5 + test/tpl/build/copyright.js | 4 +- test/tpl/build/index.js | 42 +- test/tpl/build/public/footer.js | 35 +- test/tpl/build/public/header.js | 26 +- test/tpl/build/public/logo.js | 4 +- test/tpl/build/template.js | 218 ++++++- test/tpl/package.json | 13 +- tmod.js | 188 +++--- 70 files changed, 1303 insertions(+), 1263 deletions(-) create mode 100644 doc/why-tmodjs.md rename lib/{template-AOTcompile.js => AOTcompile.js} (99%) delete mode 100644 lib/template-async.js delete mode 100644 lib/template.js create mode 100644 test/test-all/helper.html create mode 100644 test/test-all/helper/build/index.js create mode 100644 test/test-all/helper/build/template.js create mode 100644 test/test-all/helper/index.html create mode 100644 test/test-all/helper/package.json create mode 100644 test/test-all/helper/template-helpers.js rename test/{node.js => test-all/syntax-native.html} (70%) create mode 100644 test/test-all/syntax-native/.debug.js create mode 100644 test/test-all/syntax-native/build/index.js create mode 100644 test/test-all/syntax-native/build/template.js create mode 100644 test/test-all/syntax-native/index.html create mode 100644 test/test-all/syntax-native/package.json create mode 100644 test/test-all/syntax.html delete mode 100644 test/test-all/syntax/build/each.js create mode 100644 test/test-all/syntax/build/index.js delete mode 100644 test/test-all/syntax/each.html create mode 100644 test/test-all/syntax/index.html rename {lib => test/test-all/syntax}/template-syntax.js (95%) diff --git a/README.md b/README.md index 51c4fe7..f899d58 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,14 @@ TmodJS(原名 atc)是一款前端模板编译工具,它可以让前端模 通过 TmodJS 预编译技术让前端模板突破浏览器的文本文件加载限制,支持模板按文件存放,并且支持多层目录组织模板,并且模板之间可通过``include``语句进行复用。 +在 TmodJS 的规范中,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系 —— 这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在 tpl/public 目录下,新闻栏目的模板可以放在 tpl/news 下面。 + +总之,使用文件系统来管理模板已经在服务器端模板中得到广泛的验证,而在前端也同样适用,无论项目规模是多么轻量或者庞大。 + ### 模板编译输出为 js 文件 它会将模板编译成 js 文件,编译后的代码体积非常小,不包含模板引擎也无需依赖脚本加载器。因为是预编译,省去前端模板客户端动态编译过程,也能够在移动设备中节省一定的系统资源。 -编译成 js 后,原来字符串模板遇到的跨域加载、异步按需加载已经不是问题,甚至可以接入 GruntJS 等自动化工具进行部署。 - ### 支持接入 AMD 或者 CMD 部署工具 默认情况下,模板目录将会被打包成 js, 可以直接在页面中使用传统 Script 标签加载,简单且有效。除此之外还可以设置将单个模板文件输出为单个的 AMD 、CMD 异步模块,以便接入它们自动化部署工具进行深度定制化的优化,这样可以实现按需加载、合并等高级的优化手段。 @@ -40,21 +42,16 @@ TmodJS(原名 atc)是一款前端模板编译工具,它可以让前端模 先安装 [NodeJS](http://nodejs.org) 与 Npm (最新版 NodeJS 已经附带 Npm),执行: ``` -$ npm install tmodjs -g +$ npm install -g tmodjs ``` -> Mac OSX 可能需要管理员权限运行: ``$ sudo npm install tmodjs -g`` +> Mac OSX 可能需要管理员权限运行: ``$ sudo npm install -g tmodjs`` ## 快速入门 学习 TmodJS 只需要理解这四个关键点就好,5分钟可入门: -1. 建立模板目录:TmodJS 是基于目录编译,你至少要给项目建立一个专用的前端模板目录 -2. 编写模板:你需要了解 TmodJS 的模板语句,如输出变量、条件判断、循环等 -3. 编译模板:编译模板你需要知道如何使用命令参数 -4. 调用模板:如何在项目中加载模板 - ### 一、建立模板目录 TmodJS 的前端模板不再耦合在业务页面中,而是和后端模板一样有专门的目录管理。目录名称支持英文、数字、下划线。 @@ -68,55 +65,72 @@ TmodJS 的前端模板不再耦合在业务页面中,而是和后端模板一 ### 三、编译模板 ``` -$ tmod [path] [options] +$ tmod [模板目录] [配置参数] ``` -其中 path 是模板目录。TmodJS 基于目录进行处理。 +示例: + +``` +$ tmod ./tpl -w -output ./build +``` + +#### 模板目录 + +必须是模板的根目录,若无参数则为默认使用当前工作目录。 -#### options +#### 配置参数 * ``-w``或``--watch``设置监控模板修改触发编译 * ``-d``或``--debug``输出调试版本 * ``--charset value``定义模板编码,默认``utf-8`` * ``--output value``定义输出目录,默认``./build`` * ``--type value``定义输出模块格式,默认``templatejs``,可选``cmd``、``amd``、``commonjs`` -* ``--version``显示 TmodJS 版本号 +* ``--version``显示版本号 * ``--help``显示帮助信息 -运行完成后,程序会将模板编译成 js 文件。如果你经常需要修改模板,可以开启``-w``参数,让它检测修改自动编译。 +配置参数将会保存在模板目录[配置文件](#配置)中,下次运行无需输入配置参数(-w 与 -d 除外)。 -> 首次运行后,会对模板根目录进行初始化,生成 package.json,也可以编辑它进行更多配置,包括语法、公用辅助方法、压缩选项等,参考[配置](#配置)。 -> 当模板目录初始化后,下次编译模板可以无需输入配置参数,将沿用上一次的参数进行编译(-w 与 -d 除外)。 +> 1. 如果你经常需要修改模板,可以开启``-w``参数,让它检测修改自动编译。 +> 2. 如果需要设置模板更多的编译选项,请使用``--config``参数,它会打开模板目录的项目配置文件,可设置语法、公用辅助方法、压缩选项等,参考[配置](#配置)。 ### 四、调用模板 -模板编译后,模板目录会生成 build 子目录,里面包含了所有的模板编译版本。其中 build/template.js 是压缩后的模板包,通常情况下你只需要在页面中引入它就好(其余的文件可以暂时忽略)。例如: +模板编译后,模板目录会生成 build 子目录,里面包含了所有的模板编译版本。编译后的模板可以使用同步接口加载模板。 - +#### 1. 默认类型格式的使用方式 -这是默认的加载方式,除此之外还支持 RequireJS、SeaJS、NodeJS 加载。[示例](http://aui.github.io/tmodjs/test/index.html) - -#### 模板接口 +模板的``type``为默认值(``type:templatejs``)的时候,TmodJS 默认将整个目录的模板压缩合并到一个名为 template.js 的脚本中,通常情况下你只需要在页面中引入它就好(其余的文件可暂时忽略)。例如: -``` -template(path, data) -``` + + +除此之外 template.js 还支持 RequireJS、SeaJS、NodeJS 加载。[示例](http://aui.github.io/tmodjs/test/index.html) -path 参数是**模板目录相对路径**,并且**不带后缀名**,例如 : +加载并渲染模板示例: ``` var html = template('news/list', {hot: [...]}); document.getElementById('list').innerHTML = html; ``` +#### 2. 其他类型格式使用方式(amd / cmd / commonjs) + +这个时候每个模板编译后都是一个 js 模块,每个模板可在模块中单独引入,无需引入 template.js 文件,加载并渲染模板示例: + +``` +var html = require('./tpl/build/news/list'); +document.getElementById('list').innerHTML = html; +``` + +> 注意:模板路径 ID 不能包含模板后缀名 + ## 编译演示项目 -源码包中``./test``是一个演示项目,``./test/tpl``是项目的模板目录,包含了若干模板。你可以通过这个演示项目快速了解 TmodJS 用法以及模板语法、模板加载方式。 +[TmodJS 源码包](https://github.com/aui/tmodjs/archive/master.zip)中``./test``是一个演示项目,``./test/tpl``是项目的模板目录,``./test/index.html``是首页。你可以通过这个演示项目快速了解 TmodJS 用法以及模板语法、模板加载方式。 -首先,使用 cd 命令切换到 TmodJS 目录后,你可以编译这个目录模板: +首先,使用 cd 命令切换到 TmodJS 源码的``./test/tpl``目录后,直接运行即可: ``` -$ tmod ./test/tpl +$ tmod ``` 编译完毕后你可以在浏览器中打开 [test/index.html](http://aui.github.io/tmodjs/test/index.html) 查看如何加载模板。 @@ -161,48 +175,48 @@ TmodJS.on('compile', function (data) {}); ## 配置 -配置最终会保存在模板目录的 package.json 文件中,可以对它直接修改。 +配置最终会保存在模板目录 package.json 文件中,你可以修改``"tmodjs-config"``,配置说明: ``` -{ - // 编译输出目录设置 - output: './build', - - // 模板使用的编码。(注意:非 utf-8 编码的模板缺乏测试) - charset: 'utf-8', - - // 模板合并规则 - // 注意:type 参数的值为 templatejs 才会生效 - combo: ['*'], - - // 定义模板采用哪种语法,可选: - // simple: 默认语法,易于读写。可参看语法文档 - // native: 功能丰富,灵活多变。语法类似微型模板引擎 tmpl - syntax: 'simple', - - // 自定义辅助方法路径 - helpers: null, - - // 是否输出为压缩的格式 - minify: true, - - // 是否内嵌异步加载插件(beta) - // 可以支持 template.async(path, function (render) {}) 方式异步载入模板 - // 注意:type 参数是 templatejs 的时候才生效 - async: false, - - // 是否嵌入模板引擎,否则编译为不依赖引擎的纯 js 代码 - // 通常来说,模板不多的情况下,编译为原生的 js 打包后体积更小,因为不必嵌入引擎 - // 当模板很多的时候,内置模板引擎,模板使用字符串存储的方案会更能节省空间 - engine: false, - - // 输出的模块类型(不区分大小写),可选: - // templatejs: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 - // cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) - // amd: 支持 RequireJS 等流行加载器 - // commonjs: 编译为 NodeJS 模块 - type: 'templatejs' -} +// 编译输出目录设置 +output: './build', + +// 模板使用的编码。(注意:非 utf-8 编码的模板缺乏测试) +charset: 'utf-8', + +// 定义模板采用哪种语法,内置可选: +// simple: 默认语法,易于读写。可参看语法文档 +// native: 功能丰富,灵活多变。语法类似微型模板引擎 tmpl +syntax: 'simple', + +// 自定义辅助方法路径 +helpers: null, + +// 是否过滤 XSS +// 如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率 +escape: true, + +// 是否嵌入模板引擎,否则编译为不依赖引擎的纯 js 代码 +// 选择嵌入模板引擎后,模板以字符串存储并浏览器中执行编译 +engine: false, + +// 输出的模块类型,可选: +// templatejs: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 +// cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) +// amd: 支持 RequireJS 等流行加载器 +// commonjs: 编译为 NodeJS 模块 +type: 'templatejs', + +// 运行时别名 +// 仅针对于非 templatejs 的类型模块 +alias: null, + +// 是否合并模板 +// 仅针对于 templatejs 类型的模块 +combo: true, + +// 是否输出为压缩的格式 +minify: true ``` ## 常见问题 @@ -247,12 +261,19 @@ TmodJS.on('compile', function (data) {}); > 请参考:[页面中的模板迁移指南](https://github.com/aui/tmodjs/wiki/页面中的模板迁移指南)。 -**问**:如何不合并输出? +**问**:我需要手动合并模板,如何让 tmodjs 不合并输出? -> 编辑配置文件,设置``combo:[]``。 +> 编辑配置文件,设置``combo:false``。 ## 更新日志 +### TmodJS v0.0.4 + +* 简化``combo``功能,只提供全部合并与不合并两个选项,值为布尔类型(兼容旧的版本的配置,会自动转换成布尔类型)。 +* 取消鸡肋的异步载入插件,同时``async``配置失效 +* 为了便于理解,命令行输入的``--output``参数不再相对于模板目录,而是工作目录(配置文件的``output``参数仍保持不变) +* 优化控制台日志显示 + ### TmodJS v0.0.3 * 修复``combo``配置不能为空数组的 BUG diff --git a/bin/tmod b/bin/tmod index aad4bbf..3a7eb01 100644 --- a/bin/tmod +++ b/bin/tmod @@ -3,7 +3,12 @@ 'use strict'; var TmodJS = require('../tmod.js'); +var version = require('../package.json').version; + var fs = require('fs'); +var path = require('path'); +var exec = require('child_process').exec; +var os = require('os'); var options = {}; @@ -49,7 +54,9 @@ var help = function () { var dir; var value; +var userConfig; var isWatch = false; +var isEditConfig = false; var args = process.argv.slice(2); @@ -104,6 +111,10 @@ while (args.length > 0) { options.helpers = args.shift(); break; + case '--config': + isEditConfig = true; + break; + // 显示帮助 case '-h': case '--help': @@ -114,7 +125,6 @@ while (args.length > 0) { // 版本号 case '-v': case '--version': - var version = require('../package.json').version; process.stdout.write(version + '\n'); process.exit(); break; @@ -140,6 +150,12 @@ if (!fs.existsSync(dir)) { }; +// 转换成相对于模板目录的路径 +if (options.output) { + options.output = path.relative(dir, path.resolve(options.output)); +} + + TmodJS.init(dir, options); TmodJS.on('error', function (data) { @@ -148,12 +164,22 @@ TmodJS.on('error', function (data) { } }); -TmodJS.saveUserConfig(); +userConfig = TmodJS.saveUserConfig(); -TmodJS.compile(); -if (isWatch) { - TmodJS.watch(); -} +if (isEditConfig) { + + process.stdout.write('open: ' + userConfig + '\n'); + + exec( + (/windows/i.test(os.type()) ? 'start' : 'open') + + ' ' + userConfig, {timeout: 0}, function () {} + ); +} else { + TmodJS.compile(); + if (isWatch) { + TmodJS.watch(); + } +} diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md new file mode 100644 index 0000000..07e30b4 --- /dev/null +++ b/doc/why-tmodjs.md @@ -0,0 +1,243 @@ +# 进击!前端模板工程化 + +## 前言 + +现在,越来越多的前端项目采用了单页或者混合式的架构,在这种架构中后端只负责吐出 JSON 数据,前端异步来渲染 HTML,前端模板被大量使用,与此同时也将引发开发维护与发布优化的问题。为了解决这些问题,我们曾经开发了工具 TmodJS(原名 atc),试图通过本地自动化工具让端模板走上工程化之路。很高兴这个工具经过长达 8 个月的磨砺后,它从一个简陋的工具变成如今健壮的前端开发“利器”,并且已经有多个大型项目在使用它。现在我将对前端模板工程化的一些思考与总结分享出来,希望能够让大家更好的理解 TmodJS 的意义所在。在主题开始之前,先简单总结下这些年前端模板技术的发展。 + +## 手工拼接字符串时代 + +早期,开发人员都是直接在 js 文件中采用最原始的方式直接拼接 HTML 字符串: + + var html = ''; + for (var i = 0, users = data.users; i < users.length; i ++) { + html += '
  • ' + + users[i].name + + '
  • '; + } + //... + +这种方式刚开始在一两个简单的页面中还是比较灵活的,但弊端也十分明显:UI 与逻辑代码混杂在一起,阅读起来会非常吃力。一旦随着业务复杂起来,或者多人维护的情况下,几乎会失控。 + +## 前端模板引擎时代 + +受 jquery 作者的 tmpl 模板引擎影响,从 09 年开始到现在,前端模板引擎出现了百花齐放的局面,涌现出一大批行色各异的引擎,几乎每个前端工程中都使用了模板引擎。一条前端模板类似这样: + + + +它使用一个特殊的````标签来存放模板(由于浏览器不支持这种类型的声明,它存放的代码不会当作 js 运行,代码也不会被显示出来)。使用模板引擎渲染模板的示例: + + var html = tmpl('user_tmpl', data); + document.getElementById('content').innerHTML = html; + +[在线示例](http://aui.github.io/artTemplate/demo/simple-syntax/basic.html) + +通过前端模板引擎将 UI 分离后,模板的书写与修改就变得简单多了,也提升了可维护性。但是,随着这种方式规模化后其弊端也随之而来。 + +### 页面内嵌模板之弊端 + +#### 1. 开发调试 + +每次修改前端模板需要改动页面的代码,有时候存放模板的页面又依赖服务器,这使得我们无法使用使用类似 Fiddler 的工具将页面映射到本地进行开发,从而不得不传到服务器或者自己搭建本地服务器环境,以至于开发维护过程异常繁琐。 + +#### 2. 体积优化 + +动态页面与前端模板结合的架构中,不利于浏览器缓存,通常在单页应用中,页面页面会堆砌着大量的````标签,每次进入应用都需要重新加载模板代码,造成不必要的网络开销。 + +#### 3. 模板复用 + +比如类似“好友选择器”这样的公用模块,页面内嵌模板满足不了模板复用的需求。 + +以上三个问题本质都是因为模板堆砌在一个文件中造成的,于是越来越的项目开始将前端模板从页面中迁移出来,目前主要有两种方式: + +### 优化:外置模板 + +#### 1. Ajax 拉取方案 + +通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的弊端相当明显: + +1. 无法跨域部署。这是由浏览器同源策略限制的,导致模板无法部署到 CDN 网络。 +2. 复杂度比较高,难以接入主流的自动化部署、优化工具。 + +#### 2. 在 JS 文件中存放模板 + +为避免上述加载模板方案无法跨域的致命缺陷,模板存放在 js 文件中又成了最佳实践方式。但是 js 需要对回车符进行转义,对书写不友好,例如: + + var user_tmpl = + '<% for (var i = 0; i < users.length; i++) { %>\ +
  • \ + \ + <%=users[i].name%>\ + \ +
  • \ + <% } %>'; + +### 模板存放方案优劣总结 + +存放方式 | 书写友好 | CDN 跨域部署 | 本地调试 | 代码复用 | 按需加载 +------ | ------ | ------ | ------ | ------ | ------ | ------ +内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ +Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ +嵌入 js 文件 | ✗ | ✓ | ✓ | ✓ | ✓ + +在实践中我们发现:方便优化的模式不利于开发;利于开发的模式不利于优化。 + +为了解决上述问题,业界先后出现了一些工具,如 handlebars.js 与 Hogan.js(来自 Twitter) 采用了预编译技术来完成模板到 js 的转换,以 handlebars.js 使用为例,先使用 NodeJS 安装它: + + $ npm install -g handlebars + +然后提取模板内容(``script`` 标签之间)并保存到一个文件中。在这里我们把它保存为 ``user.tmpl``。运行 ``handlebars`` 命令预编译这个模板文件。 + + $ handlebars user.tmpl -f user.tmpl.js + +编译完成后就可以在前端应用中加载这个脚本,比如这样引入: + + + +在逻辑中可以如下访问到模板函数: + + var template = Handlebars.templates["user.tmpl"]; + var html = template(data); + +预编译工具在一定程度上解决了我们的问题,但由于操作实在是太繁琐,因此它们也并没有流行起来。例如一个 web app 单页应用,几百条前端模板是常有的事儿,开发阶段我们会不断的添加、修改模板,如果每次都需要重新编译这简直会令人抓狂,与此同时大量零散的模板脚本也会引发新的问题,编译后的模板没有提供显式的依赖声明,对于大型项目来说,自动化工具依然难以介入。 + +就目前而言,业界依然没有出现针对前端模板成熟的自动化工具,于是经过无数次实践、在各种方案权衡后,我们研发了全新自动化工具 —— TmodJS,为前端模板工程化而来。 + +## 工程化前端模板 + +TmodJS 采用一系列集成方案来最大化提升前端工程效率与质量,本地预编译技术的运用使得我们不必局限于浏览器的技术限制,从而让我们更多的想法通过工具来执行。 + +### 1. 基于文件系统 + +在 TmodJS 的规范中,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系 —— 这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在 tpl/public 目录下,新闻栏目的模板可以放在 tpl/news 下面。 + +同时,模板内部也支持``include``语句来引入子模板,实现模块复用。例如: + + {{include './public/header'}} + +每个模板就是一个 HTML 片段文件,前端开发工程师可直接将设计师的静态页面的 HTML 拷贝过来,无需对换行符转义,这样开发过程更加便利。 + +总之,使用文件系统来管理模板已经在服务器端模板中得到广泛的验证,而在前端也同样适用,无论项目规模是多么轻量或者庞大。 + +### 2. 使用同步加载接口 + +TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD 规范实现,从而避免浏览器的异步加载带来的各种问题,如网络速度、回调套嵌等。例如加载模板 news/index.html: + + var tpl = template('news/index'); + +模板渲染方式: + + var html = tpl(data); + document.getElementById('content').innerHTML = html; + +### 3. 自动化执行优化 + +在默认设置下,TmodJS 会将模板目录所有模板编译后再进行压缩与合并,输出后的 template.js 称之为模板包(内部名称叫 TemplateJS 格式)这种打包的方式非常适合移动端单页 WebApp,输出后的模板包可直接可作为开发阶段与线上运行的文件。 + +当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行优化,例如大名鼎鼎的 GruntJS。除此之外,使用 RequireJS 与 SeaJS 或者 NodeJS 的用户还可以通过配置将每一个模板都编译成与它们的模块,这时候模板加载的接口是这样的: + + var tpl = requier('tpl/news/index'); + var html = tpl(data); + //... + +这种情况下模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 + +总之,模板转换为 js 后不但解决了跨域部署的烦恼,其优化空间也更加灵活多样。案例: + +1. 腾讯视频前端团队的实践:关闭 TmodJS 的打包合并,按照网站栏目建立模板子目录,通过 GruntJS 按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 +2. QQ 空间 CDN 有线上 SeaJS 模块动态合并服务,这时候 TmodJS 编译后的模板会被当作一个普通的 SeaJS 模块引入到项目中,当 UI 模块被调用的时候逻辑与依赖的模板都会进行动态合并加载,完全无需本地构建工具操作。 +3. MicroTrend 是腾讯内部的一个移动端单页 WebApp 项目,包含较多的模板片段,发布前每次都需要手工进行模板抽离与压缩,极其耗时。采用 TmodJS 进行模板管理,模板被打包压缩到一个 js 文件中,开发阶段输出的模板包直接作为发布后的文件,节省了发布阶段繁琐的优化环节。[查看编译后的模板](http://microtrend.cdc.tencent.com/tpl/dist/template.js) + +### 4. 本地调试支持 + +通常开发阶段模板会经常被修改,所以 TmodJS 支持监听模板目录的修改,当模板发生修改则会进行增量编译,时间久了甚至可以让人忘记编译过程的存在,完全忘记这是在写前端模板。 + +无论如何模板最终都会转换成 js,亦可使用 Fiddler 将线上模板映射到本地进行开发调试;如果开启实时编译,开发阶段模板修改后只需要刷新浏览器即可预览到效果。 + +通常在模板开发阶段会经常遇到数据与预期不符合的情况,TmodJS 编译后的模板还支持运行时调试,可以在控制台输出报错信息,并且可精确到模板所在行: + + Template Error + + + public/header + + + Render Error + + + Cannot read property '0' of undefined + + + 5 + + + {{users[0].name}} + +### 5. 前后端模板共用 + +一般项目,我们直接采用`` + + + + + + diff --git a/test/test-all/helper/build/index.js b/test/test-all/helper/build/index.js new file mode 100644 index 0000000..d5a1d7e --- /dev/null +++ b/test/test-all/helper/build/index.js @@ -0,0 +1,2 @@ +/* */ +template("index",function(i){var t=this,e=t.$string,l=t.$ubb2html,n=i.title,a=t.$each,u=i.list,r=(i.$value,i.$index,t.$escape),h="";return h+='

    ',h+=e(l(n)),h+="

    ",new String(h)}); \ No newline at end of file diff --git a/test/test-all/helper/build/template.js b/test/test-all/helper/build/template.js new file mode 100644 index 0000000..badd02d --- /dev/null +++ b/test/test-all/helper/build/template.js @@ -0,0 +1,3 @@ +/* */ +!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},u=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},o=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},a=r.helpers={$include:function(e,n,t){var i=o(t,e);return r.render(i,n)},$string:t,$escape:u,$each:l},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=a,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){a[e]=r},r.helper("$ubb2html",function(e){return e=r.helpers.$escape(e),e.replace(/\[b\]([^\[]*?)\[\/b\]/gim,"$1").replace(/\[i\]([^\[]*?)\[\/i\]/gim,"$1").replace(/\[u\]([^\[]*?)\[\/u\]/gim,"$1").replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/gim,'$2').replace(/\[img\]([^\[]*?)\[\/img\]/gim,'')}),/**/ +r("index",function(e){var r=this,n=r.$string,t=r.$ubb2html,i=e.title,u=r.$each,c=e.list,l=(e.$value,e.$index,r.$escape),o="";return o+='

    ',o+=n(t(i)),o+="

    ",new String(o)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/test-all/helper/index.html b/test/test-all/helper/index.html new file mode 100644 index 0000000..02db824 --- /dev/null +++ b/test/test-all/helper/index.html @@ -0,0 +1,8 @@ +
    +

    {{$ubb2html title}}

    + +
    \ No newline at end of file diff --git a/test/test-all/helper/package.json b/test/test-all/helper/package.json new file mode 100644 index 0000000..2382cc6 --- /dev/null +++ b/test/test-all/helper/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.4" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": "./template-helpers.js", + "escape": true, + "engine": false, + "type": "templatejs", + "combo": true, + "minify": true + } +} \ No newline at end of file diff --git a/test/test-all/helper/template-helpers.js b/test/test-all/helper/template-helpers.js new file mode 100644 index 0000000..c53b401 --- /dev/null +++ b/test/test-all/helper/template-helpers.js @@ -0,0 +1,9 @@ +template.helper('$ubb2html', function (content) { + content = template.helpers.$escape(content); + return content + .replace(/\[b\]([^\[]*?)\[\/b\]/igm, '$1') + .replace(/\[i\]([^\[]*?)\[\/i\]/igm, '$1') + .replace(/\[u\]([^\[]*?)\[\/u\]/igm, '$1') + .replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/igm, '$2') + .replace(/\[img\]([^\[]*?)\[\/img\]/igm, ''); +}); \ No newline at end of file diff --git a/test/test-all/include/build/include.js b/test/test-all/include/build/include.js index c79a7f5..eddd1b7 100644 --- a/test/test-all/include/build/include.js +++ b/test/test-all/include/build/include.js @@ -1,13 +1,11 @@ -/*! */ +/* */ define([ "./template", "./a", "./b", "./e", "./d" ], function(template) { - return template("./include", function($data, $id) { + return template("include", function($data, $id) { var $helpers = this, include = function(id, data) { data = data || $data; - var content = $helpers.$include(id, data, $id); - if (content !== undefined) { - $out += content; - return content; - } + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; }, labe = $data.labe, xxx = $data.xxx, $out = ""; include("./a", { labe: ")" diff --git a/test/test-all/include/build/template.js b/test/test-all/include/build/template.js index 6df046e..70267fc 100644 --- a/test/test-all/include/build/template.js +++ b/test/test-all/include/build/template.js @@ -1,57 +1,66 @@ -/*! */ +/* */ !function(global) { var template = function(path, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; var cache = template.cache = {}; - var helpers = template.helpers = { - $string: function(value) { - var type = typeof value; - if (!/string|number/.test(type)) { - value = type === "function" ? helpers.$string(value()) : ""; - } - return value + ""; - }, - $escape: function(content) { - var m = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - return helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return m[s]; - }); - }, - $each: function(data, callback) { - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } + var toString = function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = toString(value.call(value)); } else { - for (i in data) { - callback.call(data, data[i], i); - } + value = ""; } - }, - $resolve: function(from, to) { - var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/[^/]+$/, ""); - var id = dirname + to; - id = id.replace(/\/\.\//g, "/"); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, "/"); + } + return value; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; + }); + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); } - return id; - }, + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + var helpers = template.helpers = { $include: function(path, data, from) { - var id = helpers.$resolve(from, path); + var id = resolve(from, path); return template.render(id, data); - } + }, + $string: toString, + $escape: escapeHTML, + $each: each }; var debug = function(e) { var message = ""; @@ -89,7 +98,7 @@ return render; }; template.get = function(id) { - return cache[id.replace(/^([^.])/, "./$1")]; + return cache[id.replace(/^\.\//, "")]; }; template.helper = function(name, helper) { helpers[name] = helper; diff --git a/test/test-all/include/package.json b/test/test-all/include/package.json index 73ab260..aacbeba 100644 --- a/test/test-all/include/package.json +++ b/test/test-all/include/package.json @@ -2,19 +2,17 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.2" + "tmodjs": "~0.0.4" }, "tmodjs-config": { "output": "./build", "charset": "utf-8", - "combo": [ - "*" - ], "syntax": "native", "helpers": null, - "minify": false, - "async": false, + "escape": true, "engine": false, - "type": "amd" + "type": "amd", + "alias": null, + "minify": false } } \ No newline at end of file diff --git a/test/test-all/mix/build/copyright.js b/test/test-all/mix/build/copyright.js index f38fe34..71f269b 100644 --- a/test/test-all/mix/build/copyright.js +++ b/test/test-all/mix/build/copyright.js @@ -1,2 +1,2 @@ -/*! */ +/* */ template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/mix/build/index.js b/test/test-all/mix/build/index.js index f17d8a4..dbc2f52 100644 --- a/test/test-all/mix/build/index.js +++ b/test/test-all/mix/build/index.js @@ -1,2 +1,2 @@ -/*! */ +/* */ template("index", "{{include './public/header'}}

    {{title}}

    {{include './public/footer'}}"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/footer.js b/test/test-all/mix/build/public/footer.js index e9e4d65..49ad12b 100644 --- a/test/test-all/mix/build/public/footer.js +++ b/test/test-all/mix/build/public/footer.js @@ -1,2 +1,2 @@ -/*! */ +/* */ template("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); \ No newline at end of file diff --git a/test/test-all/mix/build/public/header.js b/test/test-all/mix/build/public/header.js index 08271ef..4ee544f 100644 --- a/test/test-all/mix/build/public/header.js +++ b/test/test-all/mix/build/public/header.js @@ -1,2 +1,2 @@ -/*! */ +/* */ template("public/header", ' '); \ No newline at end of file diff --git a/test/test-all/mix/build/public/logo.js b/test/test-all/mix/build/public/logo.js index 3f2b672..315efa7 100644 --- a/test/test-all/mix/build/public/logo.js +++ b/test/test-all/mix/build/public/logo.js @@ -1,2 +1,2 @@ -/*! */ +/* */ template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/mix/build/template.js b/test/test-all/mix/build/template.js index fd4e4b8..3971fd0 100644 --- a/test/test-all/mix/build/template.js +++ b/test/test-all/mix/build/template.js @@ -1,10 +1,10 @@ -/*! */ +/* */ (function(global) { "use strict"; var template = function(id, content) { return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); }; - template.version = "2.0.2"; + template.version = "2.0.3"; template.openTag = "<%"; template.closeTag = "%>"; template.isEscape = true; @@ -54,37 +54,36 @@ return render; }; var _cache = template.cache = {}; - var _helpers = template.helpers = { - $include: template.render, - $string: function(value, type) { + var _helpers = template.helpers = function() { + var toString = function(value, type) { if (typeof value !== "string") { type = typeof value; if (type === "number") { value += ""; } else if (type === "function") { - value = _helpers.$string(value()); + value = toString(value.call(value)); } else { value = ""; } } return value; - }, - $escape: function(content) { - var m = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - return _helpers.$string(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return m[s]; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; }); - }, - $each: function(data, callback) { - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { if (isArray(data)) { for (var i = 0, len = data.length; i < len; i++) { callback.call(data, data[i], i, data); @@ -94,8 +93,14 @@ callback.call(data, data[i], i); } } - } - }; + }; + return { + $include: template.render, + $string: toString, + $escape: escapeHTML, + $each: each + }; + }(); template.helper = function(name, helper) { _helpers[name] = helper; }; @@ -156,10 +161,10 @@ var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); var isNewEngine = "".trim; var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; - var concat = isNewEngine ? "if(content!==undefined){$out+=content;return content;}" : "$out.push(content);"; - var print = "function(content){" + concat + "}"; - var include = "function(id,data){" + "data=data||$data;" + "var content=$helpers.$include(id,data,$id);" + concat + "}"; - forEach(code.split(openTag), function(code, i) { + var concat = isNewEngine ? "$out+=$text;return $text;" : "$out.push($text);"; + var print = "function($text){" + concat + "}"; + var include = "function(id,data){" + "data=data||$data;" + "var $text=$helpers.$include(id,data,$id);" + concat + "}"; + forEach(code.split(openTag), function(code) { code = code.split(closeTag); var $0 = code[0]; var $1 = code[1]; @@ -206,8 +211,8 @@ }); } if (code.indexOf("=") === 0) { - var isEscape = code.indexOf("==") !== 0; - code = code.replace(/^=*|[\s;]*$/g, ""); + var isEscape = !/^=[=#]/.test(code); + code = code.replace(/^=[=#]?|[\s;]*$/g, ""); if (isEscape && template.isEscape) { var name = code.replace(/\s*\([^\)]+\)/, ""); if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { @@ -271,7 +276,7 @@ !function(global, template) { var get = template.get; var helpers = template.helpers; - helpers.$resolve = function(from, to) { + var resolve = function(from, to) { var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); var id = dirname + to; @@ -282,7 +287,7 @@ return id; }; helpers.$include = function(path, data, from) { - var id = helpers.$resolve(from, path); + var id = resolve(from, path); return template.render(id, data); }; template.get = function(id) { @@ -340,7 +345,7 @@ default: if (exports.helpers.hasOwnProperty(key)) { - code = "==" + key + "(" + split.join(",") + ");"; + code = "=#" + key + "(" + split.join(",") + ");"; } else { code = code.replace(/[\s;]*$/, ""); code = "=" + code; diff --git a/test/test-all/mix/package.json b/test/test-all/mix/package.json index 8202b9d..b951b5e 100644 --- a/test/test-all/mix/package.json +++ b/test/test-all/mix/package.json @@ -2,17 +2,17 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.3" + "tmodjs": "~0.0.4" }, "tmodjs-config": { "output": "./build", "charset": "utf-8", - "combo": [], "syntax": "simple", "helpers": null, - "minify": false, - "async": false, + "escape": true, "engine": true, - "type": "templatejs" + "type": "templatejs", + "combo": false, + "minify": false } } \ No newline at end of file diff --git a/test/node.js b/test/test-all/syntax-native.html similarity index 70% rename from test/node.js rename to test/test-all/syntax-native.html index f1746ac..198d33c 100644 --- a/test/node.js +++ b/test/test-all/syntax-native.html @@ -1,4 +1,16 @@ -var template = require('./tpl/build/template'); + + + + +TemplateJS - 调用模板演示 + + + + +
    loading..
    + + + + + + + -var html = template('index', data); -console.log(html) \ No newline at end of file diff --git a/test/test-all/syntax-native/.debug.js b/test/test-all/syntax-native/.debug.js new file mode 100644 index 0000000..32373cb --- /dev/null +++ b/test/test-all/syntax-native/.debug.js @@ -0,0 +1,13 @@ +/*! */ +function anonymous($data,$id) {var $helpers=this,$escape=$helpers.$escape,$ubb2html=$data.$ubb2html,title=$data.title,$each=$helpers.$each,list=$data.list,$value=$data.$value,$index=$data.$index,$out='';$out+='

    '; +$out+=$escape($ubb2html title); +$out+='

    '; +return new String($out);} \ No newline at end of file diff --git a/test/test-all/syntax-native/build/index.js b/test/test-all/syntax-native/build/index.js new file mode 100644 index 0000000..24ee584 --- /dev/null +++ b/test/test-all/syntax-native/build/index.js @@ -0,0 +1,2 @@ +/* */ +template("index",function(i){var t=this,e=t.$escape,l=i.title,n=i.i,r=i.list,a="";a+='

    ',a+=e(l),a+="

    ",new String(a)}); \ No newline at end of file diff --git a/test/test-all/syntax-native/build/template.js b/test/test-all/syntax-native/build/template.js new file mode 100644 index 0000000..f1c9e1c --- /dev/null +++ b/test/test-all/syntax-native/build/template.js @@ -0,0 +1,3 @@ +/* */ +!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},l=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},a=r.helpers={$include:function(e,n,t){var i=l(t,e);return r.render(i,n)},$string:t,$escape:o,$each:c},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=a,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){a[e]=r},/**/ +r("index",function(e){var r=this,n=r.$escape,t=e.title,i=e.i,o=e.list,u="";u+='

    ',u+=n(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/test-all/syntax-native/index.html b/test/test-all/syntax-native/index.html new file mode 100644 index 0000000..5d73bcc --- /dev/null +++ b/test/test-all/syntax-native/index.html @@ -0,0 +1,8 @@ +
    +

    <%=title%>

    + +
    \ No newline at end of file diff --git a/test/test-all/syntax-native/package.json b/test/test-all/syntax-native/package.json new file mode 100644 index 0000000..7399d59 --- /dev/null +++ b/test/test-all/syntax-native/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.4" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "native", + "helpers": null, + "escape": true, + "engine": false, + "type": "templatejs", + "combo": true, + "minify": true + } +} \ No newline at end of file diff --git a/test/test-all/syntax.html b/test/test-all/syntax.html new file mode 100644 index 0000000..70a3195 --- /dev/null +++ b/test/test-all/syntax.html @@ -0,0 +1,49 @@ + + + + +TemplateJS - 调用模板演示 + + + + +
    loading..
    + + + + + + + + diff --git a/test/test-all/syntax/.debug.js b/test/test-all/syntax/.debug.js index a41be45..32373cb 100644 --- a/test/test-all/syntax/.debug.js +++ b/test/test-all/syntax/.debug.js @@ -1,11 +1,13 @@ -/*! */ -function anonymous($data,$id) {var $helpers=this,$each=$helpers.$each,normsName=$data.normsName,feature=$data.feature,index=$data.index,$escape=$helpers.$escape,$index=$data.$index,$out='';$each(normsName['user_feature'],function(feature,index){ -$out+=' '; -$out+=$escape(normsName['user_feature_name'][index]); -$out+=' '; -$each([],function(feature],$index){ -$out+=' '; -}); -$out+=' '; +/*! */ +function anonymous($data,$id) {var $helpers=this,$escape=$helpers.$escape,$ubb2html=$data.$ubb2html,title=$data.title,$each=$helpers.$each,list=$data.list,$value=$data.$value,$index=$data.$index,$out='';$out+='

    '; +$out+=$escape($ubb2html title); +$out+='

    '; return new String($out);} \ No newline at end of file diff --git a/test/test-all/syntax/build/each.js b/test/test-all/syntax/build/each.js deleted file mode 100644 index 5eae765..0000000 --- a/test/test-all/syntax/build/each.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! */ -template("./each",function(e){var t=this,n=t.$each,r=e.normsName,a=(e.feature,e.index,t.$escape),u=(e.$value,e.$index,t.$string),s="";return n(r.user_feature,function(e,t){s+=' ',s+=a(r.user_feature_name[t]),s+=" ",n(r["user_norm_"+e],function(){s+=" "}),s+=" ",s+=u(n()),s+=" "}),new String(s)}); \ No newline at end of file diff --git a/test/test-all/syntax/build/index.js b/test/test-all/syntax/build/index.js new file mode 100644 index 0000000..dc8394c --- /dev/null +++ b/test/test-all/syntax/build/index.js @@ -0,0 +1,2 @@ +/* */ +template("index",function(i){var e=this,l=i.x,t=e.$escape,n=i.title,a=e.$each,r=i.list,u=(i.$value,i.$index,""),l="hello world";return u+=" ",u+=t(l),u+='

    ',u+=t(n),u+="

    ",new String(u)}); \ No newline at end of file diff --git a/test/test-all/syntax/build/template.js b/test/test-all/syntax/build/template.js index 6a081b5..cffa5a7 100644 --- a/test/test-all/syntax/build/template.js +++ b/test/test-all/syntax/build/template.js @@ -1,3 +1,3 @@ -/*! */ -!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=r.helpers={$string:function(e){var r=typeof e;return/string|number/.test(r)||(e="function"===r?t.$string(e()):""),e+""},$escape:function(e){var r={"<":"<",">":">",'"':""","'":"'","&":"&"};return t.$string(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return r[e]})},$each:function(e,r){var n=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)};if(n(e))for(var t=0,u=e.length;u>t;t++)r.call(e,e[t],t,e);else for(t in e)r.call(e,e[t],t)},$resolve:function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/[^/]+$/,""),u=t+r;for(u=u.replace(/\/\.\//g,"/");u.match(n);)u=u.replace(n,"/");return u},$include:function(e,n,u){var o=t.$resolve(u,e);return r.render(o,n)}},u=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||u({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var o="function"==typeof r,a=n[e]=function(n){try{return o?new r(n,e)+"":r}catch(t){return u(t)()}};return a.prototype=r.prototype=t,a.toString=function(){return r+""},a},r.get=function(e){return n[e.replace(/^([^.])/,"./$1")]},r.helper=function(e,r){t[e]=r},/**/ -r("./each",function(e){var r=this,n=r.$each,t=e.normsName,u=(e.feature,e.index,r.$escape),o=(e.$value,e.$index,r.$string),a="";return n(t.user_feature,function(e,r){a+=' ',a+=u(t.user_feature_name[r]),a+=" ",n(t["user_norm_"+e],function(){a+=" "}),a+=" ",a+=o(n()),a+=" "}),new String(a)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/* */ +!function(e){var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},c=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+n;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},a=n.helpers={$include:function(e,r,t){var i=c(t,e);return n.render(i,r)},$string:t,$escape:o,$each:l},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,i=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(i){return f(i)()}};return i.prototype=n.prototype=a,i.toString=function(){return n+""},i},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){a[e]=n},/**/ +n("index",function(e){var n=this,r=e.x,t=n.$escape,i=e.title,o=n.$each,u=e.list,l=(e.$value,e.$index,""),r="hello world";return l+=" ",l+=t(r),l+='

    ',l+=t(i),l+="

    ",new String(l)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file diff --git a/test/test-all/syntax/each.html b/test/test-all/syntax/each.html deleted file mode 100644 index 5d56052..0000000 --- a/test/test-all/syntax/each.html +++ /dev/null @@ -1,8 +0,0 @@ -{{each normsName['user_feature'] as feature index}} - - {{normsName['user_feature_name'][index]}} - - {{each normsName['user_norm_'+feature]}} - {{/each}} - {{$each}} -{{/each}} \ No newline at end of file diff --git a/test/test-all/syntax/index.html b/test/test-all/syntax/index.html new file mode 100644 index 0000000..4c27a69 --- /dev/null +++ b/test/test-all/syntax/index.html @@ -0,0 +1,11 @@ +{{var x = 'hello world'}} +{{x}} + +
    +

    {{title}}

    + +
    \ No newline at end of file diff --git a/test/test-all/syntax/package.json b/test/test-all/syntax/package.json index 6bdb78b..220ed17 100644 --- a/test/test-all/syntax/package.json +++ b/test/test-all/syntax/package.json @@ -2,19 +2,17 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.2" + "tmodjs": "~0.0.4" }, "tmodjs-config": { "output": "./build", "charset": "utf-8", - "combo": [ - "*" - ], - "syntax": "simple", + "syntax": "./template-syntax.js", "helpers": null, - "minify": true, - "async": false, + "escape": true, "engine": false, - "type": "templatejs" + "type": "templatejs", + "combo": true, + "minify": true } } \ No newline at end of file diff --git a/lib/template-syntax.js b/test/test-all/syntax/template-syntax.js similarity index 95% rename from lib/template-syntax.js rename to test/test-all/syntax/template-syntax.js index d5fa840..2bd2958 100644 --- a/lib/template-syntax.js +++ b/test/test-all/syntax/template-syntax.js @@ -19,6 +19,11 @@ switch (key) { + case 'var': + + code = 'var ' + args + ';'; + break; + case 'if': code = 'if(' + args + '){'; diff --git a/test/tpl/build/copyright.js b/test/tpl/build/copyright.js index cd05c29..73357c0 100644 --- a/test/tpl/build/copyright.js +++ b/test/tpl/build/copyright.js @@ -1,2 +1,2 @@ -/*! */ -template("copyright","(c) 2013"); \ No newline at end of file +/* */ +template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/tpl/build/index.js b/test/tpl/build/index.js index c72ce33..dabd907 100644 --- a/test/tpl/build/index.js +++ b/test/tpl/build/index.js @@ -1,2 +1,40 @@ -/*! */ -template("index",function(i,e){var t=this,include=function(l,n){n=n||i;var r=t.$include(l,n,e);return void 0!==r?(a+=r,r):void 0},l=t.$escape,n=i.title,r=t.$each,u=i.list,a=(i.$value,i.$index,"");return include("./public/header"),a+='

    ',a+=l(n),a+="

    ",include("./public/footer"),new String(a)}); \ No newline at end of file +/* */ +template("index", function($data, $id) { + var $helpers = this, $line = 0, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + try { + $line = 1; + include("./public/header"); + $out += '

    '; + $line = 4; + $out += $escape(title); + $out += "

    "; + $line = 12; + include("./public/footer"); + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: "{{include './public/header'}}\n\n
    \n

    {{title}}

    \n \n
    \n\n{{include './public/footer'}}".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); +}); \ No newline at end of file diff --git a/test/tpl/build/public/footer.js b/test/tpl/build/public/footer.js index cf641c4..87fe42d 100644 --- a/test/tpl/build/public/footer.js +++ b/test/tpl/build/public/footer.js @@ -1,2 +1,33 @@ -/*! */ -template("public/footer",function(i,e){var t=this,n=i.time,r=t.$escape,include=function(n,r){r=r||i;var u=t.$include(n,r,e);return void 0!==u?(l+=u,u):void 0},l="";return l+='",new String(l)}); \ No newline at end of file +/* */ +template("public/footer", function($data, $id) { + var $helpers = this, $line = 0, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + try { + $out += '"; + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: "
    \n{{if time}}\n

    {{time}}

    \n{{/if}}\n{{include '../copyright'}}\n
    ".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); +}); \ No newline at end of file diff --git a/test/tpl/build/public/header.js b/test/tpl/build/public/header.js index d6177db..afcec64 100644 --- a/test/tpl/build/public/header.js +++ b/test/tpl/build/public/header.js @@ -1,2 +1,24 @@ -/*! */ -template("public/header",function(i,e){var t=this,include=function(n,l){l=l||i;var a=t.$include(n,l,e);return void 0!==a?(r+=a,a):void 0},r="";return r+=' ',new String(r)}); \ No newline at end of file +/* */ +template("public/header", function($data, $id) { + var $helpers = this, $line = 0, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + try { + $out += ' '; + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: '\n\n '.split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); +}); \ No newline at end of file diff --git a/test/tpl/build/public/logo.js b/test/tpl/build/public/logo.js index 7e7f637..ebe777c 100644 --- a/test/tpl/build/public/logo.js +++ b/test/tpl/build/public/logo.js @@ -1,2 +1,2 @@ -/*! */ -template("public/logo",'

    \u817e\u8baf\u7f51

    '); \ No newline at end of file +/* */ +template("public/logo", '\n

    \n \n 腾讯网\n \n

    \n'); \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index a8eb38a..20bfd77 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,211 @@ -/*! */ -!function(e){var t=function(e,i){return t[/string|function/.test(typeof i)?"compile":"render"].apply(t,arguments)},i=t.cache={},r=function(e,t){return"string"!=typeof e&&(t=typeof e,"number"===t?e+="":e="function"===t?r(e.call(e)):""),e},n={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return r(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return n[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,t){if(c(e))for(var i=0,r=e.length;r>i;i++)t.call(e,e[i],i,e);else for(i in e)t.call(e,e[i],i)},a=function(e,t){var i=/(\/)[^/]+\1\.\.\1/,r=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),n=r+t;for(n=n.replace(/\/\.\//g,"/");n.match(i);)n=n.replace(i,"/");return n},u=t.helpers={$include:function(e,i,r){var n=a(r,e);return t.render(n,i)},$string:r,$escape:o,$each:l},p=function(t){var i="";for(var r in t)i+="<"+r+">\n"+t[r]+"\n\n";return i&&e.console&&console.error("Template Error\n\n"+i),function(){return"{Template Error}"}};t.render=function(e,i){var r=t.get(e)||p({id:e,name:"Render Error",message:"No Template"});return i?r(i):r},t.compile=function(e,t){var r="function"==typeof t,n=i[e]=function(i){try{return r?new t(i,e)+"":t}catch(n){return p(n)()}};return n.prototype=t.prototype=u,n.toString=function(){return t+""},n},t.get=function(e){return i[e.replace(/^\.\//,"")]},t.helper=function(e,t){u[e]=t},/**/ -t("copyright","(c) 2013"),/**/ -t("index",function(e,t){var i=this,include=function(r,n){n=n||e;var o=i.$include(r,n,t);return void 0!==o?(l+=o,o):void 0},r=i.$escape,n=e.title,o=i.$each,c=e.list,l=(e.$value,e.$index,"");return include("./public/header"),l+='

    ',l+=r(n),l+="

    ",include("./public/footer"),new String(l)}),/**/ -t("public/footer",function(e,t){var i=this,r=e.time,n=i.$escape,include=function(r,n){n=n||e;var c=i.$include(r,n,t);return void 0!==c?(o+=c,c):void 0},o="";return o+='",new String(o)}),/**/ -t("public/header",function(e,t){var i=this,include=function(n,o){o=o||e;var c=i.$include(n,o,t);return void 0!==c?(r+=c,c):void 0},r="";return r+=' ',new String(r)}),/**/ -t("public/logo",'

    \u817e\u8baf\u7f51

    '),"function"==typeof define?define(function(){return t}):"undefined"!=typeof exports?module.exports=t:e.template=t}(this); \ No newline at end of file +/* */ +!function(global) { + var template = function(path, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var toString = function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = toString(value.call(value)); + } else { + value = ""; + } + } + return value; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; + }); + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + var helpers = template.helpers = { + $include: function(path, data, from) { + var id = resolve(from, path); + return template.render(id, data); + }, + $string: toString, + $escape: escapeHTML, + $each: each + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(path, data) { + var fn = template.get(path) || debug({ + id: path, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(path, fn) { + var isFunction = typeof fn === "function"; + var render = cache[path] = function(data) { + try { + return isFunction ? new fn(data, path) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = fn.prototype = helpers; + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + template("copyright", "(c) 2013"); + template("index", function($data, $id) { + var $helpers = this, $line = 0, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + try { + $line = 1; + include("./public/header"); + $out += '

    '; + $line = 4; + $out += $escape(title); + $out += "

    "; + $line = 12; + include("./public/footer"); + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: "{{include './public/header'}}\n\n
    \n

    {{title}}

    \n \n
    \n\n{{include './public/footer'}}".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); + }); + template("public/footer", function($data, $id) { + var $helpers = this, $line = 0, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + try { + $out += '"; + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: "
    \n{{if time}}\n

    {{time}}

    \n{{/if}}\n{{include '../copyright'}}\n
    ".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); + }); + template("public/header", function($data, $id) { + var $helpers = this, $line = 0, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + try { + $out += ' '; + } catch (e) { + throw { + id: $id, + name: "Render Error", + message: e.message, + line: $line, + source: '\n\n '.split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") + }; + } + return new String($out); + }); + template("public/logo", '\n

    \n \n 腾讯网\n \n

    \n'); + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index a6e51e5..a3d830e 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,20 +2,17 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.3-rc1" + "tmodjs": "~0.0.4" }, "tmodjs-config": { "output": "./build", "charset": "utf-8", - "combo": [ - "*" - ], "syntax": "simple", "helpers": null, - "alias": null, - "minify": true, - "async": false, + "escape": true, "engine": false, - "type": "templatejs" + "type": "templatejs", + "combo": true, + "minify": true } } \ No newline at end of file diff --git a/tmod.js b/tmod.js index 09f12ba..d265e2e 100644 --- a/tmod.js +++ b/tmod.js @@ -7,19 +7,22 @@ 'use strict'; var version = require('./package.json').version; -var template = require('./lib/template-AOTcompile.js'); -var uglifyjs = require("./lib/uglify.js"); +var template = require('./lib/AOTcompile.js'); +var uglifyjs = require('./lib/uglify.js'); var fs = require('fs'); var path = require('path'); var events = require('events'); var crypto = require('crypto'); -var vm = require("vm"); +var vm = require('vm'); +var exec = require('child_process').exec; +var os = require('os'); +var engineDirname = path.dirname(require.resolve('art-template')); // 跨平台 path 接口,统一 windows 与 linux 的路径分隔符, // 避免不同平台模板编译后其 id 不一致 -(function () { +;(function () { if (!/\\/.test(path.resolve())) { return path; @@ -68,40 +71,41 @@ module.exports = { // 模板使用的编码。(注意:非 utf-8 编码的模板缺乏测试) charset: 'utf-8', - // 模板合并规则 - // 注意:type 参数的值为 templatejs 才会生效 - combo: ['*'], - - // 定义模板采用哪种语法,可选: + // 定义模板采用哪种语法,内置可选: // simple: 默认语法,易于读写。可参看语法文档 // native: 功能丰富,灵活多变。语法类似微型模板引擎 tmpl + // 或者指定语法解析器路径,参考: + // https://github.com/aui/artTemplate/blob/master/src/template-syntax.js syntax: 'simple', // 自定义辅助方法路径 helpers: null, - // 运行时别名 - alias: null, - - // 是否输出为压缩的格式 - minify: true, - - // 是否内嵌异步加载插件(beta) - // 可以支持 template.async(path, function (render) {}) 方式异步载入模板 - // 注意:type 参数是 templatejs 的时候才生效 - async: false, + // 是否过滤 XSS + // 如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率 + escape: true, // 是否嵌入模板引擎,否则编译为不依赖引擎的纯 js 代码 - // 通常来说,模板不多的情况下,编译为原生的 js 打包后体积更小,因为不必嵌入引擎 - // 当模板很多的时候,内置模板引擎,模板使用字符串存储的方案会更能节省空间 + // 选择嵌入模板引擎后,模板以字符串存储并浏览器中执行编译 engine: false, - // 输出的模块类型(不区分大小写),可选: + // 输出的模块类型,可选: // templatejs: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 // cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) // amd: 支持 RequireJS 等流行加载器 // commonjs: 编译为 NodeJS 模块 - type: 'templatejs' + type: 'templatejs', + + // 运行时别名 + // 仅针对于非 templatejs 的类型模块 + alias: null, + + // 是否合并模板 + // 仅针对于 templatejs 类型的模块 + combo: true, + + // 是否输出为压缩的格式 + minify: true }, @@ -148,8 +152,8 @@ module.exports = { // 项目配置 优先级:1 - for (name in json["tmodjs-config"]) { - config[name] = json["tmodjs-config"][name]; + for (name in json['tmodjs-config']) { + config[name] = json['tmodjs-config'][name]; } @@ -159,18 +163,38 @@ module.exports = { } - json["tmodjs-config"] = config; + json['tmodjs-config'] = config; this['package.json'] = json; + // 忽略大小写 - config['type'] = config['type'].toLowerCase(); - config['syntax'] = config['syntax'].toLowerCase(); + config.type = config.type.toLowerCase(); + + + // 模板合并规则 + // 兼容 0.0.3-rc3 之前的配置 + if (Array.isArray(config.combo) && !config.combo.length) { + config.combo = false; + } else { + config.combo = !!config.combo; + } + + + // 根据生成模块的类型删除不支持的配置字段 + if (config.type === 'templatejs') { + delete config.alias; + } else { + delete config.combo; + } return config; }, - /** 保存用户配置 */ + /** + * 保存用户配置 + * @return {String} 用户配置文件路径 + */ saveUserConfig: function () { var file = this.path + '/package.json'; @@ -181,11 +205,6 @@ module.exports = { var userConfigList = Object.keys(this.defaults); - //if (options.output.indexOf('.') === 0) { - // json.main = options.output + '/' + (options.runtime || RUNTIME) + '.js'; - //} - - // 只保存指定的字段 json[configName] = JSON.parse( JSON.stringify(options, userConfigList) @@ -196,6 +215,8 @@ module.exports = { fs.writeFileSync(file, text, 'utf-8'); + + return file; }, @@ -287,7 +308,7 @@ module.exports = { }; - if (/windows/i.test(require('os').type())) { + if (/windows/i.test(os.type())) { // window 下 nodejs fs.watch 方法尚未稳定 clearTimeout(timer[fullname]); timer[fullname] = setTimeout(function() { @@ -408,7 +429,7 @@ module.exports = { this._fsWrite(debugFile, code); // 启动子进程进行调试,从根本上避免影响当前进程 - var exec = require('child_process').exec; + exec('node ' + debugFile, {timeout: 0}, function (error, stdout, stderr) { var message = error ? error.message : ''; message = message @@ -470,10 +491,10 @@ module.exports = { var that = this; var templates = []; - var ignores = []; - var isDebug = this.options.debug; - var isWrappings = this.options.type !== 'templatejs'; - var runtime = this.options.engine ? '/lib/runtime/full.js' : '/lib/runtime/basic.js'; + var options = this.options; + var isDebug = options.debug; + var isWrappings = options.type !== 'templatejs'; + var runtime = options.engine ? '/lib/runtime/full.js' : '/lib/runtime/basic.js'; var template = fs.readFileSync(__dirname + runtime, 'utf-8'); @@ -494,11 +515,6 @@ module.exports = { .replace(that.path + '/', ''); templates.push(id); - - if (!that.combo || !that.combo.test(id)) { - ignores.push(id); - return; - } var target = that.output + '/' + id + '.js'; @@ -519,7 +535,7 @@ module.exports = { }); }; - if (!isWrappings) { + if (!isWrappings && options.combo) { walk(this.path); } @@ -532,23 +548,16 @@ module.exports = { build: build, templates: combo, debug: debug, - plugins: '', syntax: '', engine: '', helpers: this.helpers }; - // 嵌入异步加载插件 - if (this.options.async) { - data.plugins = fs.readFileSync(__dirname + '/lib/template-async.js', 'utf-8'); - } - - // 嵌入引擎 if (this.options.engine) { - data.engine = fs.readFileSync(__dirname + '/lib/template.js', 'utf-8'); - data.syntax = fs.readFileSync(__dirname + '/lib/template-syntax.js', 'utf-8'); + data.engine = fs.readFileSync(engineDirname + '/template.js', 'utf-8'); + data.syntax = this.syntax; } @@ -569,9 +578,11 @@ module.exports = { this.emit('combo', { output: target, + name: RUNTIME, + fullname: RUNTIME + '.js', + extname: '.js', isDebug: isDebug, templates: templates, - ignores: ignores, build: build }); }, @@ -699,9 +710,7 @@ module.exports = { if (!error && isChange) { var md5 = this._md5(source + JSON.stringify(this['package.json'])); - mod = '/*! ' - + (isDebug ? ' ' : '') - + '*/\n' + mod; + mod = '/* ' + (isDebug ? ' ' : '') + '*/\n' + mod; this._fsWrite(target, mod); uglifyjs[isDebug || !this.options.minify ? 'beautify' : 'minify'](target); @@ -858,10 +867,10 @@ module.exports = { // 加载辅助方法 if (options['helpers']) { - var helpersPath = path.join(this.path, options['helpers']); + var helpersFile = path.join(this.path, options['helpers']); - if (fs.existsSync(helpersPath)) { - this.helpers = fs.readFileSync(helpersPath, 'utf-8'); + if (fs.existsSync(helpersFile)) { + this.helpers = fs.readFileSync(helpersFile, 'utf-8'); vm.runInNewContext(this.helpers, { template: template }); @@ -871,30 +880,21 @@ module.exports = { // 加载模板语法设置 if (options['syntax'] && options['syntax'] !== 'native') { - var syntaxPath = options['syntax'] === 'simple' - ? __dirname + '/lib/template-syntax.js' + var syntaxFile = options['syntax'] === 'simple' + ? engineDirname + '/template-syntax.js' : path.join(this.path, options['syntax']); - if (fs.existsSync(syntaxPath)) { - vm.runInNewContext(fs.readFileSync(syntaxPath, 'utf-8'), { + if (fs.existsSync(syntaxFile)) { + this.syntax = fs.readFileSync(syntaxFile, 'utf-8'); + vm.runInNewContext(this.syntax, { template: template }); } } - // 模板合并规则 - if (options.combo.length) { - var reg = []; - options.combo.forEach(function (combo) { - combo = combo - .replace(/([\$\.])/g, '\\$1') - .replace(/\*/g, '(.*?)'); - - reg.push('^' + combo + '$'); - }); - this.combo = new RegExp(reg.join('|')); - } + // 是否过滤 XSS 的开关 + template.isEscape = options.escape; // 初始化 watch 事件 @@ -919,17 +919,19 @@ module.exports = { // 监听模板加载事件 this.on('load', function (data) { + if (data.isChange) { + this._log('[green]⊙[/green] '); + } else { + this._log('[grey]⊙[/grey] '); + } + this._log(data.id.replace(/^\.\//, '') + '[grey].' + data.extname + '[/grey]'); }); // 监听模板编译事件 this.on('compile', function (data) { - if (data.isChange) { - this._log(' [green]√[/green]\n'); - } else { - this._log(' [grey]√[/grey]\n'); - } + this._log('\n'); }); @@ -943,23 +945,19 @@ module.exports = { // 监听模板合并事件 this.on('combo', function (data) { + var output = options.type === 'templatejs' + ? options.output + '/' + data.fullname + : options.output + '/'; - this._log('\n'); - - var iLength = data.ignores.length; - var tLengtn = data.templates.length; - - if (data.ignores.length) { - this._log('[grey]Ignore(' + iLength + '/' + tLengtn + '): ' - + data.ignores.join(', ') + '[/grey]\n'); - } + output = output.replace(/^\.\//, ''); - this._log('[grey]>>> [/grey][green]' + data.output + '[/green]'); - this._log(this.options.debug ? ' [red][/red]\n' : '\n'); + this._log('[grey]> [/grey]'); + this._log(this.options.debug ? '[inverse][/inverse] ' : ''); + this._log(output); + this._log('\n'); }); - } }; From c451ea00cdaf00646abb3b04352403f758a2ca4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 3 Jan 2014 00:51:53 +0800 Subject: [PATCH 09/87] v0.0.4 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f899d58..127ae3f 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,8 @@ minify: true ### TmodJS v0.0.4 -* 简化``combo``功能,只提供全部合并与不合并两个选项,值为布尔类型(兼容旧的版本的配置,会自动转换成布尔类型)。 +* 增加``escape``配置,如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的默认过滤以提升模板渲染效率 +* 简化``combo``功能,只提供全部合并与不合并两个选项,值为布尔类型(兼容旧的版本的配置,会自动转换成布尔类型) * 取消鸡肋的异步载入插件,同时``async``配置失效 * 为了便于理解,命令行输入的``--output``参数不再相对于模板目录,而是工作目录(配置文件的``output``参数仍保持不变) * 优化控制台日志显示 From 678c207cb69ea1ee9baf32ffb42e8e30590464fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 4 Jan 2014 05:00:05 +0800 Subject: [PATCH 10/87] v0.0.1 --- .gitignore | 4 +- README.md | 30 +- bin/tmod | 2 +- lib/AOTcompile.js | 94 +-- lib/path.js | 34 + lib/runtime/basic.js | 4 +- lib/runtime/full.js | 4 +- lib/stdout.js | 42 ++ lib/uglify.js | 4 +- lib/version.js | 31 + lib/watch.js | 126 ++++ package.json | 2 +- test/test-all/amd/build/copyright.js | 2 +- test/test-all/amd/build/index.js | 2 +- test/test-all/amd/build/public/footer.js | 2 +- test/test-all/amd/build/public/header.js | 2 +- test/test-all/amd/build/public/logo.js | 2 +- test/test-all/amd/build/template.js | 2 +- test/test-all/amd/package.json | 2 +- test/test-all/cmd-alias/build/copyright.js | 2 +- test/test-all/cmd-alias/build/index.js | 2 +- .../test-all/cmd-alias/build/public/footer.js | 2 +- .../test-all/cmd-alias/build/public/header.js | 2 +- test/test-all/cmd-alias/build/public/logo.js | 2 +- test/test-all/cmd-alias/build/template.js | 2 +- test/test-all/cmd-alias/package.json | 2 +- test/test-all/cmd/build/copyright.js | 2 +- test/test-all/cmd/build/index.js | 2 +- test/test-all/cmd/build/public/footer.js | 2 +- test/test-all/cmd/build/public/header.js | 2 +- test/test-all/cmd/build/public/logo.js | 2 +- test/test-all/cmd/build/template.js | 2 +- test/test-all/cmd/package.json | 2 +- test/test-all/combo-off.html | 55 ++ test/test-all/combo-off/build/copyright.js | 2 + test/test-all/combo-off/build/index.js | 23 + .../test-all/combo-off/build/public/footer.js | 19 + .../test-all/combo-off/build/public/header.js | 13 + test/test-all/combo-off/build/public/logo.js | 2 + test/test-all/combo-off/build/template.js | 115 +++ test/test-all/combo-off/copyright.html | 1 + test/test-all/combo-off/index.html | 12 + test/test-all/combo-off/package.json | 18 + test/test-all/combo-off/public/footer.html | 6 + test/test-all/combo-off/public/header.html | 11 + test/test-all/combo-off/public/logo.html | 7 + test/tpl/build/copyright.js | 2 - test/tpl/build/index.js | 40 - test/tpl/build/public/footer.js | 33 - test/tpl/build/public/header.js | 24 - test/tpl/build/public/logo.js | 2 - test/tpl/build/template.js | 218 +----- test/tpl/package.json | 4 +- tmod.js | 704 +++++++++--------- 54 files changed, 975 insertions(+), 755 deletions(-) create mode 100644 lib/path.js create mode 100644 lib/stdout.js create mode 100644 lib/version.js create mode 100644 lib/watch.js create mode 100644 test/test-all/combo-off.html create mode 100644 test/test-all/combo-off/build/copyright.js create mode 100644 test/test-all/combo-off/build/index.js create mode 100644 test/test-all/combo-off/build/public/footer.js create mode 100644 test/test-all/combo-off/build/public/header.js create mode 100644 test/test-all/combo-off/build/public/logo.js create mode 100644 test/test-all/combo-off/build/template.js create mode 100644 test/test-all/combo-off/copyright.html create mode 100644 test/test-all/combo-off/index.html create mode 100644 test/test-all/combo-off/package.json create mode 100644 test/test-all/combo-off/public/footer.html create mode 100644 test/test-all/combo-off/public/header.html create mode 100644 test/test-all/combo-off/public/logo.html delete mode 100644 test/tpl/build/copyright.js delete mode 100644 test/tpl/build/index.js delete mode 100644 test/tpl/build/public/footer.js delete mode 100644 test/tpl/build/public/header.js delete mode 100644 test/tpl/build/public/logo.js diff --git a/.gitignore b/.gitignore index 91dfed8..9fc71e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +.cache +.debug.js .DS_Store -node_modules \ No newline at end of file +node_modules diff --git a/README.md b/README.md index 127ae3f..5c8433d 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ $ tmod ./tpl -w -output ./build * ``-d``或``--debug``输出调试版本 * ``--charset value``定义模板编码,默认``utf-8`` * ``--output value``定义输出目录,默认``./build`` -* ``--type value``定义输出模块格式,默认``templatejs``,可选``cmd``、``amd``、``commonjs`` +* ``--type value``定义输出模块格式,默认``default``,可选``cmd``、``amd``、``commonjs`` * ``--version``显示版本号 * ``--help``显示帮助信息 @@ -99,7 +99,7 @@ $ tmod ./tpl -w -output ./build #### 1. 默认类型格式的使用方式 -模板的``type``为默认值(``type:templatejs``)的时候,TmodJS 默认将整个目录的模板压缩合并到一个名为 template.js 的脚本中,通常情况下你只需要在页面中引入它就好(其余的文件可暂时忽略)。例如: +模板的``type``为默认值(``type:default``)的时候,TmodJS 默认将整个目录的模板压缩合并到一个名为 template.js 的脚本中,通常情况下你只需要在页面中引入它就好(其余的文件可暂时忽略)。例如: @@ -201,18 +201,18 @@ escape: true, engine: false, // 输出的模块类型,可选: -// templatejs: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 +// default: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 // cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) // amd: 支持 RequireJS 等流行加载器 // commonjs: 编译为 NodeJS 模块 -type: 'templatejs', +type: 'default', // 运行时别名 -// 仅针对于非 templatejs 的类型模块 +// 仅针对于非 default 的类型模块 alias: null, // 是否合并模板 -// 仅针对于 templatejs 类型的模块 +// 仅针对于 default 类型的模块 combo: true, // 是否输出为压缩的格式 @@ -267,26 +267,34 @@ minify: true ## 更新日志 -### TmodJS v0.0.4 +### v0.1.0 + +* 减少对磁盘的读写,优化性能 +* 增加自动递增的模板版本号,控制台可显示模板被修改的次数 +* 优化默认设置下输出的文件数量,仅保留``template.js``,临时文件使用隐藏的``.cache``目录存放 +* 自动清理``.debug.js``文件 +* 对非规范的``include``语句模板在编译过程给予提示 + +### v0.0.4 * 增加``escape``配置,如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的默认过滤以提升模板渲染效率 -* 简化``combo``功能,只提供全部合并与不合并两个选项,值为布尔类型(兼容旧的版本的配置,会自动转换成布尔类型) +* 简化``combo``功能,default只提供全部合并与不合并两个选项,值为布尔类型(兼容旧的版本的配置,会自动转换成布尔类型) * 取消鸡肋的异步载入插件,同时``async``配置失效 * 为了便于理解,命令行输入的``--output``参数不再相对于模板目录,而是工作目录(配置文件的``output``参数仍保持不变) * 优化控制台日志显示 -### TmodJS v0.0.3 +### v0.0.3 * 修复``combo``配置不能为空数组的 BUG * 支持页面内嵌动态编译与预编译两种方案共存(请设置``engine:true``,并在页面中中引入 TmodJS 输出的 template.js。如果想让 template.js 不内置合并的模板,可以设置``combo:[]``) * 运行时性能优化 * 增加``alias``配置字段,在 AMD 与 CMD 模式下可以指定运行时依赖 ID -### TmodJS v0.0.2 +### v0.0.2 修复极其特殊情况下 TmodJS 无法为 AMD/CMD 模块正确声明依赖的问题[#14](https://github.com/aui/tmodjs/issues/14) -### TmodJS v0.0.1 +### v0.0.1 这是一个革命性的版本!同时项目更名为 **TmodJS**,内部版本号收归到 0.0.1,这是一个新的开始,未来将稳步更新。 diff --git a/bin/tmod b/bin/tmod index 3a7eb01..ddc71b1 100644 --- a/bin/tmod +++ b/bin/tmod @@ -169,7 +169,7 @@ userConfig = TmodJS.saveUserConfig(); if (isEditConfig) { - process.stdout.write('open: ' + userConfig + '\n'); + process.stdout.write('Open: ' + userConfig + '\n'); exec( (/windows/i.test(os.type()) ? 'start' : 'open') diff --git a/lib/AOTcompile.js b/lib/AOTcompile.js index f85b3c4..a193e11 100644 --- a/lib/AOTcompile.js +++ b/lib/AOTcompile.js @@ -4,8 +4,9 @@ * Released under the MIT, BSD, and GPL Licenses */ -var template = require('art-template'); +'use strict'; +var template = require('art-template'); var SLASH_RE = /\\\\/g; var DOT_RE = /\/\.\//g; @@ -41,16 +42,32 @@ var testTemplateSyntax = function (source) { // 模板 ID 规范检查 -var testId = function (id, fromID) { - if (!/^\./.test(id) || EXTNAME_RE.test(id)) { +// 保证 include 语法引用的是相对路径 +var testUri = function (uri, fromUri, source) { + if (!/^\./.test(uri) || EXTNAME_RE.test(uri)) { + + var line, source; + + // 如果只出现一次这个字符串,很容易确认模板错误行 + if (source.split(uri).length === 2) { + source.split(/\n/).forEach(function (code, index) { + if (code.indexOf(uri) !== -1) { + line = index + 1; + source = code.trim(); + } + }); + } + var error = { name: 'Syntax Error', - message: id + '\n' - + 'Template must be a relative path, and can not have a suffix.' + type: 1, + line: line, + source: source, + message: 'Template must be a relative path, and can not have a suffix.' }; - if (fromID) { - error.id = fromID; + if (fromUri) { + error.id = fromUri; } throw error; @@ -76,19 +93,19 @@ var parseDependencies = function (code) { }; -// 获取上一层 ID -var dirname = function (id) { - return id.replace(DIRNAME_RE, ''); +// 获取上一层 uri +var dirname = function (uri) { + return uri.replace(DIRNAME_RE, ''); }; -// 分解为标准化 ID -var resolve = function (id) { - id = id.replace(DOT_RE, '/'); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, '/'); +// 分解为标准化 uri +var resolve = function (uri) { + uri = uri.replace(DOT_RE, '/'); + while (uri.match(DOUBLE_DOT_RE)) { + uri = uri.replace(DOUBLE_DOT_RE, '/'); } - return id; + return uri; }; @@ -114,14 +131,14 @@ var compressHTML = function (code) { /** * 模板预编译器,根据设置生成不同格式的 javascript 模块 - * @param {String} 模板ID,例如 ./home/list - * 要求:必须是 . 的相对路径开头,且末尾不能有后缀名 + * @param {String} 注册的模板 ID,例如 home/list + * 要求:不能是 . 活者 / 开头的名称,且末尾不能有后缀名 * @param {String} 模板源代码 - * @param {Object} 可选项 + * @param {Object} 选项 */ template.AOTcompile = function (id, source, options) { - testId(id); + var uri = './' + id; // 是否嵌入完整模板引擎,嵌入后将把模板保存为字符串 var isEngine = options.engine; @@ -138,11 +155,10 @@ template.AOTcompile = function (id, source, options) { var RUNTIME = 'template'; var code = ''; - var render = compile(id, source, isDebug); + var render = compile(uri, source, isDebug); var requires = parseDependencies(render); var isLogic = testTemplateSyntax(source); - var dir = dirname(id); - var tid = id.replace(/^\.\//, ''); + var dir = dirname(uri); var isHTML = isEngine || !isLogic; @@ -173,23 +189,15 @@ template.AOTcompile = function (id, source, options) { var getRequireCode = function () { var requiresCode = []; - requires.forEach(function (id) { - requiresCode.push("require('" + id + "');"); + requires.forEach(function (uri) { + requiresCode.push("require('" + uri + "');"); }); return requiresCode.join('\n'); }; - switch (type || '') { - - // TemplateJS 模块格式 - case '': - case 'templatejs': - - code = "template('" + tid + "'," + code + ");"; - break; - + switch (type) { // RequireJS / SeaJS 兼容模块格式 case 'cmd': @@ -198,7 +206,7 @@ template.AOTcompile = function (id, source, options) { = "define(function(require){" + getRequireCode() + "return require('" + getRuntime() + "')" - + "('" + tid + "', " + code + ");" + + "('" + id + "', " + code + ");" + "});"; break; @@ -210,7 +218,7 @@ template.AOTcompile = function (id, source, options) { = "define(" + "['" + getRuntime() + "','" + requires.join("','") + "']," + "function(template){" - + "return template('" + tid + "', " + code + ");" + + "return template('" + id + "', " + code + ");" + "});"; break; @@ -221,17 +229,13 @@ template.AOTcompile = function (id, source, options) { code = "var template=require('" + getRuntime() + "');" + getRequireCode() - + "module.exports=template('" + tid + "'," + code + ");"; + + "module.exports=template('" + id + "'," + code + ");"; break; default: - throw { - id: id, - name: 'Type Error', - message: 'Unsupported type: ' + type - }; + code = "template('" + id + "'," + code + ");"; } @@ -242,9 +246,9 @@ template.AOTcompile = function (id, source, options) { code: code, // 依赖的子模板 - requires: requires.map(function (subId) { - testId(subId, id); - return resolve(dir + subId); + requires: requires.map(function (subUir) { + testUri(subUir, uri, source); + return resolve(dir + subUir); }) }; diff --git a/lib/path.js b/lib/path.js new file mode 100644 index 0000000..f168e18 --- /dev/null +++ b/lib/path.js @@ -0,0 +1,34 @@ +/*! + * NodeJS path 跨平台支持(让 windows 路径分隔与 linux 保持一致,统一为:“/”) + * https://github.com/aui/tmodjs + * Released under the MIT, BSD, and GPL Licenses + */ + +'use strict'; + +var path = require('path'); + +if (!/\\/.test(path.resolve())) { + module.exports = path; +} else { + var oldPath = path; + var newPath = Object.create(oldPath); + var proxy = function (name) { + return function () { + var value = oldPath[name].apply(oldPath, arguments); + if (typeof value === 'string') { + value = value.split(oldPath.sep).join('/'); + } + return value; + } + }; + + for (var name in newPath) { + if (typeof oldPath[name] === 'function') { + newPath[name] = proxy(name); + } + } + + module.exports = newPath; +} + diff --git a/lib/runtime/basic.js b/lib/runtime/basic.js index 8a42a0d..578a12e 100644 --- a/lib/runtime/basic.js +++ b/lib/runtime/basic.js @@ -1,7 +1,7 @@ -/* '> '<:debug:>'*/ - !function (global) { + 'use strict'; + var template = function (path, content) { return template[ /string|function/.test(typeof content) ? 'compile' : 'render' diff --git a/lib/runtime/full.js b/lib/runtime/full.js index 9105fa1..44a88cc 100644 --- a/lib/runtime/full.js +++ b/lib/runtime/full.js @@ -1,7 +1,7 @@ -/* '> '<:debug:>'*/ - '<:engine:>' !function (global, template) { + + 'use strict'; var get = template.get; var helpers = template.helpers; diff --git a/lib/stdout.js b/lib/stdout.js new file mode 100644 index 0000000..f7b10ad --- /dev/null +++ b/lib/stdout.js @@ -0,0 +1,42 @@ +/*! + * 在控制台支持多彩日志显示 + * https://github.com/aui/tmodjs + * Released under the MIT, BSD, and GPL Licenses + */ + +'use strict'; + +var styles = { + // styles + 'bold' : ['\x1B[1m', '\x1B[22m'], + 'italic' : ['\x1B[3m', '\x1B[23m'], + 'underline' : ['\x1B[4m', '\x1B[24m'], + 'inverse' : ['\x1B[7m', '\x1B[27m'], + // colors + 'white' : ['\x1B[37m', '\x1B[39m'], + 'grey' : ['\x1B[90m', '\x1B[39m'], + 'black' : ['\x1B[30m', '\x1B[39m'], + 'blue' : ['\x1B[34m', '\x1B[39m'], + 'cyan' : ['\x1B[36m', '\x1B[39m'], + 'green' : ['\x1B[32m', '\x1B[39m'], + 'magenta' : ['\x1B[35m', '\x1B[39m'], + 'red' : ['\x1B[31m', '\x1B[39m'], + 'yellow' : ['\x1B[33m', '\x1B[39m'] +}; + + +styles['b'] = styles['bold']; +styles['i'] = styles['italic']; +styles['u'] = styles['underline']; + +module.exports = function (message) { + message = message.replace(/\[([^\]]*?)\]/igm, function ($1, $2) { + return $2.indexOf('/') === 0 + ? styles[$2.slice(1)][1] + : styles[$2][0]; + }); + + + process.stdout.write(message); +}; + diff --git a/lib/uglify.js b/lib/uglify.js index f96c6a4..15cd1be 100644 --- a/lib/uglify.js +++ b/lib/uglify.js @@ -29,7 +29,7 @@ exports.beautify = function (file) { version: false, output: file, o: file, - comments: '//', + comments: '/TMODJS\:/', screw_ie8: false, export_all: false }; @@ -63,7 +63,7 @@ exports.minify = function (file) { m: true, reserved: 'include,require', r: 'include,require', - comments: '/|^$/', + comments: '/TMODJS\:|^$/', compress: 'warnings=fasle', c: 'warnings=false', beautify: 'beautify=false,ascii-only=true', diff --git a/lib/version.js b/lib/version.js new file mode 100644 index 0000000..c2a00bb --- /dev/null +++ b/lib/version.js @@ -0,0 +1,31 @@ + /*! + * 新旧版本号对比 + * https://github.com/aui/tmodjs + * Released under the MIT, BSD, and GPL Licenses + */ + +// 模板工程依赖的的 TomdJS 版本号与当前版本对比 + +// a < b +module.exports = function (a, b) { + a = a.replace('~', '').split('.'); + b = b.replace('~', '').split('.'); + var ret = true; + + for (var i = 0, an, bn; i < a.length; i ++) { + bn = Number(b[i]); + an = Number(a[i]); + + if (bn > an) { + ret = true; + break; + } + + if (an > bn) { + ret = false; + break; + } + } + + return ret; +} \ No newline at end of file diff --git a/lib/watch.js b/lib/watch.js new file mode 100644 index 0000000..ff1f387 --- /dev/null +++ b/lib/watch.js @@ -0,0 +1,126 @@ +/*! + * fs watch 子目录的监听与跨平台支持(让 windows 的行为与 linux 保持一致) + * https://github.com/aui/tmodjs + * Released under the MIT, BSD, and GPL Licenses + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var os = require('os'); + + +var watchList = {}; +var timer = {}; + + +var walk = function (dir, callback, filter) { + fs.readdirSync(dir).forEach(function (item) { + var fullname = dir + '/' + item; + if (fs.statSync(fullname).isDirectory()){ + watch(fullname, callback, filter); + walk(fullname, callback, filter); + } + }); +}; + + +var watch = function (parent, callback, filter) { + + var target = path.basename(parent); + + if (!filter(target)){ + return; + } + + if (watchList[parent]) { + watchList[parent].close(); + } + + watchList[parent] = fs.watch(parent, function (event, filename) { + + var fullname = parent + '/' + filename; + var type; + var fstype; + + if (!filter(filename)) { + return; + } + + // 检查文件、目录是否存在 + if (!fs.existsSync(fullname)) { + + // 如果目录被删除则关闭监视器 + if (watchList[fullname]) { + fstype = 'directory'; + watchList[fullname].close(); + delete watchList[fullname]; + } else { + fstype = 'file'; + } + + type = 'delete'; + + } else { + + // 文件 + if (fs.statSync(fullname).isFile()) { + + fstype = 'file'; + type = event == 'rename' ? 'create' : 'updated' + + // 文件夹 + } else if (event === 'rename') { + + fstype = 'directory'; + type = 'create' + watch(fullname, callback, filter); + walk(fullname, callback, filter); + } + + } + + var eventData = { + type: type, + target: filename, + parent: parent, + fstype: fstype + }; + + + if (/windows/i.test(os.type())) { + // window 下 nodejs fs.watch 方法尚未稳定 + clearTimeout(timer[fullname]); + timer[fullname] = setTimeout(function() { + callback(eventData); + }, 16); + + } else { + callback(eventData); + } + + + }); + +}; + + +/** + * @param {String} 要监听的目录 + * @param {Function} 文件、目录改变后的回调函数 + * @param {Function} 过滤器(可选) + */ +module.exports = function (dir, callback, filter) { + + // 排除“.”、“_”开头或者非英文命名的目录 + var FILTER_RE = /[^\w\.\-$]/; + filter = filter || function (name) { + return !FILTER_RE.test(name); + }; + + //if (filter(dir)) { + watch(dir, callback, filter); + walk(dir, callback, filter); + //} +}; \ No newline at end of file diff --git a/package.json b/package.json index 886019d..008f638 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "0.0.4-rc1", + "version": "0.1.0", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/test/test-all/amd/build/copyright.js b/test/test-all/amd/build/copyright.js index eca4d02..b0b5799 100644 --- a/test/test-all/amd/build/copyright.js +++ b/test/test-all/amd/build/copyright.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"1e854fa382ff15601ae476b5e4bda572"}*/ define([ "./template", "" ], function(template) { return template("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/amd/build/index.js b/test/test-all/amd/build/index.js index 02f1531..cc7adcb 100644 --- a/test/test-all/amd/build/index.js +++ b/test/test-all/amd/build/index.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"f911f5d9484d6d439bd92b74059a3dc3"}*/ define([ "./template", "./public/header", "./public/footer" ], function(template) { return template("index", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/amd/build/public/footer.js b/test/test-all/amd/build/public/footer.js index 9b31718..5e28caa 100644 --- a/test/test-all/amd/build/public/footer.js +++ b/test/test-all/amd/build/public/footer.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"6599b97b0326fe559d5c1fbef543716a"}*/ define([ "../template", "../copyright" ], function(template) { return template("public/footer", function($data, $id) { var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { diff --git a/test/test-all/amd/build/public/header.js b/test/test-all/amd/build/public/header.js index 6f60cdf..5e128c5 100644 --- a/test/test-all/amd/build/public/header.js +++ b/test/test-all/amd/build/public/header.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"69b5fd873be0e48445f3c880438bdb9e"}*/ define([ "../template", "./logo" ], function(template) { return template("public/header", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/amd/build/public/logo.js b/test/test-all/amd/build/public/logo.js index aa05d35..487162b 100644 --- a/test/test-all/amd/build/public/logo.js +++ b/test/test-all/amd/build/public/logo.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"408d70188cd7876ae67f9bcd8198c05f"}*/ define([ "../template", "" ], function(template) { return template("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/amd/build/template.js b/test/test-all/amd/build/template.js index 0eb1eea..92ea0d9 100644 --- a/test/test-all/amd/build/template.js +++ b/test/test-all/amd/build/template.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"build":1388773675300}*/ !function(global) { var template = function(path, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); diff --git a/test/test-all/amd/package.json b/test/test-all/amd/package.json index 68c72aa..be1938e 100644 --- a/test/test-all/amd/package.json +++ b/test/test-all/amd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.0.4-rc2" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/cmd-alias/build/copyright.js b/test/test-all/cmd-alias/build/copyright.js index d3ced82..423c7c8 100644 --- a/test/test-all/cmd-alias/build/copyright.js +++ b/test/test-all/cmd-alias/build/copyright.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"e56a7a30ce6f8c09306ffd08a238e51b"}*/ define(function(require) { return require("template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/index.js b/test/test-all/cmd-alias/build/index.js index 2368ccc..883dd74 100644 --- a/test/test-all/cmd-alias/build/index.js +++ b/test/test-all/cmd-alias/build/index.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"c892cf70a4f8c1de7a8cc9f93c735afd"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/cmd-alias/build/public/footer.js b/test/test-all/cmd-alias/build/public/footer.js index 2b3591d..be93805 100644 --- a/test/test-all/cmd-alias/build/public/footer.js +++ b/test/test-all/cmd-alias/build/public/footer.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"49e539323ef82ca1d715f06311ab1d49"}*/ define(function(require) { require("../copyright"); return require("template")("public/footer", function($data, $id) { diff --git a/test/test-all/cmd-alias/build/public/header.js b/test/test-all/cmd-alias/build/public/header.js index 95cfcbe..9135197 100644 --- a/test/test-all/cmd-alias/build/public/header.js +++ b/test/test-all/cmd-alias/build/public/header.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"cab5e6f388319725086c6dc61243125a"}*/ define(function(require) { require("./logo"); return require("template")("public/header", function($data, $id) { diff --git a/test/test-all/cmd-alias/build/public/logo.js b/test/test-all/cmd-alias/build/public/logo.js index eaee786..8987ea3 100644 --- a/test/test-all/cmd-alias/build/public/logo.js +++ b/test/test-all/cmd-alias/build/public/logo.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"79e7371ff8af641acec8e0175c9a0c6a"}*/ define(function(require) { return require("template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/template.js b/test/test-all/cmd-alias/build/template.js index b62f87b..d2214d3 100644 --- a/test/test-all/cmd-alias/build/template.js +++ b/test/test-all/cmd-alias/build/template.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"build":1388773711193}*/ !function(global) { var template = function(path, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); diff --git a/test/test-all/cmd-alias/package.json b/test/test-all/cmd-alias/package.json index 74c9c28..e3db6e3 100644 --- a/test/test-all/cmd-alias/package.json +++ b/test/test-all/cmd-alias/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.0.4-rc2" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/cmd/build/copyright.js b/test/test-all/cmd/build/copyright.js index 5f8dd43..0b673b6 100644 --- a/test/test-all/cmd/build/copyright.js +++ b/test/test-all/cmd/build/copyright.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"5ec159b8fdc1336b504bb8b4b98c95aa"}*/ define(function(require) { return require("./template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/cmd/build/index.js b/test/test-all/cmd/build/index.js index f7c4ab1..332a31b 100644 --- a/test/test-all/cmd/build/index.js +++ b/test/test-all/cmd/build/index.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"80d169ac84706a2be199526f5aaf7fba"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/cmd/build/public/footer.js b/test/test-all/cmd/build/public/footer.js index eb92ee9..8ca480d 100644 --- a/test/test-all/cmd/build/public/footer.js +++ b/test/test-all/cmd/build/public/footer.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"6806db482b03336ae6e90e735c9acb58"}*/ define(function(require) { require("../copyright"); return require("../template")("public/footer", function($data, $id) { diff --git a/test/test-all/cmd/build/public/header.js b/test/test-all/cmd/build/public/header.js index 8be8a2a..94f5a4d 100644 --- a/test/test-all/cmd/build/public/header.js +++ b/test/test-all/cmd/build/public/header.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"13a6961d3b34f888c13557b3a8cead25"}*/ define(function(require) { require("./logo"); return require("../template")("public/header", function($data, $id) { diff --git a/test/test-all/cmd/build/public/logo.js b/test/test-all/cmd/build/public/logo.js index 7968e08..761d813 100644 --- a/test/test-all/cmd/build/public/logo.js +++ b/test/test-all/cmd/build/public/logo.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"6552b917201e4947f819e3d0b2fcf207"}*/ define(function(require) { return require("../template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/cmd/build/template.js b/test/test-all/cmd/build/template.js index 4e94211..acc7cbf 100644 --- a/test/test-all/cmd/build/template.js +++ b/test/test-all/cmd/build/template.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"build":1388773704118}*/ !function(global) { var template = function(path, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); diff --git a/test/test-all/cmd/package.json b/test/test-all/cmd/package.json index 9785241..8495ebf 100644 --- a/test/test-all/cmd/package.json +++ b/test/test-all/cmd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.0.4-rc2" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/combo-off.html b/test/test-all/combo-off.html new file mode 100644 index 0000000..1e9cb70 --- /dev/null +++ b/test/test-all/combo-off.html @@ -0,0 +1,55 @@ + + + + +TemplateJS - 调用模板演示 + + + + +
    loading..
    + + + + + + + + + + + + + + diff --git a/test/test-all/combo-off/build/copyright.js b/test/test-all/combo-off/build/copyright.js new file mode 100644 index 0000000..a2bd907 --- /dev/null +++ b/test/test-all/combo-off/build/copyright.js @@ -0,0 +1,2 @@ +/*TMODJS:{"version":1,"md5":"4487a23c46554f983d0d65b853f2d425"}*/ +template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/combo-off/build/index.js b/test/test-all/combo-off/build/index.js new file mode 100644 index 0000000..205fbe0 --- /dev/null +++ b/test/test-all/combo-off/build/index.js @@ -0,0 +1,23 @@ +/*TMODJS:{"version":1,"md5":"b0a98aa9248087cee2026cc4239e4f41"}*/ +template("index", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; + include("./public/header"); + $out += '

    '; + $out += $escape(title); + $out += "

    "; + include("./public/footer"); + return new String($out); +}); \ No newline at end of file diff --git a/test/test-all/combo-off/build/public/footer.js b/test/test-all/combo-off/build/public/footer.js new file mode 100644 index 0000000..f6e3825 --- /dev/null +++ b/test/test-all/combo-off/build/public/footer.js @@ -0,0 +1,19 @@ +/*TMODJS:{"version":1,"md5":"56f427d7513a75362140b92ae977ce11"}*/ +template("public/footer", function($data, $id) { + var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + $out += '"; + return new String($out); +}); \ No newline at end of file diff --git a/test/test-all/combo-off/build/public/header.js b/test/test-all/combo-off/build/public/header.js new file mode 100644 index 0000000..3c6de11 --- /dev/null +++ b/test/test-all/combo-off/build/public/header.js @@ -0,0 +1,13 @@ +/*TMODJS:{"version":1,"md5":"1a90867e48c355a45f2d512c6e14dfb4"}*/ +template("public/header", function($data, $id) { + var $helpers = this, include = function(id, data) { + data = data || $data; + var $text = $helpers.$include(id, data, $id); + $out += $text; + return $text; + }, $out = ""; + $out += ' '; + return new String($out); +}); \ No newline at end of file diff --git a/test/test-all/combo-off/build/public/logo.js b/test/test-all/combo-off/build/public/logo.js new file mode 100644 index 0000000..72833e9 --- /dev/null +++ b/test/test-all/combo-off/build/public/logo.js @@ -0,0 +1,2 @@ +/*TMODJS:{"version":1,"md5":"b76a88eb7083ef9c0a8c9466130e1b96"}*/ +template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/combo-off/build/template.js b/test/test-all/combo-off/build/template.js new file mode 100644 index 0000000..9dc17a5 --- /dev/null +++ b/test/test-all/combo-off/build/template.js @@ -0,0 +1,115 @@ +/*TMODJS:{"build":1388773720011}*/ +!function(global) { + var template = function(path, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var toString = function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = toString(value.call(value)); + } else { + value = ""; + } + } + return value; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; + }); + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + var helpers = template.helpers = { + $include: function(path, data, from) { + var id = resolve(from, path); + return template.render(id, data); + }, + $string: toString, + $escape: escapeHTML, + $each: each + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(path, data) { + var fn = template.get(path) || debug({ + id: path, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(path, fn) { + var isFunction = typeof fn === "function"; + var render = cache[path] = function(data) { + try { + return isFunction ? new fn(data, path) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = fn.prototype = helpers; + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/test-all/combo-off/copyright.html b/test/test-all/combo-off/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/combo-off/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/combo-off/index.html b/test/test-all/combo-off/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/combo-off/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
    +

    {{title}}

    + +
    + +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/combo-off/package.json b/test/test-all/combo-off/package.json new file mode 100644 index 0000000..a0ae45a --- /dev/null +++ b/test/test-all/combo-off/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.0.4-rc2" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": null, + "escape": true, + "engine": false, + "type": "templatejs", + "combo": false, + "minify": false + } +} \ No newline at end of file diff --git a/test/test-all/combo-off/public/footer.html b/test/test-all/combo-off/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/combo-off/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/combo-off/public/header.html b/test/test-all/combo-off/public/header.html new file mode 100644 index 0000000..d93e780 --- /dev/null +++ b/test/test-all/combo-off/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/combo-off/public/logo.html b/test/test-all/combo-off/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/combo-off/public/logo.html @@ -0,0 +1,7 @@ + +

    + + 腾讯网 + +

    + \ No newline at end of file diff --git a/test/tpl/build/copyright.js b/test/tpl/build/copyright.js deleted file mode 100644 index 73357c0..0000000 --- a/test/tpl/build/copyright.js +++ /dev/null @@ -1,2 +0,0 @@ -/* */ -template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/tpl/build/index.js b/test/tpl/build/index.js deleted file mode 100644 index dabd907..0000000 --- a/test/tpl/build/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/* */ -template("index", function($data, $id) { - var $helpers = this, $line = 0, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; - try { - $line = 1; - include("./public/header"); - $out += '

    '; - $line = 4; - $out += $escape(title); - $out += "

    "; - $line = 12; - include("./public/footer"); - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: "{{include './public/header'}}\n\n
    \n

    {{title}}

    \n \n
    \n\n{{include './public/footer'}}".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); -}); \ No newline at end of file diff --git a/test/tpl/build/public/footer.js b/test/tpl/build/public/footer.js deleted file mode 100644 index 87fe42d..0000000 --- a/test/tpl/build/public/footer.js +++ /dev/null @@ -1,33 +0,0 @@ -/* */ -template("public/footer", function($data, $id) { - var $helpers = this, $line = 0, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $out = ""; - try { - $out += '"; - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: "
    \n{{if time}}\n

    {{time}}

    \n{{/if}}\n{{include '../copyright'}}\n
    ".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); -}); \ No newline at end of file diff --git a/test/tpl/build/public/header.js b/test/tpl/build/public/header.js deleted file mode 100644 index afcec64..0000000 --- a/test/tpl/build/public/header.js +++ /dev/null @@ -1,24 +0,0 @@ -/* */ -template("public/header", function($data, $id) { - var $helpers = this, $line = 0, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $out = ""; - try { - $out += ' '; - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: '\n\n '.split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); -}); \ No newline at end of file diff --git a/test/tpl/build/public/logo.js b/test/tpl/build/public/logo.js deleted file mode 100644 index ebe777c..0000000 --- a/test/tpl/build/public/logo.js +++ /dev/null @@ -1,2 +0,0 @@ -/* */ -template("public/logo", '\n

    \n \n 腾讯网\n \n

    \n'); \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 20bfd77..c73310c 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,211 +1,7 @@ -/* */ -!function(global) { - var template = function(path, content) { - return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); - }; - var cache = template.cache = {}; - var toString = function(value, type) { - if (typeof value !== "string") { - type = typeof value; - if (type === "number") { - value += ""; - } else if (type === "function") { - value = toString(value.call(value)); - } else { - value = ""; - } - } - return value; - }; - var escapeMap = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - var escapeHTML = function(content) { - return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return escapeMap[s]; - }); - }; - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; - var each = function(data, callback) { - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } - } else { - for (i in data) { - callback.call(data, data[i], i); - } - } - }; - var resolve = function(from, to) { - var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); - var id = dirname + to; - id = id.replace(/\/\.\//g, "/"); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, "/"); - } - return id; - }; - var helpers = template.helpers = { - $include: function(path, data, from) { - var id = resolve(from, path); - return template.render(id, data); - }, - $string: toString, - $escape: escapeHTML, - $each: each - }; - var debug = function(e) { - var message = ""; - for (var name in e) { - message += "<" + name + ">\n" + e[name] + "\n\n"; - } - if (message && global.console) { - console.error("Template Error\n\n" + message); - } - return function() { - return "{Template Error}"; - }; - }; - template.render = function(path, data) { - var fn = template.get(path) || debug({ - id: path, - name: "Render Error", - message: "No Template" - }); - return data ? fn(data) : fn; - }; - template.compile = function(path, fn) { - var isFunction = typeof fn === "function"; - var render = cache[path] = function(data) { - try { - return isFunction ? new fn(data, path) + "" : fn; - } catch (e) { - return debug(e)(); - } - }; - render.prototype = fn.prototype = helpers; - render.toString = function() { - return fn + ""; - }; - return render; - }; - template.get = function(id) { - return cache[id.replace(/^\.\//, "")]; - }; - template.helper = function(name, helper) { - helpers[name] = helper; - }; - template("copyright", "(c) 2013"); - template("index", function($data, $id) { - var $helpers = this, $line = 0, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $escape = $helpers.$escape, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $out = ""; - try { - $line = 1; - include("./public/header"); - $out += '

    '; - $line = 4; - $out += $escape(title); - $out += "

    "; - $line = 12; - include("./public/footer"); - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: "{{include './public/header'}}\n\n
    \n

    {{title}}

    \n \n
    \n\n{{include './public/footer'}}".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); - }); - template("public/footer", function($data, $id) { - var $helpers = this, $line = 0, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $out = ""; - try { - $out += '"; - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: "
    \n{{if time}}\n

    {{time}}

    \n{{/if}}\n{{include '../copyright'}}\n
    ".split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); - }); - template("public/header", function($data, $id) { - var $helpers = this, $line = 0, include = function(id, data) { - data = data || $data; - var $text = $helpers.$include(id, data, $id); - $out += $text; - return $text; - }, $out = ""; - try { - $out += ' '; - } catch (e) { - throw { - id: $id, - name: "Render Error", - message: e.message, - line: $line, - source: '\n\n '.split(/\n/)[$line - 1].replace(/^[\s\t]+/, "") - }; - } - return new String($out); - }); - template("public/logo", '\n

    \n \n 腾讯网\n \n

    \n'); - if (typeof define === "function") { - define(function() { - return template; - }); - } else if (typeof exports !== "undefined") { - module.exports = template; - } else { - global.template = template; - } -}(this); \ No newline at end of file +/*TMODJS:{"build":1388782270806}*/ +!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ +r("copyright","(c) 2013"),/**/ +r("index",function(e,r){var n=this,include=function(t,i){i=i||e;var o=n.$include(t,i,r);return u+=o,o},t=n.$escape,i=e.title,o=n.$each,c=e.list,u=(e.$value,e.$index,"");return include("./public/header"),u+='

    ',u+=t(i),u+="

    ",include("./public/footer"),new String(u)}),/**/ +r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,include=function(t,i){i=i||e;var c=n.$include(t,i,r);return o+=c,c},o="";return o+='",new String(o)}),/**/ +r("public/header",function(e,r){var n=this,include=function(i,o){o=o||e;var c=n.$include(i,o,r);return t+=c,c},t="";return t+=' ',new String(t)}),/**/ +r("public/logo",'

    \u817e\u8baf\u7f51

    '),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index a3d830e..f35c19b 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": null, "escape": true, "engine": false, - "type": "templatejs", + "type": "default", "combo": true, "minify": true } diff --git a/tmod.js b/tmod.js index d265e2e..52fe380 100644 --- a/tmod.js +++ b/tmod.js @@ -9,51 +9,29 @@ var version = require('./package.json').version; var template = require('./lib/AOTcompile.js'); var uglifyjs = require('./lib/uglify.js'); +var stdout = require('./lib/stdout.js'); +var watch = require('./lib/watch.js'); +var path = require('./lib/path.js'); var fs = require('fs'); -var path = require('path'); +var vm = require('vm'); +var os = require('os'); var events = require('events'); var crypto = require('crypto'); -var vm = require('vm'); var exec = require('child_process').exec; -var os = require('os'); var engineDirname = path.dirname(require.resolve('art-template')); -// 跨平台 path 接口,统一 windows 与 linux 的路径分隔符, -// 避免不同平台模板编译后其 id 不一致 -;(function () { - - if (!/\\/.test(path.resolve())) { - return path; - } - - var oldPath = path; - var newPath = Object.create(oldPath); - var proxy = function (name) { - return function () { - var value = oldPath[name].apply(oldPath, arguments); - if (typeof value === 'string') { - value = value.split(oldPath.sep).join('/'); - } - return value; - } - }; - - for (var name in newPath) { - if (typeof oldPath[name] === 'function') { - newPath[name] = proxy(name); - } - } - - path = newPath; -})(); var RUNTIME = 'template'; -var EXTNAME_RE = /\.(html|htm|tpl)$/i; -var FILTER_RE = /[^\w\.\-$]/; var DIRNAME_RE = /[^\/]*/; +var FILTER_RE = /[^\w\.\-$]/; +var EXTNAME_RE = /\.(html|htm|tpl)$/i; + +var log = function (message) { + console.log(message) +}; module.exports = { @@ -90,18 +68,18 @@ module.exports = { engine: false, // 输出的模块类型,可选: - // templatejs: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 + // default: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 // cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) // amd: 支持 RequireJS 等流行加载器 // commonjs: 编译为 NodeJS 模块 - type: 'templatejs', + type: 'default', // 运行时别名 - // 仅针对于非 templatejs 的类型模块 + // 仅针对于非 default 的类型模块 alias: null, // 是否合并模板 - // 仅针对于 templatejs 类型的模块 + // 仅针对于 default 类型的模块 combo: true, // 是否输出为压缩的格式 @@ -112,7 +90,8 @@ module.exports = { // 获取用户配置 getUserConfig: function (options, dir) { - dir = this.path || dir; + + dir = this.base || dir; var file = dir + '/package.json'; var defaults = this.defaults; var json = null; @@ -136,14 +115,45 @@ module.exports = { "name": 'template', "version": '1.0.0', "dependencies": { - "tmodjs": "" + "tmodjs": "0" }, "tmodjs-config": {} } } + + // 模板工程依赖的的 TomdJS 版本号与当前版本对比 + (function (a, b) { + a = a.replace('~', '').split('.'); + b = b.replace('~', '').split('.'); + + for (var i = 0, an, bn; i < a.length; i ++) { + bn = Number(b[i]); + an = Number(a[i]); + + if (bn > an) { + b = 1; + a = 0; + break; + } + + if (an > bn) { + b = 0; + a = 1; + break; + } + } + + if (a > b) { + stdout('[red]You must upgrade to the latest version of TmodJS![/red]\n'); + process.exit(1); + } + })(json.dependencies.tmodjs, version); + + json.dependencies.tmodjs = '~' + version; + // 默认配置 优先级:0 for (name in defaults) { @@ -166,27 +176,9 @@ module.exports = { json['tmodjs-config'] = config; this['package.json'] = json; - - // 忽略大小写 - config.type = config.type.toLowerCase(); - - - // 模板合并规则 - // 兼容 0.0.3-rc3 之前的配置 - if (Array.isArray(config.combo) && !config.combo.length) { - config.combo = false; - } else { - config.combo = !!config.combo; - } + this._fixConfig(config); - // 根据生成模块的类型删除不支持的配置字段 - if (config.type === 'templatejs') { - delete config.alias; - } else { - delete config.combo; - } - return config; }, @@ -197,7 +189,7 @@ module.exports = { */ saveUserConfig: function () { - var file = this.path + '/package.json'; + var file = this.base + '/package.json'; var configName = 'tmodjs-config'; var json = this['package.json']; @@ -220,112 +212,36 @@ module.exports = { }, - // 绑定文件监听事件 - _onwatch: function (dir, callback) { + // 修正配置-向前兼容 + _fixConfig: function (config) { - var that = this; - var watchList = {}; - var timer = {}; - - - var walk = function (dir) { - fs.readdirSync(dir).forEach(function (item) { - var fullname = dir + '/' + item; - if (fs.statSync(fullname).isDirectory()){ - watch(fullname); - walk(fullname); - } - }); - }; - - - // 排除“.”、“_”开头或者非英文命名的目录 - var filter = function (name) { - return !FILTER_RE.test(name) && name !== that.output; - }; - - - var watch = function (parent) { - - var target = path.basename(parent); - - if (!filter(target)){ - return; - } - - if (watchList[parent]) { - watchList[parent].close(); - } - - watchList[parent] = fs.watch(parent, function (event, filename) { - - var fullname = parent + '/' + filename; - var type; - var fstype; - - if (!filter(filename)) { - return; - } - - // 检查文件、目录是否存在 - if (!fs.existsSync(fullname)) { - - // 如果目录被删除则关闭监视器 - if (watchList[fullname]) { - fstype = 'directory'; - watchList[fullname].close(); - delete watchList[fullname]; - } else { - fstype = 'file'; - } - - type = 'delete'; - - } else { - - // 文件 - if (fs.statSync(fullname).isFile()) { - - fstype = 'file'; - type = event == 'rename' ? 'create' : 'updated' - - // 文件夹 - } else if (event === 'rename') { + // 忽略大小写 + config.type = config.type.toLowerCase(); - fstype = 'directory'; - type = 'create' - watch(fullname); - walk(fullname); - } - } - - var eventData = { - type: type, - target: filename, - parent: parent, - fstype: fstype - }; + // 模板合并规则 + // 兼容 0.0.3-rc3 之前的配置 + if (Array.isArray(config.combo) && !config.combo.length) { + config.combo = false; + } else { + config.combo = !!config.combo; + } - - if (/windows/i.test(os.type())) { - // window 下 nodejs fs.watch 方法尚未稳定 - clearTimeout(timer[fullname]); - timer[fullname] = setTimeout(function() { - callback.call(that, eventData); - }, 16); - - } else { - callback.call(that, eventData); - } + // 兼容 0.1.0 之前的配置 + if (config.type === 'templatejs') { + config.type = 'default'; + } - }); - }; + // 根据生成模块的类型删除不支持的配置字段 + if (config.type === 'default') { + delete config.alias; + } else { + delete config.combo; + } - watch(dir); - walk(dir); + return config; }, @@ -405,19 +321,45 @@ module.exports = { // 获取字符串 md5 值 - _md5: function (text) { + _getMd5: function (text) { return crypto.createHash('md5').update(text).digest('hex'); }, - // 检查模板是否更改 + // 检查模板与配置是否更改 _isChange: function (html, js) { - var newMd5 = this._md5(html + JSON.stringify(this['package.json'])); - var oldMd5 = js.match(//)[1]; + var newMd5 = this._getMd5(html + JSON.stringify(this['package.json'])); + var oldMd5 = this._getMetadata(js).md5; return newMd5 !== oldMd5; }, + // 获取元数据 + _getMetadata: function (js) { + var data = js.match(/\/\*TMODJS\:(.*?)\*\//); + if (data) { + return JSON.parse(data[1]); + } else { + return {}; + } + }, + + + // 删除元数据 + _removeMetadata: function (js) { + // 文件末尾设置一个空注释,然后让 UglifyJS 不压缩它,避免很多文件挤成一行 + return js.replace(/^\/\*TMODJS\:[\w\W]*?\*\//, '/**/'); + }, + + + // 设置元数据 + _setMetadata: function (js, data) { + data = JSON.stringify(data || {}); + js = '/*TMODJS:' + data + '*/\n' + js + .replace(/\/\*TMODJS\:(.*?)\*\//, ''); + return js; + }, + // 调试语法错误 _debug: function (error, callback) { @@ -438,153 +380,101 @@ module.exports = { callback(message); }); - //this._fsUnlink(debugFile); }, - // 在控制台显示日志(支持UBB) - _log: function (message) { - - var styles = { - // styles - 'bold' : ['\x1B[1m', '\x1B[22m'], - 'italic' : ['\x1B[3m', '\x1B[23m'], - 'underline' : ['\x1B[4m', '\x1B[24m'], - 'inverse' : ['\x1B[7m', '\x1B[27m'], - // colors - 'white' : ['\x1B[37m', '\x1B[39m'], - 'grey' : ['\x1B[90m', '\x1B[39m'], - 'black' : ['\x1B[30m', '\x1B[39m'], - 'blue' : ['\x1B[34m', '\x1B[39m'], - 'cyan' : ['\x1B[36m', '\x1B[39m'], - 'green' : ['\x1B[32m', '\x1B[39m'], - 'magenta' : ['\x1B[35m', '\x1B[39m'], - 'red' : ['\x1B[31m', '\x1B[39m'], - 'yellow' : ['\x1B[33m', '\x1B[39m'] - }; + // 编译运行时 + _buildRuntime: function (templates, metadata) { + var placeholder = '/*#templates#*/'; + var output = path.join(this.output, RUNTIME + '.js'); + var data = this._runtime; + var runtime; - styles['b'] = styles['bold']; - styles['i'] = styles['italic']; - styles['u'] = styles['underline']; + if (!data) { + runtime = fs.readFileSync( + __dirname + ( + this.options.engine + ? '/lib/runtime/full.js' + : '/lib/runtime/basic.js' + ) + , 'utf-8'); - message = message.replace(/\[([^\]]*?)\]/igm, function ($1, $2) { - return $2.indexOf('/') === 0 - ? styles[$2.slice(1)][1] - : styles[$2][0]; - }); + data = { + helpers: this.helpers, + templates: templates || placeholder, + syntax: '', + engine: '' + }; - this.log(message); - }, + // 嵌入引擎 + if (this.options.engine) { + data.engine = this.engine; + data.syntax = this.syntax; + } + runtime = runtime + .replace(/['"]<\:(.*?)\:>['"]/g, function ($1, $2) { + return data[$2] || ''; + }); - log: function (message) { - process.stdout.write(message); - }, + runtime = this._setMetadata(runtime, metadata); - // 打包模板 - _combo: function () { - var that = this; - var templates = []; - var options = this.options; - var isDebug = options.debug; - var isWrappings = options.type !== 'templatejs'; - var runtime = options.engine ? '/lib/runtime/full.js' : '/lib/runtime/basic.js'; - - var template = fs.readFileSync(__dirname + runtime, 'utf-8'); + data.runtime = runtime; + this._runtime = data; - var combo = ''; - - - var walk = function (dir) { - var dirList = fs.readdirSync(dir); - - dirList.forEach(function (item) { - - if (fs.statSync(dir + '/' + item).isDirectory()) { - walk(dir + '/' + item); - } else if (that._filter(item)) { + } else { - var id = (dir + '/' + item) - .replace(EXTNAME_RE, '') - .replace(that.path + '/', ''); + runtime = data.runtime.replace(placeholder, templates); + runtime = this._setMetadata(runtime, metadata); - templates.push(id); - - var target = that.output + '/' + id + '.js'; + } + this._fsWrite(output, runtime); - if (fs.existsSync(target)) { - var code = that._fsRead(target); + this.options.debug || !this.options.minify + ? uglifyjs.beautify(output) + : uglifyjs.minify(output); - // 一个猥琐的实现: - // 文件末尾设置一个空注释,然后让 UglifyJS 不压缩它,避免很多文件挤成一行 - code = code.replace(/^\/\*[\w\W]*?\*\//, '/**/'); + data.file = output; - combo += code; - } - + return data; + }, - } - }); - }; - if (!isWrappings && options.combo) { - walk(this.path); - } + // 打包模板 + _combo: function () { - var build = Date.now(); - var debug = isDebug ? '' : ''; + var that = this; + var files = []; + var eventData = null; + var combo = ''; + var cache = this.cache; + var code = ''; - var data = { - version: this.version, - build: build, - templates: combo, - debug: debug, - syntax: '', - engine: '', - helpers: this.helpers - }; + for (var i in cache) { + code = cache[i]; + code = this._removeMetadata(code); + combo += code; - // 嵌入引擎 - if (this.options.engine) { - data.engine = fs.readFileSync(engineDirname + '/template.js', 'utf-8'); - data.syntax = this.syntax; + files.push(i); } - - template = template.replace(/['"]<\:(.*?)\:>['"]/g, function ($1, $2) { - return data[$2] || ''; + eventData = this._buildRuntime(combo, { + debug: this.options.debug, + build: Date.now() }); + eventData.files = files; - - var target = path.join(this.output, RUNTIME + '.js'); - - - this._fsWrite(target, template); - - isDebug || !this.options.minify - ? uglifyjs.beautify(target) - : uglifyjs.minify(target); - - - this.emit('combo', { - output: target, - name: RUNTIME, - fullname: RUNTIME + '.js', - extname: '.js', - isDebug: isDebug, - templates: templates, - build: build - }); + this.emit('combo', eventData); }, @@ -608,20 +498,27 @@ module.exports = { if (type === 'delete') { this.emit('delete', { - source: data.target + source: target }); fullname = fullname.replace(EXTNAME_RE, ''); - this._fsUnlink(fullname.replace(this.path, this.output) + '.js'); - this._combo(); + this._fsUnlink(fullname.replace(this.base, this.output) + '.js'); + + delete this.cache[target]; + + if (this.options.combo) { + this._combo(); + } } else if (/updated|create/.test(type)) { this.emit('change', { - source: data.target + source: target }); if (this._compile(fullname)) { - this._combo(); + if (this.options.combo) { + this._combo(); + } }; } @@ -643,36 +540,55 @@ module.exports = { // 目标路径 var target = file .replace(EXTNAME_RE, '.js') - .replace(this.path, this.output); + .replace(this.base, this.output); + + var cacheTarget = target.replace(this.output, this.output + '/.cache'); - var mod = ''; + var mod = this.cache[file]; var modObject = {}; + var metadata = {}; + var count = 0; var error = true; var errorInfo = null; var isDebug = this.options.debug; var isWrappings = this.options.wrappings; var isEngine = this.options.engine; + var newMd5 = this._getMd5(source + JSON.stringify(this['package.json'])); + - // 读取上一次编译的结果 - if (fs.existsSync(target)) { + // 如果开启了合并,编译后的文件使用缓存目录保存 + if (this.options.combo) { + target = target.replace(this.output, this.output + '/.cache'); + } + + + // 尝试从文件中读取上一次编译的结果 + if (!mod && fs.existsSync(target)) { mod = this._fsRead(target); } - // 检查模板是否有改动 - var isChange = !mod - || //.test(mod) - || isDebug - || this._isChange(source, mod); + // 获取缓存的元数据 + if (mod) { + metadata = this._getMetadata(mod); + count = metadata.version || 0; + } - var id = file - .replace(this.path + '/', './'); + // 检查是否需要编译 + var isChange = !mod // 从来没有编译过 + || metadata.debug // 上个版本为调试版 + || isDebug // 当前配置为调试版 + || newMd5 !== metadata.md5; // 模板已经发生了修改(包括配置文件) + + var id = file.replace(this.base + '/', ''); + var extname = id.match(EXTNAME_RE)[1]; + id = id.replace(EXTNAME_RE, ''); @@ -689,6 +605,7 @@ module.exports = { try { + // 编译模板 if (isChange) { modObject = template.AOTcompile(id, source, { alias: this.options.alias, @@ -707,20 +624,26 @@ module.exports = { } + // 不输出的情况:遇到错误 || 文件或配置没有更新 if (!error && isChange) { - var md5 = this._md5(source + JSON.stringify(this['package.json'])); - mod = '/* ' + (isDebug ? ' ' : '') + '*/\n' + mod; + count ++; + + mod = this._setMetadata(mod, { + debug: isDebug, + version: count, + md5: newMd5 + }); this._fsWrite(target, mod); uglifyjs[isDebug || !this.options.minify ? 'beautify' : 'minify'](target); - } var compileInfo = { id: id, + version: count, file: file, extname: extname, isChange: isChange, @@ -733,35 +656,61 @@ module.exports = { if (error) { - errorInfo.debugFile = this.path + '/.debug.js'; - - this._debug(errorInfo, function (message) { - - var e = { - name: errorInfo.name, - type: 'compileError', - message: message, - debugFile: errorInfo.debugFile, - temp: errorInfo.temp - }; - for (var name in compileInfo) { - e[name] = compileInfo[name]; - } - // 模板编译错误事件 - this.emit('compileError', e); + if (errorInfo.source) { + + // 规范错误,模板编译器通常能够给出错误源 + + this.emit('compileError', errorInfo); + this.emit('error', errorInfo); + + } else { + + // 语法错误,目前只能对比生成后的 js 来查找错误的模板语法 + + errorInfo.debugFile = this.base + '/.debug.js'; + + this.debuging = true; + + this._debug(errorInfo, function (message) { + + var e = { + name: errorInfo.name, + type: 'compileError', + message: message, + debugFile: errorInfo.debugFile, + temp: errorInfo.temp + }; + for (var name in compileInfo) { + e[name] = compileInfo[name]; + } - this.emit('error', e); + // 模板编译错误事件 + this.emit('compileError', e); + + this.emit('error', e); + + }.bind(this)); + } - }.bind(this)); } else { // 模板编译成功事件 this.emit('compile', compileInfo); + + // 删除上次遗留的调试文件 + if (this.debuging) { + this._fsUnlink(this.base + '/.debug.js'); + delete this.debuging; + } } + // 缓存编译好的模板 + this.cache[file] = mod; + + if (error) { return false; } else { @@ -800,7 +749,7 @@ module.exports = { if (!error && recursion !== false && info.requires.length) { list = info.requires.map(function (id) { - var target = path.resolve(that.path, id + extname); + var target = path.resolve(that.base, id + extname); return target; }); @@ -813,7 +762,9 @@ module.exports = { walk(typeof file === 'string' ? [file] : file); - !error && this._combo(); + if (!error && this.options.combo) { + this._combo(); + } } else { @@ -842,8 +793,11 @@ module.exports = { }; - walk(this.path); - !error && this._combo(); + walk(this.base); + + if (!error && this.options.combo) { + this._combo(); + } } }, @@ -853,21 +807,44 @@ module.exports = { events.EventEmitter.call(this); - options = this.options = this.getUserConfig(options, input); - // 模板目录 - this.path = path.resolve(input); + // 编译后的模板缓存 + this.cache = {}; + + + // 配置 + this.options = options = this.getUserConfig(options, input); + + + // 模板目录绝对路径 + this.base = path.resolve(input); - // 输出目录 - this.output = path.resolve(path.join(this.path, options.output)); - // 辅助方法 + // 输出目录绝对路径 + this.output = path.resolve(path.join(this.base, options.output)); + + + // 辅助方法(代码) this.helpers = ''; - // 加载辅助方法 + // 模板语法转换器(代码) + this.syntax = ''; + + + // 模板引擎(代码) + this.engine = fs.readFileSync( + engineDirname + '/template.js' + , 'utf-8'); + + + // 配置模板引擎:过滤数据中的 HTML + template.isEscape = options.escape; + + + // 配置模板引擎:辅助方法 if (options['helpers']) { - var helpersFile = path.join(this.path, options['helpers']); + var helpersFile = path.join(this.base, options['helpers']); if (fs.existsSync(helpersFile)) { this.helpers = fs.readFileSync(helpersFile, 'utf-8'); @@ -878,11 +855,11 @@ module.exports = { } - // 加载模板语法设置 + // 配置模板引擎:模板语法 if (options['syntax'] && options['syntax'] !== 'native') { var syntaxFile = options['syntax'] === 'simple' ? engineDirname + '/template-syntax.js' - : path.join(this.path, options['syntax']); + : path.join(this.base, options['syntax']); if (fs.existsSync(syntaxFile)) { this.syntax = fs.readFileSync(syntaxFile, 'utf-8'); @@ -892,19 +869,39 @@ module.exports = { } } + + // 删除上次遗留的调试文件 + this._fsUnlink(this.base + '/.debug.js'); - // 是否过滤 XSS 的开关 - template.isEscape = options.escape; + // 删除不必要的缓存目录 + if (!options.combo) { + this._rmdir(this.output + '/.cache'); + } + + + // 输出运行时 + this._buildRuntime(); - // 初始化 watch 事件 + + stdout('[grey]TmodJS - Template Compiler' + '[/grey]\n'); + //stdout('[grey]» ' + this.output + '[/grey]\n'); + + + // 初始化 watch 事件,插入兼容钩子 this.on('newListener', function (event, listener) { - if (event === 'watch') { - this._log('\n[inverse]Waiting..[/inverse]\n\n'); - this._onwatch(this.path, function (data) { + if (watch && event === 'watch') { + + stdout('\n[green]Waiting..[/green]\n\n'); + + watch(this.base, function (data) { this.emit('watch', data); - }); - this._onwatch = function () {}; + }.bind(this), function (name) { + // 排除“.”、“_”开头或者非英文命名的目录 + return !FILTER_RE.test(name) && name !== this.output; + }.bind(this), fs); + + watch = null; } }); @@ -912,51 +909,48 @@ module.exports = { // 监听模板修改事件 this.on('change', function (data) { var time = (new Date).toLocaleTimeString(); - this._log('[grey]' + time + '[/grey] '); - this._log('[grey]Template has been updated[/grey]\n'); + stdout('[grey]' + time + ' Change[/grey]\n'); }); // 监听模板加载事件 this.on('load', function (data) { if (data.isChange) { - this._log('[green]⊙[/green] '); + stdout('[green]•[/green] '); } else { - this._log('[grey]⊙[/grey] '); + stdout('[grey]•[/grey] '); } - this._log(data.id.replace(/^\.\//, '') + '[grey].' + data.extname + '[/grey]'); + stdout(data.id); }); // 监听模板编译事件 this.on('compile', function (data) { - this._log('\n'); + stdout(this.options.debug ? ' [grey][/grey]' : ''); + stdout(' [grey]:v' + data.version + '[/grey]'); + stdout('\n'); }); // 监听编译错误事件 this.on('compileError', function (data) { - this._log(' [inverse][red]Syntax Error[/red][/inverse]\n'); - this._log('\n[red]Template ID: ' + data.id + '[/red]\n'); - this._log('[red]' + data.message + '[/red]\n'); - }); - + stdout(' [inverse][red]Syntax Error[/red][/inverse]\n\n'); - // 监听模板合并事件 - this.on('combo', function (data) { - var output = options.type === 'templatejs' - ? options.output + '/' + data.fullname - : options.output + '/'; + if (data.source) { + stdout('[red]' + data.line + ': ' + data.source + '[/red]\n'); + } - output = output.replace(/^\.\//, ''); + stdout('[red]' + data.message + '[/red]\n'); + }); - this._log('[grey]> [/grey]'); - this._log(this.options.debug ? '[inverse][/inverse] ' : ''); - this._log(output); - this._log('\n'); - }); + // 监听模板合并事件 + // this.on('combo', function (data) { + // stdout('[grey]»[/grey] '); + // stdout('[grey]' + data.file + '[/grey]'); + // stdout('\n'); + // }); } From bb668074dcae4927c29defa905f975f5a50782e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sun, 5 Jan 2014 02:17:47 +0800 Subject: [PATCH 11/87] v0.1.0 --- .gitignore | 1 + README.md | 2 +- bin/tmod | 6 +- lib/version.js | 31 -- package.json | 2 +- {lib => src}/AOTcompile.js | 2 +- {lib => src}/path.js | 2 +- {lib => src}/runtime/basic.js | 0 {lib => src}/runtime/full.js | 0 {lib => src}/stdout.js | 0 tmod.js => src/tmod.js | 370 ++++++++++++------ {lib => src}/uglify.js | 0 {lib => src}/watch.js | 5 +- test/README.md | 29 +- test/test-all/escape-off.html | 49 +++ test/test-all/escape-off/build/index.js | 2 + test/test-all/escape-off/build/template.js | 3 + test/test-all/escape-off/index.html | 8 + test/test-all/escape-off/package.json | 18 + test/test-all/helper/package.json | 4 +- test/test-all/helper/template-helpers.js | 6 +- test/test-all/include/build/include.js | 2 +- test/test-all/include/build/template.js | 3 +- test/test-all/include/package.json | 2 +- test/test-all/mix-cmd/build/copyright.js | 2 +- test/test-all/mix-cmd/build/index.js | 2 +- test/test-all/mix-cmd/build/public/footer.js | 2 +- test/test-all/mix-cmd/build/public/header.js | 2 +- test/test-all/mix-cmd/build/public/logo.js | 2 +- test/test-all/mix-cmd/build/template.js | 19 +- test/test-all/mix-cmd/package.json | 11 +- test/test-all/mix/build/copyright.js | 2 +- test/test-all/mix/build/index.js | 2 +- test/test-all/mix/build/public/footer.js | 2 +- test/test-all/mix/build/public/header.js | 2 +- test/test-all/mix/build/public/logo.js | 2 +- test/test-all/mix/build/template.js | 3 +- test/test-all/mix/package.json | 4 +- test/test-all/syntax-native/.debug.js | 13 - test/test-all/syntax-native/build/template.js | 4 +- test/test-all/syntax-native/package.json | 4 +- test/test-all/syntax/.debug.js | 13 - test/test-all/syntax/build/template.js | 6 +- test/test-all/syntax/package.json | 4 +- test/tpl/build/template.js | 4 +- test/tpl/index.html | 2 +- 46 files changed, 405 insertions(+), 249 deletions(-) delete mode 100644 lib/version.js rename {lib => src}/AOTcompile.js (99%) rename {lib => src}/path.js (98%) rename {lib => src}/runtime/basic.js (100%) rename {lib => src}/runtime/full.js (100%) rename {lib => src}/stdout.js (100%) rename tmod.js => src/tmod.js (77%) rename {lib => src}/uglify.js (100%) rename {lib => src}/watch.js (96%) create mode 100644 test/test-all/escape-off.html create mode 100644 test/test-all/escape-off/build/index.js create mode 100644 test/test-all/escape-off/build/template.js create mode 100644 test/test-all/escape-off/index.html create mode 100644 test/test-all/escape-off/package.json delete mode 100644 test/test-all/syntax-native/.debug.js delete mode 100644 test/test-all/syntax/.debug.js diff --git a/.gitignore b/.gitignore index 9fc71e8..338b964 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache .debug.js .DS_Store +*.sublime-workspace node_modules diff --git a/README.md b/README.md index 5c8433d..e798ad1 100644 --- a/README.md +++ b/README.md @@ -269,11 +269,11 @@ minify: true ### v0.1.0 -* 减少对磁盘的读写,优化性能 * 增加自动递增的模板版本号,控制台可显示模板被修改的次数 * 优化默认设置下输出的文件数量,仅保留``template.js``,临时文件使用隐藏的``.cache``目录存放 * 自动清理``.debug.js``文件 * 对非规范的``include``语句模板在编译过程给予提示 +* 减少对磁盘的读写,优化性能 ### v0.0.4 diff --git a/bin/tmod b/bin/tmod index ddc71b1..e7365ce 100644 --- a/bin/tmod +++ b/bin/tmod @@ -2,7 +2,7 @@ 'use strict'; -var TmodJS = require('../tmod.js'); +var TmodJS = require('../src/tmod.js'); var version = require('../package.json').version; var fs = require('fs'); @@ -147,7 +147,7 @@ if (!fs.existsSync(dir)) { process.stdout.write('Error: directory does not exist\n'); help(); process.exit(1); -}; +} // 转换成相对于模板目录的路径 @@ -177,9 +177,11 @@ if (isEditConfig) { ); } else { + TmodJS.compile(); if (isWatch) { TmodJS.watch(); } + } diff --git a/lib/version.js b/lib/version.js deleted file mode 100644 index c2a00bb..0000000 --- a/lib/version.js +++ /dev/null @@ -1,31 +0,0 @@ - /*! - * 新旧版本号对比 - * https://github.com/aui/tmodjs - * Released under the MIT, BSD, and GPL Licenses - */ - -// 模板工程依赖的的 TomdJS 版本号与当前版本对比 - -// a < b -module.exports = function (a, b) { - a = a.replace('~', '').split('.'); - b = b.replace('~', '').split('.'); - var ret = true; - - for (var i = 0, an, bn; i < a.length; i ++) { - bn = Number(b[i]); - an = Number(a[i]); - - if (bn > an) { - ret = true; - break; - } - - if (an > bn) { - ret = false; - break; - } - } - - return ret; -} \ No newline at end of file diff --git a/package.json b/package.json index 008f638..8570446 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "type": "git", "url": "https://github.com/aui/tmodjs.git" }, - "main": "./tmod.js", + "main": "./src/tmod.js", "bin": { "tmod": "./bin/tmod", "tmodjs": "./bin/tmod" diff --git a/lib/AOTcompile.js b/src/AOTcompile.js similarity index 99% rename from lib/AOTcompile.js rename to src/AOTcompile.js index a193e11..fd17175 100644 --- a/lib/AOTcompile.js +++ b/src/AOTcompile.js @@ -46,7 +46,7 @@ var testTemplateSyntax = function (source) { var testUri = function (uri, fromUri, source) { if (!/^\./.test(uri) || EXTNAME_RE.test(uri)) { - var line, source; + var line; // 如果只出现一次这个字符串,很容易确认模板错误行 if (source.split(uri).length === 2) { diff --git a/lib/path.js b/src/path.js similarity index 98% rename from lib/path.js rename to src/path.js index f168e18..ac6af7d 100644 --- a/lib/path.js +++ b/src/path.js @@ -20,7 +20,7 @@ if (!/\\/.test(path.resolve())) { value = value.split(oldPath.sep).join('/'); } return value; - } + }; }; for (var name in newPath) { diff --git a/lib/runtime/basic.js b/src/runtime/basic.js similarity index 100% rename from lib/runtime/basic.js rename to src/runtime/basic.js diff --git a/lib/runtime/full.js b/src/runtime/full.js similarity index 100% rename from lib/runtime/full.js rename to src/runtime/full.js diff --git a/lib/stdout.js b/src/stdout.js similarity index 100% rename from lib/stdout.js rename to src/stdout.js diff --git a/tmod.js b/src/tmod.js similarity index 77% rename from tmod.js rename to src/tmod.js index 52fe380..8608d3e 100644 --- a/tmod.js +++ b/src/tmod.js @@ -6,16 +6,15 @@ 'use strict'; -var version = require('./package.json').version; -var template = require('./lib/AOTcompile.js'); -var uglifyjs = require('./lib/uglify.js'); -var stdout = require('./lib/stdout.js'); -var watch = require('./lib/watch.js'); -var path = require('./lib/path.js'); +var version = require('../package.json').version; +var template = require('./AOTcompile.js'); +var uglifyjs = require('./uglify.js'); +var stdout = require('./stdout.js'); +var watch = require('./watch.js'); +var path = require('./path.js'); var fs = require('fs'); var vm = require('vm'); -var os = require('os'); var events = require('events'); var crypto = require('crypto'); var exec = require('child_process').exec; @@ -25,12 +24,11 @@ var engineDirname = path.dirname(require.resolve('art-template')); var RUNTIME = 'template'; -var DIRNAME_RE = /[^\/]*/; var FILTER_RE = /[^\w\.\-$]/; var EXTNAME_RE = /\.(html|htm|tpl)$/i; var log = function (message) { - console.log(message) + console.log(message); }; @@ -89,10 +87,16 @@ module.exports = { // 获取用户配置 - getUserConfig: function (options, dir) { + getUserConfig: function () { - dir = this.base || dir; - var file = dir + '/package.json'; + var options = arguments[0]; + + if (!options) { + return this.options; + } + + var base = this.base; + var file = base + '/package.json'; var defaults = this.defaults; var json = null; var name = null; @@ -118,7 +122,7 @@ module.exports = { "tmodjs": "0" }, "tmodjs-config": {} - } + }; } @@ -251,16 +255,18 @@ module.exports = { }, - // 模板文件写入 - _fsWrite: function (file, data) { + // 文件写入 + _fsWrite: function (file, data, charset) { this._fsMkdir(path.dirname(file)); - fs.writeFileSync(file, data, this.options['charset']); + fs.writeFileSync(file, data, charset || 'utf-8'); }, - // 模板文件读取 - _fsRead: function (file) { - return fs.readFileSync(file, this.options['charset']); + // 文件读取 + _fsRead: function (file, charset) { + if (fs.existsSync(file)) { + return fs.readFileSync(file, charset || 'utf-8'); + } }, @@ -283,7 +289,7 @@ module.exports = { // 删除文件夹,包括子文件夹 - _rmdir: function (dir) { + _fsRmdir: function (dir) { var walk = function (dir) { @@ -360,6 +366,7 @@ module.exports = { return js; }, + // 调试语法错误 _debug: function (error, callback) { @@ -368,7 +375,7 @@ module.exports = { code = "/*! */\n' + code; - this._fsWrite(debugFile, code); + this._fsWrite(debugFile, code, this.options.charset); // 启动子进程进行调试,从根本上避免影响当前进程 @@ -386,23 +393,26 @@ module.exports = { // 编译运行时 _buildRuntime: function (templates, metadata) { + metadata = metadata || {}; + var placeholder = '/*#templates#*/'; var output = path.join(this.output, RUNTIME + '.js'); var data = this._runtime; var runtime; + if (!data) { runtime = fs.readFileSync( __dirname + ( this.options.engine - ? '/lib/runtime/full.js' - : '/lib/runtime/basic.js' + ? '/runtime/full.js' + : '/runtime/basic.js' ) , 'utf-8'); data = { - helpers: this.helpers, + helpers: this._helpersCode, templates: templates || placeholder, syntax: '', engine: '' @@ -411,8 +421,10 @@ module.exports = { // 嵌入引擎 if (this.options.engine) { - data.engine = this.engine; - data.syntax = this.syntax; + data.engine = fs.readFileSync( + engineDirname + '/template.js' + , 'utf-8'); + data.syntax = this._syntaxCode; } @@ -435,29 +447,27 @@ module.exports = { } - this._fsWrite(output, runtime); + this._fsWrite(output, runtime, this.options.charset); - this.options.debug || !this.options.minify - ? uglifyjs.beautify(output) - : uglifyjs.minify(output); - - - data.file = output; + if (this.options.debug || !this.options.minify) { + uglifyjs.beautify(output); + } else { + uglifyjs.minify(output); + } - return data; + return runtime; }, // 打包模板 _combo: function () { - var that = this; var files = []; - var eventData = null; + var mod = null; var combo = ''; - - var cache = this.cache; + var cache = this._getCache(); var code = ''; + var build = Date.now(); for (var i in cache) { @@ -468,13 +478,28 @@ module.exports = { files.push(i); } - eventData = this._buildRuntime(combo, { + mod = this._buildRuntime(combo, { debug: this.options.debug, - build: Date.now() + build: build }); - eventData.files = files; + + + // 广播:合并事件 + this.emit('combo', { - this.emit('combo', eventData); + // 编译时间 + build: build, + + // 打包的代码 + output: mod, + + // 输出的文件路径 + outputFile: path.join(this.output, RUNTIME + '.js'), + + // 被合并的文件列表 + sourcefiles: files + + }); }, @@ -498,12 +523,13 @@ module.exports = { if (type === 'delete') { this.emit('delete', { - source: target + id: this._toId(target), + sourceFile: target }); fullname = fullname.replace(EXTNAME_RE, ''); this._fsUnlink(fullname.replace(this.base, this.output) + '.js'); - delete this.cache[target]; + this._removeCache(target); if (this.options.combo) { this._combo(); @@ -512,14 +538,15 @@ module.exports = { } else if (/updated|create/.test(type)) { this.emit('change', { - source: target + id: this._toId(target), + sourceFile: target }); if (this._compile(fullname)) { if (this.options.combo) { this._combo(); } - }; + } } } @@ -529,32 +556,33 @@ module.exports = { }, + // 路径转换为模板 ID + _toId: function (file) { + var id = file.replace(this.base + '/', ''); + var extname = id.match(EXTNAME_RE)[1]; + id = id.replace(EXTNAME_RE, ''); + return id; + }, + + // 编译单个模板 _compile: function (file) { - var that = this; - // 模板字符串 - var source = this._fsRead(file); + var source = this._fsRead(file, this.options.charset); // 目标路径 var target = file .replace(EXTNAME_RE, '.js') .replace(this.base, this.output); - - var cacheTarget = target.replace(this.output, this.output + '/.cache'); - - var mod = this.cache[file]; + var mod = this._getCache(file); var modObject = {}; var metadata = {}; var count = 0; var error = true; var errorInfo = null; var isDebug = this.options.debug; - var isWrappings = this.options.wrappings; - var isEngine = this.options.engine; - var newMd5 = this._getMd5(source + JSON.stringify(this['package.json'])); @@ -566,7 +594,7 @@ module.exports = { // 尝试从文件中读取上一次编译的结果 if (!mod && fs.existsSync(target)) { - mod = this._fsRead(target); + mod = this._fsRead(target, this.options.charset); } @@ -584,22 +612,28 @@ module.exports = { || newMd5 !== metadata.md5; // 模板已经发生了修改(包括配置文件) + // 获取模板 ID + var id = this._toId(file); - var id = file.replace(this.base + '/', ''); - - var extname = id.match(EXTNAME_RE)[1]; - - id = id.replace(EXTNAME_RE, ''); - - // 模板加载事件 + // 广播:模板加载事件 this.emit('load', { + + // 模板 ID id: id, - file: file, - extname: extname, + + // 模板是否被修改 isChange: isChange, + + // 原始文件路径 + sourceFile: file, + + // 模板源代码 source: source, - target: target + + // 输出路径 + outputFile: target + }); @@ -635,22 +669,39 @@ module.exports = { md5: newMd5 }); - this._fsWrite(target, mod); + this._fsWrite(target, mod, this.options.charset); uglifyjs[isDebug || !this.options.minify ? 'beautify' : 'minify'](target); } var compileInfo = { + + // 模板 ID id: id, + + // 版本 version: count, - file: file, - extname: extname, + + // 源码 + source: source, + + // 模板文件路径 + sourceFile: file, + + // 编译结果代码 + output: mod, + + // 编译输出文件路径 + outputFile: target, + + // 是否被修改 isChange: isChange, + + // 是否遇到错误 error: error, - source: source, - target: target, - code: mod, + + // 依赖的子模板 ID 列表 requires: modObject.requires || [] }; @@ -675,12 +726,21 @@ module.exports = { this._debug(errorInfo, function (message) { var e = { + + // 错误名称 name: errorInfo.name, - type: 'compileError', + + // 报错信息 message: message, + + // 调试文件地址 debugFile: errorInfo.debugFile, + + // 编译器输出的临时文件 temp: errorInfo.temp + }; + for (var name in compileInfo) { e[name] = compileInfo[name]; } @@ -708,7 +768,7 @@ module.exports = { // 缓存编译好的模板 - this.cache[file] = mod; + this._setCache(file, mod); if (error) { @@ -728,12 +788,13 @@ module.exports = { var that = this; var error = false; + var walk; if (file) { var extname = path.extname(file); - var walk = function (list) { + walk = function (list) { list.forEach(function (file) { @@ -755,7 +816,7 @@ module.exports = { walk(list); - }; + } }); }; @@ -769,7 +830,7 @@ module.exports = { } else { - var walk = function (dir) { + walk = function (dir) { if (dir === that.output) { return; @@ -803,93 +864,135 @@ module.exports = { }, - init: function (input, options) { - - events.EventEmitter.call(this); - - - // 编译后的模板缓存 - this.cache = {}; - - - // 配置 - this.options = options = this.getUserConfig(options, input); - + // 计算字节长度 + _getByteLength: function (content) { + return content.replace(/[^\x00-\xff]/gi, '--').length; + }, - // 模板目录绝对路径 - this.base = path.resolve(input); + _cache: {}, - // 输出目录绝对路径 - this.output = path.resolve(path.join(this.base, options.output)); + // 获取缓存 + _getCache: function (id) { + if (typeof id === 'undefined') { + return this._cache; + } else { + return this._cache[id]; + } + }, - // 辅助方法(代码) - this.helpers = ''; + // 设置缓存 + _setCache: function (id, data) { + this._cache[id] = data; + }, - // 模板语法转换器(代码) - this.syntax = ''; + // 删除缓存 + _removeCache: function (id) { + delete this._cache[id]; + }, - // 模板引擎(代码) - this.engine = fs.readFileSync( - engineDirname + '/template.js' - , 'utf-8'); + // 初始化模板引擎 + _initEngine: function () { + var options = this.options; // 配置模板引擎:过滤数据中的 HTML template.isEscape = options.escape; // 配置模板引擎:辅助方法 - if (options['helpers']) { - var helpersFile = path.join(this.base, options['helpers']); + if (options.helpers) { + var helpersFile = path.resolve(this.base, options.helpers); + if (fs.existsSync(helpersFile)) { - this.helpers = fs.readFileSync(helpersFile, 'utf-8'); - vm.runInNewContext(this.helpers, { + + this._helpersCode = fs.readFileSync(helpersFile, 'utf-8'); + vm.runInNewContext(this._helpersCode, { template: template }); + + } else { + + stdout('[red]Not found: ' + helpersFile + '[/red]'); + process.exit(1); + } } // 配置模板引擎:模板语法 - if (options['syntax'] && options['syntax'] !== 'native') { - var syntaxFile = options['syntax'] === 'simple' + if (options.syntax && options.syntax !== 'native') { + + var syntaxFile = options.syntax === 'simple' ? engineDirname + '/template-syntax.js' - : path.join(this.base, options['syntax']); + : path.resolve(this.base, options.syntax); if (fs.existsSync(syntaxFile)) { - this.syntax = fs.readFileSync(syntaxFile, 'utf-8'); - vm.runInNewContext(this.syntax, { + + this._syntaxCode = fs.readFileSync(syntaxFile, 'utf-8'); + vm.runInNewContext(this._syntaxCode, { template: template }); + + } else { + + stdout('[red]Not found: ' + syntaxFile + '[/red]'); + process.exit(1); + } - } + } + }, + + + // 清理项目临时文件 + _clear: function () { - // 删除上次遗留的调试文件 this._fsUnlink(this.base + '/.debug.js'); // 删除不必要的缓存目录 - if (!options.combo) { - this._rmdir(this.output + '/.cache'); + if (!this.options.combo) { + this._fsRmdir(this.output + '/.cache'); } - - // 输出运行时 - this._buildRuntime(); + }, - stdout('[grey]TmodJS - Template Compiler' + '[/grey]\n'); - //stdout('[grey]» ' + this.output + '[/grey]\n'); + init: function (input, options) { + + + // 模板项目路径 + this.base = path.resolve(input); + + + // 项目配置选项 + this.options = options = this.getUserConfig(options); + + + // 输出路径 + this.output = path.resolve(this.base, options.output); + + + // 清理模板项目临时文件 + this._clear(); + + + // 初始化模板引擎 + this._initEngine(); + + + // 初始化事件系统 + events.EventEmitter.call(this); // 初始化 watch 事件,插入兼容钩子 this.on('newListener', function (event, listener) { + if (watch && event === 'watch') { stdout('\n[green]Waiting..[/green]\n\n'); @@ -897,19 +1000,30 @@ module.exports = { watch(this.base, function (data) { this.emit('watch', data); }.bind(this), function (name) { - // 排除“.”、“_”开头或者非英文命名的目录 + + // 排除“.”、“_”开头或者非英文命名的子目录 return !FILTER_RE.test(name) && name !== this.output; + }.bind(this), fs); watch = null; } + }); // 监听模板修改事件 this.on('change', function (data) { var time = (new Date).toLocaleTimeString(); - stdout('[grey]' + time + ' Change[/grey]\n'); + stdout('[grey]' + time + '[/grey]\n'); + }); + + + // 监听模板删除事件 + this.on('delete', function (data) { + var time = (new Date).toLocaleTimeString(); + stdout('[grey]' + time + '[/grey]\n'); + stdout('[red]-[/red] ' + data.id + '\n'); }); @@ -937,7 +1051,7 @@ module.exports = { this.on('compileError', function (data) { stdout(' [inverse][red]Syntax Error[/red][/inverse]\n\n'); - if (data.source) { + if (data.line && data.source) { stdout('[red]' + data.line + ': ' + data.source + '[/red]\n'); } @@ -948,11 +1062,17 @@ module.exports = { // 监听模板合并事件 // this.on('combo', function (data) { // stdout('[grey]»[/grey] '); - // stdout('[grey]' + data.file + '[/grey]'); + // stdout('[grey]' + RUNTIME + '.js[/grey]'); + // stdout(' [grey]:build' + data.version + '[/grey]'); // stdout('\n'); // }); + // 输出运行时 + this._buildRuntime(); + + + stdout('[grey]TmodJS - Template Compiler' + '[/grey]\n'); + } }; - diff --git a/lib/uglify.js b/src/uglify.js similarity index 100% rename from lib/uglify.js rename to src/uglify.js diff --git a/lib/watch.js b/src/watch.js similarity index 96% rename from lib/watch.js rename to src/watch.js index ff1f387..93d27a9 100644 --- a/lib/watch.js +++ b/src/watch.js @@ -68,13 +68,14 @@ var watch = function (parent, callback, filter) { if (fs.statSync(fullname).isFile()) { fstype = 'file'; - type = event == 'rename' ? 'create' : 'updated' + type = event == 'rename' ? 'create' : 'updated'; // 文件夹 } else if (event === 'rename') { fstype = 'directory'; - type = 'create' + type = 'create'; + watch(fullname, callback, filter); walk(fullname, callback, filter); } diff --git a/test/README.md b/test/README.md index 40f1427..6c4b13d 100644 --- a/test/README.md +++ b/test/README.md @@ -1,21 +1,20 @@ # TmodJS-运行测试例子 -切换到当前目录,执行 tmod 命令 +## 一、运行默认设置测试例子 + +### 编译 + +切换到当前目录 + + $ cd test/tpl + +然后执行 tmod 命令 ``` -$ cd tmodjs/test/tpl $ tmod ``` -如果终端返回如下信息,表示模板已经编译完成 -``` -./copyright.html √ -./index.html √ -./public/footer.html √ -./public/header.html √ -./public/logo.html √ -``` -## 在浏览器中加载模板 +### 在浏览器中加载模板 模板经过编译后,支持普通脚本引入与 SeaJS 、RequireJS 等模块管理器调用。 @@ -23,8 +22,12 @@ $ tmod * 使用 RequireJS 加载模板:[requirejs.html](requirejs.html) * 使用 SeaJS 加载模板:[seajs.html](seajs.html) -## 在 NodeJS 中加载模板 +### 在 NodeJS 中加载模板 运行示例: - $ node node.js \ No newline at end of file + $ node node.js + +## 二、运行其他设置测试例子 + +``test/test-all`` 下每个目录都是一个独立的模板项目,它们设置了不同的配置属性,切换到目录后可以单独编译。目录有对应的同名``.html``文件,可以用来查看效果。 diff --git a/test/test-all/escape-off.html b/test/test-all/escape-off.html new file mode 100644 index 0000000..dfccc62 --- /dev/null +++ b/test/test-all/escape-off.html @@ -0,0 +1,49 @@ + + + + +TemplateJS - 调用模板演示 + + + + +
    loading..
    + + + + + + + + diff --git a/test/test-all/escape-off/build/index.js b/test/test-all/escape-off/build/index.js new file mode 100644 index 0000000..d5a1d7e --- /dev/null +++ b/test/test-all/escape-off/build/index.js @@ -0,0 +1,2 @@ +/* */ +template("index",function(i){var t=this,e=t.$string,l=t.$ubb2html,n=i.title,a=t.$each,u=i.list,r=(i.$value,i.$index,t.$escape),h="";return h+='

    ',h+=e(l(n)),h+="

    ",new String(h)}); \ No newline at end of file diff --git a/test/test-all/escape-off/build/template.js b/test/test-all/escape-off/build/template.js new file mode 100644 index 0000000..705628d --- /dev/null +++ b/test/test-all/escape-off/build/template.js @@ -0,0 +1,3 @@ +/*TMODJS:{"build":1388858518985}*/ +!function(n){"use strict";var e=function(n,r){return e[/string|function/.test(typeof r)?"compile":"render"].apply(e,arguments)},r=e.cache={},t=function(n,e){return"string"!=typeof n&&(e=typeof n,"number"===e?n+="":n="function"===e?t(n.call(n)):""),n},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(n){return t(n).replace(/&(?![\w#]+;)|[<>"']/g,function(n){return i[n]})},u=Array.isArray||function(n){return"[object Array]"==={}.toString.call(n)},c=function(n,e){if(u(n))for(var r=0,t=n.length;t>r;r++)e.call(n,n[r],r,n);else for(r in n)e.call(n,n[r],r)},a=function(n,e){var r=/(\/)[^/]+\1\.\.\1/,t=n.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+e;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},l=e.helpers={$include:function(n,r,t){var i=a(t,n);return e.render(i,r)},$string:t,$escape:o,$each:c},f=function(e){var r="";for(var t in e)r+="<"+t+">\n"+e[t]+"\n\n";return r&&n.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};e.render=function(n,r){var t=e.get(n)||f({id:n,name:"Render Error",message:"No Template"});return r?t(r):t},e.compile=function(n,e){var t="function"==typeof e,i=r[n]=function(r){try{return t?new e(r,n)+"":e}catch(i){return f(i)()}};return i.prototype=e.prototype=l,i.toString=function(){return e+""},i},e.get=function(n){return r[n.replace(/^\.\//,"")]},e.helper=function(n,e){l[n]=e},/**/ +e("index",function(n){var e=this,r=e.$string,t=n.title,i=e.$each,o=n.list,u=(n.$value,n.$index,"");return u+='

    ',u+=r(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:n.template=e}(this); \ No newline at end of file diff --git a/test/test-all/escape-off/index.html b/test/test-all/escape-off/index.html new file mode 100644 index 0000000..f85da91 --- /dev/null +++ b/test/test-all/escape-off/index.html @@ -0,0 +1,8 @@ +
    +

    {{title}}

    + +
    \ No newline at end of file diff --git a/test/test-all/escape-off/package.json b/test/test-all/escape-off/package.json new file mode 100644 index 0000000..ce9947c --- /dev/null +++ b/test/test-all/escape-off/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.1.0" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": null, + "escape": false, + "engine": false, + "type": "default", + "combo": true, + "minify": true + } +} \ No newline at end of file diff --git a/test/test-all/helper/package.json b/test/test-all/helper/package.json index 2382cc6..c2cd2cc 100644 --- a/test/test-all/helper/package.json +++ b/test/test-all/helper/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": "./template-helpers.js", "escape": true, "engine": false, - "type": "templatejs", + "type": "default", "combo": true, "minify": true } diff --git a/test/test-all/helper/template-helpers.js b/test/test-all/helper/template-helpers.js index c53b401..7a3bbb7 100644 --- a/test/test-all/helper/template-helpers.js +++ b/test/test-all/helper/template-helpers.js @@ -1,5 +1,9 @@ template.helper('$ubb2html', function (content) { - content = template.helpers.$escape(content); + + // 转义 HTML 字符 + content = this.$escape(content); + + // 解析 UBB 字符 return content .replace(/\[b\]([^\[]*?)\[\/b\]/igm, '$1') .replace(/\[i\]([^\[]*?)\[\/i\]/igm, '$1') diff --git a/test/test-all/include/build/include.js b/test/test-all/include/build/include.js index eddd1b7..ab56936 100644 --- a/test/test-all/include/build/include.js +++ b/test/test-all/include/build/include.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{"version":1,"md5":"5bfc05c04116597857d42c256cd45c60"}*/ define([ "./template", "./a", "./b", "./e", "./d" ], function(template) { return template("include", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/include/build/template.js b/test/test-all/include/build/template.js index 70267fc..3a46f9b 100644 --- a/test/test-all/include/build/template.js +++ b/test/test-all/include/build/template.js @@ -1,5 +1,6 @@ -/* */ +/*TMODJS:{}*/ !function(global) { + "use strict"; var template = function(path, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; diff --git a/test/test-all/include/package.json b/test/test-all/include/package.json index aacbeba..ff0ed81 100644 --- a/test/test-all/include/package.json +++ b/test/test-all/include/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/mix-cmd/build/copyright.js b/test/test-all/mix-cmd/build/copyright.js index 1cef0d2..60c77b6 100644 --- a/test/test-all/mix-cmd/build/copyright.js +++ b/test/test-all/mix-cmd/build/copyright.js @@ -1,4 +1,4 @@ -/*! */ +/*TMODJS:{"version":1,"md5":"a7505b204693c3fc9d8d9d4a39e1dd79"}*/ define(function(require) { return require("./template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/index.js b/test/test-all/mix-cmd/build/index.js index 4bf2eda..50f6fcb 100644 --- a/test/test-all/mix-cmd/build/index.js +++ b/test/test-all/mix-cmd/build/index.js @@ -1,4 +1,4 @@ -/*! */ +/*TMODJS:{"version":1,"md5":"ca7744b5f395e246704f15a19b0f1cd3"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/mix-cmd/build/public/footer.js b/test/test-all/mix-cmd/build/public/footer.js index 5cca7ea..0f9d4a1 100644 --- a/test/test-all/mix-cmd/build/public/footer.js +++ b/test/test-all/mix-cmd/build/public/footer.js @@ -1,4 +1,4 @@ -/*! */ +/*TMODJS:{"version":1,"md5":"c87606660b2dcff6eeb6f77834eb2e33"}*/ define(function(require) { require("../copyright"); return require("../template")("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); diff --git a/test/test-all/mix-cmd/build/public/header.js b/test/test-all/mix-cmd/build/public/header.js index d1ec4cb..3596b62 100644 --- a/test/test-all/mix-cmd/build/public/header.js +++ b/test/test-all/mix-cmd/build/public/header.js @@ -1,4 +1,4 @@ -/*! */ +/*TMODJS:{"version":1,"md5":"90bde39e11e49b42ffea5e5d22ceab72"}*/ define(function(require) { require("./logo"); return require("../template")("public/header", ' '); diff --git a/test/test-all/mix-cmd/build/public/logo.js b/test/test-all/mix-cmd/build/public/logo.js index a75fc6a..fa8ece6 100644 --- a/test/test-all/mix-cmd/build/public/logo.js +++ b/test/test-all/mix-cmd/build/public/logo.js @@ -1,4 +1,4 @@ -/*! */ +/*TMODJS:{"version":1,"md5":"3f4b1a471c0bc8b81b1e8e733745db04"}*/ define(function(require) { return require("../template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/template.js b/test/test-all/mix-cmd/build/template.js index 9fa1ed3..e57f2b5 100644 --- a/test/test-all/mix-cmd/build/template.js +++ b/test/test-all/mix-cmd/build/template.js @@ -1,10 +1,10 @@ -/*! */ +/*TMODJS:{}*/ (function(global) { "use strict"; var template = function(id, content) { return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); }; - template.version = "2.0.2"; + template.version = "2.0.3"; template.openTag = "<%"; template.closeTag = "%>"; template.isEscape = true; @@ -161,10 +161,10 @@ var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); var isNewEngine = "".trim; var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; - var concat = isNewEngine ? "if(content!==undefined){$out+=content;return content;}" : "$out.push(content);"; - var print = "function(content){" + concat + "}"; - var include = "function(id,data){" + "data=data||$data;" + "var content=$helpers.$include(id,data,$id);" + concat + "}"; - forEach(code.split(openTag), function(code, i) { + var concat = isNewEngine ? "$out+=$text;return $text;" : "$out.push($text);"; + var print = "function($text){" + concat + "}"; + var include = "function(id,data){" + "data=data||$data;" + "var $text=$helpers.$include(id,data,$id);" + concat + "}"; + forEach(code.split(openTag), function(code) { code = code.split(closeTag); var $0 = code[0]; var $1 = code[1]; @@ -211,8 +211,8 @@ }); } if (code.indexOf("=") === 0) { - var isEscape = code.indexOf("==") !== 0; - code = code.replace(/^=*|[\s;]*$/g, ""); + var isEscape = !/^=[=#]/.test(code); + code = code.replace(/^=[=#]?|[\s;]*$/g, ""); if (isEscape && template.isEscape) { var name = code.replace(/\s*\([^\)]+\)/, ""); if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { @@ -274,6 +274,7 @@ })(this); !function(global, template) { + "use strict"; var get = template.get; var helpers = template.helpers; var resolve = function(from, to) { @@ -345,7 +346,7 @@ default: if (exports.helpers.hasOwnProperty(key)) { - code = "==" + key + "(" + split.join(",") + ");"; + code = "=#" + key + "(" + split.join(",") + ");"; } else { code = code.replace(/[\s;]*$/, ""); code = "=" + code; diff --git a/test/test-all/mix-cmd/package.json b/test/test-all/mix-cmd/package.json index 2d90e8f..937dbee 100644 --- a/test/test-all/mix-cmd/package.json +++ b/test/test-all/mix-cmd/package.json @@ -2,18 +2,17 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.3-rc1" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", "charset": "utf-8", - "combo": [], "syntax": "simple", "helpers": null, - "alias": null, - "minify": false, - "async": false, + "escape": true, "engine": true, - "type": "cmd" + "type": "cmd", + "alias": null, + "minify": false } } \ No newline at end of file diff --git a/test/test-all/mix/build/copyright.js b/test/test-all/mix/build/copyright.js index 71f269b..44868a2 100644 --- a/test/test-all/mix/build/copyright.js +++ b/test/test-all/mix/build/copyright.js @@ -1,2 +1,2 @@ -/* */ +/*TMODJS:{"version":1,"md5":"162d4ecb65e3a746824f925ec57a7c7e"}*/ template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/mix/build/index.js b/test/test-all/mix/build/index.js index dbc2f52..fc6ab18 100644 --- a/test/test-all/mix/build/index.js +++ b/test/test-all/mix/build/index.js @@ -1,2 +1,2 @@ -/* */ +/*TMODJS:{"version":1,"md5":"1feef40be049b6913e9bee646e8d73bc"}*/ template("index", "{{include './public/header'}}

    {{title}}

    {{include './public/footer'}}"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/footer.js b/test/test-all/mix/build/public/footer.js index 49ad12b..250f2d4 100644 --- a/test/test-all/mix/build/public/footer.js +++ b/test/test-all/mix/build/public/footer.js @@ -1,2 +1,2 @@ -/* */ +/*TMODJS:{"version":1,"md5":"33cd8faf8ecaf3969a682d0a841e6a90"}*/ template("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); \ No newline at end of file diff --git a/test/test-all/mix/build/public/header.js b/test/test-all/mix/build/public/header.js index 4ee544f..537bbdb 100644 --- a/test/test-all/mix/build/public/header.js +++ b/test/test-all/mix/build/public/header.js @@ -1,2 +1,2 @@ -/* */ +/*TMODJS:{"version":1,"md5":"19b7578ca7e4ae65953473c882a40ea7"}*/ template("public/header", ' '); \ No newline at end of file diff --git a/test/test-all/mix/build/public/logo.js b/test/test-all/mix/build/public/logo.js index 315efa7..70e01f8 100644 --- a/test/test-all/mix/build/public/logo.js +++ b/test/test-all/mix/build/public/logo.js @@ -1,2 +1,2 @@ -/* */ +/*TMODJS:{"version":1,"md5":"708e33cf506cd61b17b3ec6b7d6ad405"}*/ template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/mix/build/template.js b/test/test-all/mix/build/template.js index 3971fd0..e57f2b5 100644 --- a/test/test-all/mix/build/template.js +++ b/test/test-all/mix/build/template.js @@ -1,4 +1,4 @@ -/* */ +/*TMODJS:{}*/ (function(global) { "use strict"; var template = function(id, content) { @@ -274,6 +274,7 @@ })(this); !function(global, template) { + "use strict"; var get = template.get; var helpers = template.helpers; var resolve = function(from, to) { diff --git a/test/test-all/mix/package.json b/test/test-all/mix/package.json index b951b5e..c9255de 100644 --- a/test/test-all/mix/package.json +++ b/test/test-all/mix/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": null, "escape": true, "engine": true, - "type": "templatejs", + "type": "default", "combo": false, "minify": false } diff --git a/test/test-all/syntax-native/.debug.js b/test/test-all/syntax-native/.debug.js deleted file mode 100644 index 32373cb..0000000 --- a/test/test-all/syntax-native/.debug.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! */ -function anonymous($data,$id) {var $helpers=this,$escape=$helpers.$escape,$ubb2html=$data.$ubb2html,title=$data.title,$each=$helpers.$each,list=$data.list,$value=$data.$value,$index=$data.$index,$out='';$out+='

    '; -$out+=$escape($ubb2html title); -$out+='

    '; -return new String($out);} \ No newline at end of file diff --git a/test/test-all/syntax-native/build/template.js b/test/test-all/syntax-native/build/template.js index f1c9e1c..d07c765 100644 --- a/test/test-all/syntax-native/build/template.js +++ b/test/test-all/syntax-native/build/template.js @@ -1,3 +1,3 @@ -/* */ -!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},l=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},a=r.helpers={$include:function(e,n,t){var i=l(t,e);return r.render(i,n)},$string:t,$escape:o,$each:c},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=a,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){a[e]=r},/**/ +/*TMODJS:{"build":1388856475738}*/ +!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:c},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ r("index",function(e){var r=this,n=r.$escape,t=e.title,i=e.i,o=e.list,u="";u+='

    ',u+=n(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/test-all/syntax-native/package.json b/test/test-all/syntax-native/package.json index 7399d59..e12a325 100644 --- a/test/test-all/syntax-native/package.json +++ b/test/test-all/syntax-native/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": null, "escape": true, "engine": false, - "type": "templatejs", + "type": "default", "combo": true, "minify": true } diff --git a/test/test-all/syntax/.debug.js b/test/test-all/syntax/.debug.js deleted file mode 100644 index 32373cb..0000000 --- a/test/test-all/syntax/.debug.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! */ -function anonymous($data,$id) {var $helpers=this,$escape=$helpers.$escape,$ubb2html=$data.$ubb2html,title=$data.title,$each=$helpers.$each,list=$data.list,$value=$data.$value,$index=$data.$index,$out='';$out+='

    '; -$out+=$escape($ubb2html title); -$out+='

    '; -return new String($out);} \ No newline at end of file diff --git a/test/test-all/syntax/build/template.js b/test/test-all/syntax/build/template.js index cffa5a7..8736115 100644 --- a/test/test-all/syntax/build/template.js +++ b/test/test-all/syntax/build/template.js @@ -1,3 +1,3 @@ -/* */ -!function(e){var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},c=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+n;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},a=n.helpers={$include:function(e,r,t){var i=c(t,e);return n.render(i,r)},$string:t,$escape:o,$each:l},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,i=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(i){return f(i)()}};return i.prototype=n.prototype=a,i.toString=function(){return n+""},i},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){a[e]=n},/**/ -n("index",function(e){var n=this,r=e.x,t=n.$escape,i=e.title,o=n.$each,u=e.list,l=(e.$value,e.$index,""),r="hello world";return l+=" ",l+=t(r),l+='

    ',l+=t(i),l+="

    ",new String(l)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file +/*TMODJS:{"build":1388856469181}*/ +!function(e){"use strict";var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},l=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+n;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},a=n.helpers={$include:function(e,r,t){var i=l(t,e);return n.render(i,r)},$string:t,$escape:o,$each:c},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,i=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(i){return f(i)()}};return i.prototype=n.prototype=a,i.toString=function(){return n+""},i},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){a[e]=n},/**/ +n("index",function(e){var n=this,r=e.x,t=n.$escape,i=e.title,o=n.$each,u=e.list,c=(e.$value,e.$index,""),r="hello world";return c+=" ",c+=t(r),c+='

    ',c+=t(i),c+="

    ",new String(c)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file diff --git a/test/test-all/syntax/package.json b/test/test-all/syntax/package.json index 220ed17..1c5ac3c 100644 --- a/test/test-all/syntax/package.json +++ b/test/test-all/syntax/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4" + "tmodjs": "~0.1.0" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": null, "escape": true, "engine": false, - "type": "templatejs", + "type": "default", "combo": true, "minify": true } diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index c73310c..bc4ec09 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ -/*TMODJS:{"build":1388782270806}*/ +/*TMODJS:{"build":1388859207989}*/ !function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ r("copyright","(c) 2013"),/**/ -r("index",function(e,r){var n=this,include=function(t,i){i=i||e;var o=n.$include(t,i,r);return u+=o,o},t=n.$escape,i=e.title,o=n.$each,c=e.list,u=(e.$value,e.$index,"");return include("./public/header"),u+='

    ',u+=t(i),u+="

    ",include("./public/footer"),new String(u)}),/**/ +r("index",function(e,r){var n=this,include=function(t,i){i=i||e;var o=n.$include(t,i,r);return u+=o,o},t=n.$escape,i=e.title,o=n.$each,c=e.list,u=(e.$value,e.$index,"");return include("./public/header"),u+='

    ',u+=t(i),u+="

    ",include("./public/footer"),u+=" ",new String(u)}),/**/ r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,include=function(t,i){i=i||e;var c=n.$include(t,i,r);return o+=c,c},o="";return o+='",new String(o)}),/**/ r("public/header",function(e,r){var n=this,include=function(i,o){o=o||e;var c=n.$include(i,o,r);return t+=c,c},t="";return t+=' ',new String(t)}),/**/ r("public/logo",'

    \u817e\u8baf\u7f51

    '),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/tpl/index.html b/test/tpl/index.html index fc67525..c259213 100644 --- a/test/tpl/index.html +++ b/test/tpl/index.html @@ -9,4 +9,4 @@

    {{title}}

    -{{include './public/footer'}} \ No newline at end of file +{{include './public/footer'}} \ No newline at end of file From 7695a6ed7987eb6a8129154d354db1e5c45e2e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sun, 5 Jan 2014 02:19:38 +0800 Subject: [PATCH 12/87] v0.1.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e798ad1..982eb98 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ minify: true ### v0.1.0 * 增加自动递增的模板版本号,控制台可显示模板被修改的次数 -* 优化默认设置下输出的文件数量,仅保留``template.js``,临时文件使用隐藏的``.cache``目录存放 +* 优化默认设置下的文件输出,仅保留``template.js``,临时文件使用隐藏的``.cache``目录存放 * 自动清理``.debug.js``文件 * 对非规范的``include``语句模板在编译过程给予提示 * 减少对磁盘的读写,优化性能 From 145ae504937cc1f12a90e20401c017c33e5b4231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sun, 5 Jan 2014 04:00:50 +0800 Subject: [PATCH 13/87] v0.1.0 --- README.md | 15 ++++++++++----- doc/why-tmodjs.md | 27 +++++++++++++++++---------- src/tmod.js | 2 +- test/tpl/build/template.js | 2 +- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 982eb98..015ae51 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,17 @@ # TmodJS-前端模板编译工具 -## 关于 TmodJS - TmodJS(原名 atc)是一款前端模板编译工具,它可以让前端模板外置、实现类似后端模板一样按文件与目录组织前端模板,并且模板之间可以使用``include``语句相互包含。 +## 为什么要使用 TmodJS? + +作为开发者,我们纵然可以使用记事本来编写程序,但事实上没人这样做,因为我们被喜欢的编辑器或 IDE 迷惑,这不是仅仅是习惯问题,我们坚信一个好用的工具可以大幅度提高我们的效率。 + +对于前端开发者而言,TmodJS 也会像你的编辑器一样,一旦上手,爱不释手。 + +请继续阅读:[《进击!前端模板工程化》](http://aui.github.io/tmodjs/)。 + +## 特性 + ### 像后端一样书写前端模板 相对与前端模板,后端模板有两个优秀的特征: @@ -374,6 +382,3 @@ NodeJS 版本: * [@warmhug](https://github.com/warmhug)(在工具雏形阶段的热心的测试与反馈) --------------------- - -附:为何要创造 TmodJS?请阅读[《进击!前端模板之工程化》](http://aui.github.io/tmodjs/) \ No newline at end of file diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 07e30b4..faf8652 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -1,3 +1,4 @@ + # 进击!前端模板工程化 ## 前言 @@ -106,9 +107,9 @@ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ var template = Handlebars.templates["user.tmpl"]; var html = template(data); -预编译工具在一定程度上解决了我们的问题,但由于操作实在是太繁琐,因此它们也并没有流行起来。例如一个 web app 单页应用,几百条前端模板是常有的事儿,开发阶段我们会不断的添加、修改模板,如果每次都需要重新编译这简直会令人抓狂,与此同时大量零散的模板脚本也会引发新的问题,编译后的模板没有提供显式的依赖声明,对于大型项目来说,自动化工具依然难以介入。 +预编译工具在一定程度上解决了我们的问题,但由于操作实在是太繁琐,因此它们也并没有流行起来。前端模板因为大量的局部模板存在,相对于后端模板的一个显著特征是碎片化程度高,例如一个 web app 单页应用,几百条前端模板是常有的事儿,开发阶段我们会不断的添加、修改模板,如果每次都需要重新编译这简直会令人抓狂,与此同时大量零散的模板脚本也会引发新的问题,编译后的模板没有提供显式的依赖声明,对于大型项目来说,自动化工具依然难以介入。 -就目前而言,业界依然没有出现针对前端模板成熟的自动化工具,于是经过无数次实践、在各种方案权衡后,我们研发了全新自动化工具 —— TmodJS,为前端模板工程化而来。 +问题总会引起我们的思考,于是经过无数次实践、设计设计了一套方案试图让前端模板进入工程化时代,与此同时推出了相应的工具 —— TmodJS。 ## 工程化前端模板 @@ -151,9 +152,9 @@ TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD 总之,模板转换为 js 后不但解决了跨域部署的烦恼,其优化空间也更加灵活多样。案例: -1. 腾讯视频前端团队的实践:关闭 TmodJS 的打包合并,按照网站栏目建立模板子目录,通过 GruntJS 按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 -2. QQ 空间 CDN 有线上 SeaJS 模块动态合并服务,这时候 TmodJS 编译后的模板会被当作一个普通的 SeaJS 模块引入到项目中,当 UI 模块被调用的时候逻辑与依赖的模板都会进行动态合并加载,完全无需本地构建工具操作。 -3. MicroTrend 是腾讯内部的一个移动端单页 WebApp 项目,包含较多的模板片段,发布前每次都需要手工进行模板抽离与压缩,极其耗时。采用 TmodJS 进行模板管理,模板被打包压缩到一个 js 文件中,开发阶段输出的模板包直接作为发布后的文件,节省了发布阶段繁琐的优化环节。[查看编译后的模板](http://microtrend.cdc.tencent.com/tpl/dist/template.js) +1. **配合本地构建工具**:腾讯视频前端团队关闭 TmodJS 的打包合并,使用 GruntJS 对输出的脚本进行二次优化。按照网站栏目建立模板子目录,然后按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 +2. **后端动态压缩合并**:QQ 空间 CDN 有线上 SeaJS 模块动态合并服务,这时候 TmodJS 编译后的模板会被当作一个普通的 SeaJS 模块引入到项目中,当 UI 模块被调用的时候逻辑与依赖的模板都会进行动态合并加载,完全无需本地构建工具操作。 +3. **自带的优化手段**:MicroTrend 是腾讯内部的一个移动端单页 WebApp 小项目,采用 TmodJS 进行模板管理后,模板被打包压缩到一个 js 文件中,开发阶段输出的模板包直接作为发布后的文件,十分便捷。[查看编译后的模板](http://microtrend.cdc.tencent.com/tpl/dist/template.js) ### 4. 本地调试支持 @@ -182,7 +183,7 @@ TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD ### 5. 前后端模板共用 -一般项目,我们直接采用`` # 进击!前端模板工程化 +###### 基于 TmodJS 前端模板工程化解决方案 + ## 前言 -现在,越来越多的前端项目采用了单页或者混合式的架构,在这种架构中后端只负责吐出 JSON 数据,前端异步来渲染 HTML,前端模板被大量使用,与此同时也将引发开发维护与发布优化的问题。为了解决这些问题,我们曾经开发了工具 TmodJS(原名 atc),试图通过本地自动化工具让端模板走上工程化之路。很高兴这个工具经过长达 8 个月的磨砺后,它从一个简陋的工具变成如今健壮的前端开发“利器”,并且已经有多个大型项目在使用它。现在我将对前端模板工程化的一些思考与总结分享出来,希望能够让大家更好的理解 TmodJS 的意义所在。在主题开始之前,先简单总结下这些年前端模板技术的发展。 +现在,越来越多的前端项目采用了单页或者混合式的架构,在这种架构中后端只负责吐出 JSON 数据,前端异步来渲染 HTML,前端模板被大量使用,与此同时也将引发开发维护与发布优化的问题。为了解决这些问题,我们开发了工具 TmodJS(原名 atc),试图通过本地自动化工具让端模板走上工程化之路。很高兴这个工具经过长达 8 个月的磨砺后,它从一个简陋的工具变成如今健壮的前端开发“利器”,并且已经有多个大型项目在使用它。现在我将对前端模板工程化的一些思考与总结分享出来,希望能够让大家更好的了解 TmodJS 的意义所在。在主题开始之前,先简单总结下这些年前端模板技术的发展。 ## 手工拼接字符串时代 @@ -62,7 +64,7 @@ #### 1. Ajax 拉取方案 -通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的弊端相当明显: +通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显: 1. 无法跨域部署。这是由浏览器同源策略限制的,导致模板无法部署到 CDN 网络。 2. 复杂度比较高,难以接入主流的自动化部署、优化工具。 @@ -72,17 +74,24 @@ 为避免上述加载模板方案无法跨域的致命缺陷,模板存放在 js 文件中又成了最佳实践方式。但是 js 需要对回车符进行转义,对书写不友好,例如: var user_tmpl = - '<% for (var i = 0; i < users.length; i++) { %>\ + '{{each users as value}}\
  • \ - \ - <%=users[i].name%>\ - \ + {{value.name}}\
  • \ - <% } %>'; + {{/each}}'; + +或者: + + var user_tmpl = + '{{each users as value}}' + + '
  • ' + + '{{value.name}}' + + '
  • ' + +'{{/each}}'; ### 模板存放方案优劣总结 -存放方式 | 书写友好 | CDN 跨域部署 | 本地调试 | 代码复用 | 按需加载 +存放方式 | 书写友好 | CDN 部署 | 本地调试 | 代码复用 | 按需加载 ------ | ------ | ------ | ------ | ------ | ------ | ------ 内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ @@ -90,11 +99,11 @@ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ 在实践中我们发现:方便优化的模式不利于开发;利于开发的模式不利于优化。 -为了解决上述问题,业界先后出现了一些工具,如 handlebars.js 与 Hogan.js(来自 Twitter) 采用了预编译技术来完成模板到 js 的转换,以 handlebars.js 使用为例,先使用 NodeJS 安装它: +业界后来出现了一些工具试图解决上述问题,如 Handlebars.js 与 Hogan.js(来自 Twitter) 采用了预编译技术来完成模板到 js 的转换,以 Handlebars.js 使用为例,先使用 NodeJS 安装它: $ npm install -g handlebars -然后提取模板内容(``script`` 标签之间)并保存到一个文件中。在这里我们把它保存为 ``user.tmpl``。运行 ``handlebars`` 命令预编译这个模板文件。 +然后提取模板内容(``script``标签之间)并保存到一个文件中。在这里我们把它保存为 ``user.tmpl``。运行 ``handlebars`` 命令预编译这个模板文件。 $ handlebars user.tmpl -f user.tmpl.js @@ -109,15 +118,15 @@ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ 预编译工具在一定程度上解决了我们的问题,但由于操作实在是太繁琐,因此它们也并没有流行起来。前端模板因为大量的局部模板存在,相对于后端模板的一个显著特征是碎片化程度高,例如一个 web app 单页应用,几百条前端模板是常有的事儿,开发阶段我们会不断的添加、修改模板,如果每次都需要重新编译这简直会令人抓狂,与此同时大量零散的模板脚本也会引发新的问题,编译后的模板没有提供显式的依赖声明,对于大型项目来说,自动化工具依然难以介入。 -问题总会引起我们的思考,于是经过无数次实践、设计设计了一套方案试图让前端模板进入工程化时代,与此同时推出了相应的工具 —— TmodJS。 +于是,在这种情况下针对前端模板开发的全新工具 —— TmodJS 顺势而生,只为工程化而来。 ## 工程化前端模板 -TmodJS 采用一系列集成方案来最大化提升前端工程效率与质量,本地预编译技术的运用使得我们不必局限于浏览器的技术限制,从而让我们更多的想法通过工具来执行。 +TmodJS 采用一系列集成方案来最大化提升前端模板开发的效率与质量,本地预编译技术的运用使得我们不必局限于浏览器的技术限制,从而让我们更多的想法通过工具来执行,让前端模板可以大规模使用,从而创造更好的用户体验。 ### 1. 基于文件系统 -在 TmodJS 的规范中,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系 —— 这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在 tpl/public 目录下,新闻栏目的模板可以放在 tpl/news 下面。 +在 TmodJS 的规范中,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系 —— 这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在``tpl/public``目录下,首屏的模板可以放在``tpl/home``下面。 同时,模板内部也支持``include``语句来引入子模板,实现模块复用。例如: @@ -129,30 +138,35 @@ TmodJS 采用一系列集成方案来最大化提升前端工程效率与质量 ### 2. 使用同步加载接口 -TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD 规范实现,从而避免浏览器的异步加载带来的各种问题,如网络速度、回调套嵌等。例如加载模板 news/index.html: +TmodJS 的同步接口是通过通过预先合并或者使用 AMD、CMD、CommonJS 规范实现,从而避免浏览器的异步加载带来的各种问题,如网络速度、回调套嵌等。 - var tpl = template('news/index'); +例如加载模板``home/index.html``,如果编译为默认类型的模块,使用如下加载方式: -模板渲染方式: - + var tpl = template('home/index'); + var html = tpl(data); + document.getElementById('content').innerHTML = html; + +如果编译为 AMD、CMD、CommonJS 类型的模块,每个模板都是一个标准模块: + + var tpl = requier('./tpl/home/index'); var html = tpl(data); document.getElementById('content').innerHTML = html; -### 3. 自动化执行优化 +### 3. 构建自动化 -在默认设置下,TmodJS 会将模板目录所有模板编译后再进行压缩与合并,输出后的 template.js 称之为模板包(内部名称叫 TemplateJS 格式)这种打包的方式非常适合移动端单页 WebApp,输出后的模板包可直接可作为开发阶段与线上运行的文件。 +#### 内置打包合并 -当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行优化,例如大名鼎鼎的 GruntJS。除此之外,使用 RequireJS 与 SeaJS 或者 NodeJS 的用户还可以通过配置将每一个模板都编译成与它们的模块,这时候模板加载的接口是这样的: +在默认设置下,TmodJS 会将模板目录所有模板编译后再进行压缩与合并,输出后的 template.js 称之为模板包(内部名称叫 TemplateJS 格式)这种打包的方式非常适合移动端单页 WebApp,输出后的模板包可直接可作为开发阶段与线上运行的文件,适合中小型项目。 - var tpl = requier('tpl/news/index'); - var html = tpl(data); - //... - -这种情况下模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 +#### 配合外部工具 + +当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行按需打包合并(例如 GruntJS)。 + +除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 -总之,模板转换为 js 后不但解决了跨域部署的烦恼,其优化空间也更加灵活多样。案例: +总之,模板转换为 js 后不但解决了跨域部署的烦恼,其优化手段也更加灵活多样。案例: -1. **配合本地构建工具**:腾讯视频前端团队关闭 TmodJS 的打包合并,使用 GruntJS 对输出的脚本进行二次优化。按照网站栏目建立模板子目录,然后按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 +1. **配合本地构建工具**:腾讯视频前端团队了关闭 TmodJS 的打包合并,让 TmodJS 接入 GruntJS,让 GruntJS 对 TmodJS 输出的脚本进行构建:按照网站栏目建立模板子目录,然后按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 2. **后端动态压缩合并**:QQ 空间 CDN 有线上 SeaJS 模块动态合并服务,这时候 TmodJS 编译后的模板会被当作一个普通的 SeaJS 模块引入到项目中,当 UI 模块被调用的时候逻辑与依赖的模板都会进行动态合并加载,完全无需本地构建工具操作。 3. **自带的优化手段**:MicroTrend 是腾讯内部的一个移动端单页 WebApp 小项目,采用 TmodJS 进行模板管理后,模板被打包压缩到一个 js 文件中,开发阶段输出的模板包直接作为发布后的文件,十分便捷。[查看编译后的模板](http://microtrend.cdc.tencent.com/tpl/dist/template.js) @@ -162,7 +176,7 @@ TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD 无论如何模板最终都会转换成 js,亦可使用 Fiddler 将线上模板映射到本地进行开发调试;如果开启实时编译,开发阶段模板修改后只需要刷新浏览器即可预览到效果。 -通常在模板开发阶段会经常遇到数据与预期不符合的情况,TmodJS 编译后的模板还支持运行时调试,可以在控制台输出报错信息,并且可精确到模板所在行: +通常在模板开发阶段会经常遇到数据与预期不符合的情况,前端模板调试成了一个棘手的问题,于是 TmodJS 支持编译调试版本,这样可以在运行时进行调试,控制台可以定位到出错模板所在的行: Template Error @@ -183,9 +197,7 @@ TmodJS 的同步接口是通过通过打包合并模板或者使用 AMD、CMD ### 5. 前后端模板共用 -前面提到,TmodJS 默认设置下会输出一个包含所有模板的模板包 template.js,这个文件可以兼容多种模块格式,除了通过脚本直接加载还可以使用 RequierJS、SeaJS、NodeJS 加载。 - -RequierJS 的模块规范是 AMD,SeaJS 的模块是 CMD,而 NodeJS 的模块规范是 CommonJS —— 这几种规范有很多共同点,很容易进行兼容,这是模板包内部的实现方式: +前面提到,TmodJS 默认设置下会输出一个包含所有模板的模板包 template.js,这个文件可以兼容多种模块格式,除了通过`` @@ -262,8 +262,10 @@ minify: true ### v0.1.1 +* 修复无逻辑的模板在 Safari 浏览器上兼容问题 * 默认开启模板实时监控。取消请使用``--watch-off`` * 给压缩打包合并后的每条模板增加版本标记,例如``/*v:13*/``,以便对比线上版本 +* 增加``compileStart``与``compileEnd``事件 ### v0.1.0 diff --git a/bin/tmod b/bin/tmod index ee51798..e13c9b2 100644 --- a/bin/tmod +++ b/bin/tmod @@ -15,7 +15,7 @@ var options = {}; var help = function () { var message = [ - '\x1B[7mTmodJS - Template Compiler\x1B[27m', + '\x1B[7mTmodJS\x1B[27m - Template Compiler', '', 'Usage:', ' tmod [path] [options]', @@ -60,7 +60,7 @@ var help = function () { var dir; var value; var userConfig; -var isWatch = false; +var isWatch = true; var isEditConfig = false; var args = process.argv.slice(2); diff --git a/src/runtime/basic.js b/src/runtime/basic.js index df7cf7c..af39e12 100644 --- a/src/runtime/basic.js +++ b/src/runtime/basic.js @@ -132,7 +132,12 @@ } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + + if (isFunction) { + fn.prototype = helpers; + } + render.toString = function () { return fn + ''; }; diff --git a/src/stdout.js b/src/stdout.js index f7b10ad..c799ff9 100644 --- a/src/stdout.js +++ b/src/stdout.js @@ -29,14 +29,19 @@ styles['b'] = styles['bold']; styles['i'] = styles['italic']; styles['u'] = styles['underline']; -module.exports = function (message) { +var stdout = function (message) { message = message.replace(/\[([^\]]*?)\]/igm, function ($1, $2) { - return $2.indexOf('/') === 0 - ? styles[$2.slice(1)][1] - : styles[$2][0]; + if (styles[$2.replace('/', '')]) { + return $2.indexOf('/') === 0 + ? styles[$2.slice(1)][1] + : styles[$2][0]; + } else { + return $1; + } }); - process.stdout.write(message); }; +module.exports = stdout; + diff --git a/src/tmod.js b/src/tmod.js index 4ee3d2c..bd7e80c 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -150,7 +150,7 @@ module.exports = { } if (a > b) { - stdout('[red]You must upgrade to the latest version of TmodJS![/red]\n'); + console.error('You must upgrade to the latest version of TmodJS!'); process.exit(1); } })(json.dependencies.tmodjs, version); @@ -459,21 +459,27 @@ module.exports = { // 打包模板 _combo: function () { + var that = this; var files = []; var mod = null; var combo = ''; - var cache = this._getCache(); - var code = ''; var build = Date.now(); - for (var i in cache) { + this._getCacheKeys().forEach(function (id) { + var code = that._getCache(id, 'output'); + + // 如果之前编译失败,可能没有缓存值 + // TODO: 这里写兼容逻辑有点猥琐,待改进 + if (!code) { + if (that._compile(id)) {} + code = that._getCache(id, 'output'); + } - code = cache[i]; - code = this._removeMetadata(code); + code = that._removeMetadata(code); combo += code; - files.push(i); - } + files.push(id); + }); mod = this._buildRuntime(combo, { debug: this.options.debug, @@ -505,6 +511,8 @@ module.exports = { */ watch: function () { + this.emit('watchStart', {}); + // 监控模板目录 this.on('watch', function (data) { @@ -573,7 +581,7 @@ module.exports = { .replace(EXTNAME_RE, '.js') .replace(this.base, this.output); - var mod = this._getCache(file); + var mod = this._getCache(file, 'output'); var modObject = {}; var metadata = {}; var count = 0; @@ -767,7 +775,11 @@ module.exports = { } // 缓存编译好的模板 - this._setCache(file, mod); + this._setCache(file, 'output', mod); + + // 缓存源文件 + this._setCache(file, 'source', source); + } @@ -789,13 +801,16 @@ module.exports = { var that = this; var error = false; - var walk; + var length = 0; + + this.emit('compileStart', {}); + if (file) { var extname = path.extname(file); - walk = function (list) { + var walk = function (list) { list.forEach(function (file) { @@ -807,16 +822,18 @@ module.exports = { error = !info; - - if (!error && recursion !== false && info.requires.length) { + if (!error) { + length ++; + if (!error && recursion !== false && info.requires.length) { - list = info.requires.map(function (id) { - var target = path.resolve(that.base, id + extname); - return target; - }); + list = info.requires.map(function (id) { + var target = path.resolve(that.base, id + extname); + return target; + }); - walk(list); + walk(list); + } } }); @@ -830,38 +847,28 @@ module.exports = { } else { + var list = this._getCacheKeys(); - walk = function (dir) { - - if (dir === that.output) { - return; + for (var i = 0; i < list.length; i ++) { + if (error = !that._compile(list[i])) { + break; + } else { + length ++; } - - var dirList = fs.readdirSync(dir); - - dirList.forEach(function (item) { - - if (error) { - return; - } - - if (fs.statSync(dir + '/' + item).isDirectory()) { - walk(dir + '/' + item); - } else if (that._filter(item)) { - error = !that._compile(dir + '/' + item); - } - - }); - }; - - - walk(this.base); + } if (!error && this.options.combo) { this._combo(); } + } + + this.emit('compileEnd', { + error: error, + length: length + }); + }, @@ -871,28 +878,96 @@ module.exports = { }, - _cache: {}, - - // 获取缓存 - _getCache: function (id) { + _getCache: function (id, name) { + var cache = this._cache; + if (typeof id === 'undefined') { - return this._cache; + return cache; } else { - return this._cache[id]; + cache = cache[id]; + + if (typeof name === 'undefined') { + return cache; + } else if (cache) { + return cache[name]; + } } }, + // 获取所有缓存 id + _getCacheKeys: function () { + return Object.keys(this._cache); + }, + + // 设置缓存 - _setCache: function (id, data) { - this._cache[id] = data; + _setCache: function (id, name, data) { + var cache = this._cache; + + if (typeof data === 'undefined') { + data = name; + cache[id] = data; + } else { + + cache = cache[id]; + if (!cache) { + cache = this._cache[id] = {}; + } + + cache[name] = data; + } }, // 删除缓存 - _removeCache: function (id) { - delete this._cache[id]; + _removeCache: function (id, name) { + var cache = this._cache; + + if (typeof name === 'undefined') { + delete cache[id]; + } else if (cache[id]) { + delete cache[id][name]; + } + }, + + + // 清除全部缓存 + _clearCache: function () { + this._cache = {}; + }, + + + // 初始化缓存 + _initCache: function () { + var that = this; + var cache = this._cache = {}; + var charset = this.options.charset; + var bo = this.base === this.output; + + var walk = function (dir) { + + if (!bo && dir === that.output) { + return; + } + + var dirList = fs.readdirSync(dir); + + dirList.forEach(function (item) { + + if (fs.statSync(dir + '/' + item).isDirectory()) { + walk(dir + '/' + item); + } else if (that._filter(item)) { + var source = fs.readFileSync(dir + '/' + item, charset); + that._setCache(dir + '/' + item, 'source', source); + } + + }); + }; + + + walk(this.base); }, @@ -966,6 +1041,9 @@ module.exports = { init: function (input, options) { + stdout('[inverse]TmodJS[/inverse] - Template Compiler' + '\n'); + stdout('[grey]http://aui.github.io/tmodjs[/grey]\n'); + // 模板项目路径 this.base = path.resolve(input); @@ -987,6 +1065,14 @@ module.exports = { this._initEngine(); + // 初始化缓存系统 + this._initCache(); + + + // 输出运行时 + this._buildRuntime(); + + // 初始化事件系统 events.EventEmitter.call(this); @@ -996,8 +1082,6 @@ module.exports = { if (watch && event === 'watch') { - stdout('\n[green]Waiting..[/green]\n\n'); - watch(this.base, function (data) { this.emit('watch', data); }.bind(this), function (name) { @@ -1013,6 +1097,13 @@ module.exports = { }); + this.on('watchStart', function () { + setTimeout(function () { + stdout('\n[green]Watching..[/green] [grey](quit watch: control + c)[/grey]\n\n'); + }, 140); + }); + + // 监听模板修改事件 this.on('change', function (data) { var time = (new Date).toLocaleTimeString(); @@ -1056,7 +1147,7 @@ module.exports = { stdout('[red]' + data.line + ': ' + data.source + '[/red]\n'); } - stdout('[red]' + data.message + '[/red]\n'); + stdout('[red]' + data.message + '[/red]\n\n'); }); @@ -1064,16 +1155,9 @@ module.exports = { // this.on('combo', function (data) { // stdout('[grey]»[/grey] '); // stdout('[grey]' + RUNTIME + '.js[/grey]'); - // stdout(' [grey]:build' + data.version + '[/grey]'); // stdout('\n'); // }); - // 输出运行时 - this._buildRuntime(); - - - stdout('[grey]TmodJS - Template Compiler' + '[/grey]\n'); - } }; diff --git a/test/test-all/amd/build/copyright.js b/test/test-all/amd/build/copyright.js index b0b5799..5b21daa 100644 --- a/test/test-all/amd/build/copyright.js +++ b/test/test-all/amd/build/copyright.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"1e854fa382ff15601ae476b5e4bda572"}*/ +/*TMODJS:{"version":2,"md5":"2a0b01b76d901ee255c2ec43685dcd32"}*/ define([ "./template", "" ], function(template) { return template("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/amd/build/index.js b/test/test-all/amd/build/index.js index cc7adcb..7a61daf 100644 --- a/test/test-all/amd/build/index.js +++ b/test/test-all/amd/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"f911f5d9484d6d439bd92b74059a3dc3"}*/ +/*TMODJS:{"version":2,"md5":"afe25510dffe1a37249f495a80e58d4d"}*/ define([ "./template", "./public/header", "./public/footer" ], function(template) { return template("index", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/amd/build/public/footer.js b/test/test-all/amd/build/public/footer.js index 5e28caa..eadd154 100644 --- a/test/test-all/amd/build/public/footer.js +++ b/test/test-all/amd/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"6599b97b0326fe559d5c1fbef543716a"}*/ +/*TMODJS:{"version":2,"md5":"e616712eabe752419ca04ccf713b3eb8"}*/ define([ "../template", "../copyright" ], function(template) { return template("public/footer", function($data, $id) { var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { diff --git a/test/test-all/amd/build/public/header.js b/test/test-all/amd/build/public/header.js index 5e128c5..5112da8 100644 --- a/test/test-all/amd/build/public/header.js +++ b/test/test-all/amd/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"69b5fd873be0e48445f3c880438bdb9e"}*/ +/*TMODJS:{"version":2,"md5":"52de152a10a5d1fe4fb72047dcde1fae"}*/ define([ "../template", "./logo" ], function(template) { return template("public/header", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/amd/build/public/logo.js b/test/test-all/amd/build/public/logo.js index 487162b..3f955ba 100644 --- a/test/test-all/amd/build/public/logo.js +++ b/test/test-all/amd/build/public/logo.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"408d70188cd7876ae67f9bcd8198c05f"}*/ +/*TMODJS:{"version":2,"md5":"72f6a9510f0a61ce6841335c94b7f79e"}*/ define([ "../template", "" ], function(template) { return template("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/amd/build/template.js b/test/test-all/amd/build/template.js index 92ea0d9..9b82a24 100644 --- a/test/test-all/amd/build/template.js +++ b/test/test-all/amd/build/template.js @@ -1,6 +1,7 @@ -/*TMODJS:{"build":1388773675300}*/ +/*TMODJS:{}*/ !function(global) { - var template = function(path, content) { + "use strict"; + var template = function(uri, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; var cache = template.cache = {}; @@ -54,8 +55,8 @@ return id; }; var helpers = template.helpers = { - $include: function(path, data, from) { - var id = resolve(from, path); + $include: function(uri, data, from) { + var id = resolve(from, uri); return template.render(id, data); }, $string: toString, @@ -74,24 +75,27 @@ return "{Template Error}"; }; }; - template.render = function(path, data) { - var fn = template.get(path) || debug({ - id: path, + template.render = function(uri, data) { + var fn = template.get(uri) || debug({ + id: uri, name: "Render Error", message: "No Template" }); return data ? fn(data) : fn; }; - template.compile = function(path, fn) { + template.compile = function(uri, fn) { var isFunction = typeof fn === "function"; - var render = cache[path] = function(data) { + var render = cache[uri] = function(data) { try { - return isFunction ? new fn(data, path) + "" : fn; + return isFunction ? new fn(data, uri) + "" : fn; } catch (e) { return debug(e)(); } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } render.toString = function() { return fn + ""; }; diff --git a/test/test-all/amd/package.json b/test/test-all/amd/package.json index be1938e..95b6633 100644 --- a/test/test-all/amd/package.json +++ b/test/test-all/amd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4-rc2" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/cmd-alias/build/copyright.js b/test/test-all/cmd-alias/build/copyright.js index bc7f744..1ab1f52 100644 --- a/test/test-all/cmd-alias/build/copyright.js +++ b/test/test-all/cmd-alias/build/copyright.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":2,"md5":"5243be670cc6ac7a89ecc13b61baafe8"}*/ +/*TMODJS:{"version":3,"md5":"44ff14b2d5af97a5ebfda8fcfdae78a4"}*/ define(function(require) { return require("template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/index.js b/test/test-all/cmd-alias/build/index.js index 309ad3c..43fe7be 100644 --- a/test/test-all/cmd-alias/build/index.js +++ b/test/test-all/cmd-alias/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":2,"md5":"af2378bf3578c140775c9fec52ba4b82"}*/ +/*TMODJS:{"version":3,"md5":"c528a99a659ed7881970baa491009734"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/cmd-alias/build/public/footer.js b/test/test-all/cmd-alias/build/public/footer.js index 53799f3..dd27e92 100644 --- a/test/test-all/cmd-alias/build/public/footer.js +++ b/test/test-all/cmd-alias/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":2,"md5":"91b0bfeee484fd1caf31daf8bf403b1c"}*/ +/*TMODJS:{"version":3,"md5":"fdd204cf36eb75c25aa5d1d3d4eb2c13"}*/ define(function(require) { require("../copyright"); return require("template")("public/footer", function($data, $id) { diff --git a/test/test-all/cmd-alias/build/public/header.js b/test/test-all/cmd-alias/build/public/header.js index c5d6ce2..0c5fe89 100644 --- a/test/test-all/cmd-alias/build/public/header.js +++ b/test/test-all/cmd-alias/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":2,"md5":"572809eb7da16c9258e7ae64bf984cd8"}*/ +/*TMODJS:{"version":3,"md5":"d84efb73184f7a829860aab51abf6505"}*/ define(function(require) { require("./logo"); return require("template")("public/header", function($data, $id) { diff --git a/test/test-all/cmd-alias/build/public/logo.js b/test/test-all/cmd-alias/build/public/logo.js index ee2e10f..3fc23d5 100644 --- a/test/test-all/cmd-alias/build/public/logo.js +++ b/test/test-all/cmd-alias/build/public/logo.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":2,"md5":"cb705b90a4f3350cd8900497094ecf82"}*/ +/*TMODJS:{"version":3,"md5":"69737bc019577c0efb526b2dc6f50094"}*/ define(function(require) { return require("template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/cmd-alias/build/template.js b/test/test-all/cmd-alias/build/template.js index a00bef0..9b82a24 100644 --- a/test/test-all/cmd-alias/build/template.js +++ b/test/test-all/cmd-alias/build/template.js @@ -92,7 +92,10 @@ return debug(e)(); } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } render.toString = function() { return fn + ""; }; diff --git a/test/test-all/cmd-alias/package.json b/test/test-all/cmd-alias/package.json index 6e37d51..001ac5d 100644 --- a/test/test-all/cmd-alias/package.json +++ b/test/test-all/cmd-alias/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0-rc2" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/cmd/build/copyright.js b/test/test-all/cmd/build/copyright.js index 0b673b6..dc2e86e 100644 --- a/test/test-all/cmd/build/copyright.js +++ b/test/test-all/cmd/build/copyright.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"5ec159b8fdc1336b504bb8b4b98c95aa"}*/ +/*TMODJS:{"version":2,"md5":"a099d41c7a737c7fa6cd2be2e84ce786"}*/ define(function(require) { return require("./template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/cmd/build/index.js b/test/test-all/cmd/build/index.js index 332a31b..0349b0f 100644 --- a/test/test-all/cmd/build/index.js +++ b/test/test-all/cmd/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"80d169ac84706a2be199526f5aaf7fba"}*/ +/*TMODJS:{"version":2,"md5":"cf5668e932b9de5ab3be84b65addd31c"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/cmd/build/public/footer.js b/test/test-all/cmd/build/public/footer.js index 8ca480d..a2daadb 100644 --- a/test/test-all/cmd/build/public/footer.js +++ b/test/test-all/cmd/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"6806db482b03336ae6e90e735c9acb58"}*/ +/*TMODJS:{"version":2,"md5":"8084a2fec9437fffc94ff34c904352d2"}*/ define(function(require) { require("../copyright"); return require("../template")("public/footer", function($data, $id) { diff --git a/test/test-all/cmd/build/public/header.js b/test/test-all/cmd/build/public/header.js index 94f5a4d..43739aa 100644 --- a/test/test-all/cmd/build/public/header.js +++ b/test/test-all/cmd/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"13a6961d3b34f888c13557b3a8cead25"}*/ +/*TMODJS:{"version":2,"md5":"11ce602a0d05f5cb5e78f53f827c7729"}*/ define(function(require) { require("./logo"); return require("../template")("public/header", function($data, $id) { diff --git a/test/test-all/cmd/build/public/logo.js b/test/test-all/cmd/build/public/logo.js index 761d813..bfffeba 100644 --- a/test/test-all/cmd/build/public/logo.js +++ b/test/test-all/cmd/build/public/logo.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"6552b917201e4947f819e3d0b2fcf207"}*/ +/*TMODJS:{"version":2,"md5":"64e4b483123e0de36d84a49c9a3cd055"}*/ define(function(require) { return require("../template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/cmd/build/template.js b/test/test-all/cmd/build/template.js index acc7cbf..9b82a24 100644 --- a/test/test-all/cmd/build/template.js +++ b/test/test-all/cmd/build/template.js @@ -1,6 +1,7 @@ -/*TMODJS:{"build":1388773704118}*/ +/*TMODJS:{}*/ !function(global) { - var template = function(path, content) { + "use strict"; + var template = function(uri, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; var cache = template.cache = {}; @@ -54,8 +55,8 @@ return id; }; var helpers = template.helpers = { - $include: function(path, data, from) { - var id = resolve(from, path); + $include: function(uri, data, from) { + var id = resolve(from, uri); return template.render(id, data); }, $string: toString, @@ -74,24 +75,27 @@ return "{Template Error}"; }; }; - template.render = function(path, data) { - var fn = template.get(path) || debug({ - id: path, + template.render = function(uri, data) { + var fn = template.get(uri) || debug({ + id: uri, name: "Render Error", message: "No Template" }); return data ? fn(data) : fn; }; - template.compile = function(path, fn) { + template.compile = function(uri, fn) { var isFunction = typeof fn === "function"; - var render = cache[path] = function(data) { + var render = cache[uri] = function(data) { try { - return isFunction ? new fn(data, path) + "" : fn; + return isFunction ? new fn(data, uri) + "" : fn; } catch (e) { return debug(e)(); } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } render.toString = function() { return fn + ""; }; diff --git a/test/test-all/cmd/package.json b/test/test-all/cmd/package.json index 8495ebf..486de9d 100644 --- a/test/test-all/cmd/package.json +++ b/test/test-all/cmd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4-rc2" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/combo-off/build/copyright.js b/test/test-all/combo-off/build/copyright.js index a2bd907..19c20fe 100644 --- a/test/test-all/combo-off/build/copyright.js +++ b/test/test-all/combo-off/build/copyright.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"4487a23c46554f983d0d65b853f2d425"}*/ +/*TMODJS:{"version":2,"md5":"e520dc8b2452eb53d2979169a72533e0"}*/ template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/combo-off/build/index.js b/test/test-all/combo-off/build/index.js index 205fbe0..e109592 100644 --- a/test/test-all/combo-off/build/index.js +++ b/test/test-all/combo-off/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"b0a98aa9248087cee2026cc4239e4f41"}*/ +/*TMODJS:{"version":2,"md5":"389e1e681e44404576459aa4e5be3cbe"}*/ template("index", function($data, $id) { var $helpers = this, include = function(id, data) { data = data || $data; diff --git a/test/test-all/combo-off/build/public/footer.js b/test/test-all/combo-off/build/public/footer.js index f6e3825..5db6ec9 100644 --- a/test/test-all/combo-off/build/public/footer.js +++ b/test/test-all/combo-off/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"56f427d7513a75362140b92ae977ce11"}*/ +/*TMODJS:{"version":2,"md5":"ec1bad2919c1f5938a2ddac95d097743"}*/ template("public/footer", function($data, $id) { var $helpers = this, time = $data.time, $escape = $helpers.$escape, include = function(id, data) { data = data || $data; diff --git a/test/test-all/combo-off/build/public/header.js b/test/test-all/combo-off/build/public/header.js index 3c6de11..a0789f7 100644 --- a/test/test-all/combo-off/build/public/header.js +++ b/test/test-all/combo-off/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"1a90867e48c355a45f2d512c6e14dfb4"}*/ +/*TMODJS:{"version":2,"md5":"f8adad2caf2862b5683b6b58d1c79664"}*/ template("public/header", function($data, $id) { var $helpers = this, include = function(id, data) { data = data || $data; diff --git a/test/test-all/combo-off/build/public/logo.js b/test/test-all/combo-off/build/public/logo.js index 72833e9..16938da 100644 --- a/test/test-all/combo-off/build/public/logo.js +++ b/test/test-all/combo-off/build/public/logo.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"b76a88eb7083ef9c0a8c9466130e1b96"}*/ +/*TMODJS:{"version":2,"md5":"4068c97d17d4257e31c784123dd379df"}*/ template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/combo-off/build/template.js b/test/test-all/combo-off/build/template.js index 9dc17a5..9b82a24 100644 --- a/test/test-all/combo-off/build/template.js +++ b/test/test-all/combo-off/build/template.js @@ -1,6 +1,7 @@ -/*TMODJS:{"build":1388773720011}*/ +/*TMODJS:{}*/ !function(global) { - var template = function(path, content) { + "use strict"; + var template = function(uri, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; var cache = template.cache = {}; @@ -54,8 +55,8 @@ return id; }; var helpers = template.helpers = { - $include: function(path, data, from) { - var id = resolve(from, path); + $include: function(uri, data, from) { + var id = resolve(from, uri); return template.render(id, data); }, $string: toString, @@ -74,24 +75,27 @@ return "{Template Error}"; }; }; - template.render = function(path, data) { - var fn = template.get(path) || debug({ - id: path, + template.render = function(uri, data) { + var fn = template.get(uri) || debug({ + id: uri, name: "Render Error", message: "No Template" }); return data ? fn(data) : fn; }; - template.compile = function(path, fn) { + template.compile = function(uri, fn) { var isFunction = typeof fn === "function"; - var render = cache[path] = function(data) { + var render = cache[uri] = function(data) { try { - return isFunction ? new fn(data, path) + "" : fn; + return isFunction ? new fn(data, uri) + "" : fn; } catch (e) { return debug(e)(); } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } render.toString = function() { return fn + ""; }; diff --git a/test/test-all/combo-off/package.json b/test/test-all/combo-off/package.json index a0ae45a..8671151 100644 --- a/test/test-all/combo-off/package.json +++ b/test/test-all/combo-off/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.0.4-rc2" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", @@ -11,7 +11,7 @@ "helpers": null, "escape": true, "engine": false, - "type": "templatejs", + "type": "default", "combo": false, "minify": false } diff --git a/test/test-all/escape-off/build/template.js b/test/test-all/escape-off/build/template.js index 705628d..af39c50 100644 --- a/test/test-all/escape-off/build/template.js +++ b/test/test-all/escape-off/build/template.js @@ -1,3 +1,3 @@ -/*TMODJS:{"build":1388858518985}*/ -!function(n){"use strict";var e=function(n,r){return e[/string|function/.test(typeof r)?"compile":"render"].apply(e,arguments)},r=e.cache={},t=function(n,e){return"string"!=typeof n&&(e=typeof n,"number"===e?n+="":n="function"===e?t(n.call(n)):""),n},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(n){return t(n).replace(/&(?![\w#]+;)|[<>"']/g,function(n){return i[n]})},u=Array.isArray||function(n){return"[object Array]"==={}.toString.call(n)},c=function(n,e){if(u(n))for(var r=0,t=n.length;t>r;r++)e.call(n,n[r],r,n);else for(r in n)e.call(n,n[r],r)},a=function(n,e){var r=/(\/)[^/]+\1\.\.\1/,t=n.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+e;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},l=e.helpers={$include:function(n,r,t){var i=a(t,n);return e.render(i,r)},$string:t,$escape:o,$each:c},f=function(e){var r="";for(var t in e)r+="<"+t+">\n"+e[t]+"\n\n";return r&&n.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};e.render=function(n,r){var t=e.get(n)||f({id:n,name:"Render Error",message:"No Template"});return r?t(r):t},e.compile=function(n,e){var t="function"==typeof e,i=r[n]=function(r){try{return t?new e(r,n)+"":e}catch(i){return f(i)()}};return i.prototype=e.prototype=l,i.toString=function(){return e+""},i},e.get=function(n){return r[n.replace(/^\.\//,"")]},e.helper=function(n,e){l[n]=e},/**/ +/*TMODJS:{"build":1390231427879}*/ +!function(n){"use strict";var e=function(n,r){return e[/string|function/.test(typeof r)?"compile":"render"].apply(e,arguments)},r=e.cache={},t=function(n,e){return"string"!=typeof n&&(e=typeof n,"number"===e?n+="":n="function"===e?t(n.call(n)):""),n},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(n){return t(n).replace(/&(?![\w#]+;)|[<>"']/g,function(n){return i[n]})},u=Array.isArray||function(n){return"[object Array]"==={}.toString.call(n)},c=function(n,e){if(u(n))for(var r=0,t=n.length;t>r;r++)e.call(n,n[r],r,n);else for(r in n)e.call(n,n[r],r)},a=function(n,e){var r=/(\/)[^/]+\1\.\.\1/,t=n.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+e;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},f=e.helpers={$include:function(n,r,t){var i=a(t,n);return e.render(i,r)},$string:t,$escape:o,$each:c},l=function(e){var r="";for(var t in e)r+="<"+t+">\n"+e[t]+"\n\n";return r&&n.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};e.render=function(n,r){var t=e.get(n)||l({id:n,name:"Render Error",message:"No Template"});return r?t(r):t},e.compile=function(n,e){var t="function"==typeof e,i=r[n]=function(r){try{return t?new e(r,n)+"":e}catch(i){return l(i)()}};return i.prototype=f,t&&(e.prototype=f),i.toString=function(){return e+""},i},e.get=function(n){return r[n.replace(/^\.\//,"")]},e.helper=function(n,e){f[n]=e},/*v:2*/ e("index",function(n){var e=this,r=e.$string,t=n.title,i=e.$each,o=n.list,u=(n.$value,n.$index,"");return u+='

    ',u+=r(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:n.template=e}(this); \ No newline at end of file diff --git a/test/test-all/escape-off/package.json b/test/test-all/escape-off/package.json index ce9947c..2798a44 100644 --- a/test/test-all/escape-off/package.json +++ b/test/test-all/escape-off/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/helper/build/index.js b/test/test-all/helper/build/index.js deleted file mode 100644 index d5a1d7e..0000000 --- a/test/test-all/helper/build/index.js +++ /dev/null @@ -1,2 +0,0 @@ -/* */ -template("index",function(i){var t=this,e=t.$string,l=t.$ubb2html,n=i.title,a=t.$each,u=i.list,r=(i.$value,i.$index,t.$escape),h="";return h+='

    ',h+=e(l(n)),h+="

    ",new String(h)}); \ No newline at end of file diff --git a/test/test-all/helper/build/template.js b/test/test-all/helper/build/template.js index badd02d..dae50af 100644 --- a/test/test-all/helper/build/template.js +++ b/test/test-all/helper/build/template.js @@ -1,3 +1,138 @@ -/* */ -!function(e){var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},u=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},l=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},o=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},a=r.helpers={$include:function(e,n,t){var i=o(t,e);return r.render(i,n)},$string:t,$escape:u,$each:l},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=a,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){a[e]=r},r.helper("$ubb2html",function(e){return e=r.helpers.$escape(e),e.replace(/\[b\]([^\[]*?)\[\/b\]/gim,"$1").replace(/\[i\]([^\[]*?)\[\/i\]/gim,"$1").replace(/\[u\]([^\[]*?)\[\/u\]/gim,"$1").replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/gim,'$2').replace(/\[img\]([^\[]*?)\[\/img\]/gim,'')}),/**/ -r("index",function(e){var r=this,n=r.$string,t=r.$ubb2html,i=e.title,u=r.$each,c=e.list,l=(e.$value,e.$index,r.$escape),o="";return o+='

    ',o+=n(t(i)),o+="

    ",new String(o)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/*TMODJS:{"build":1390231456444}*/ +!function(global) { + "use strict"; + var template = function(uri, content) { + return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); + }; + var cache = template.cache = {}; + var toString = function(value, type) { + if (typeof value !== "string") { + type = typeof value; + if (type === "number") { + value += ""; + } else if (type === "function") { + value = toString(value.call(value)); + } else { + value = ""; + } + } + return value; + }; + var escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }; + var escapeHTML = function(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { + return escapeMap[s]; + }); + }; + var isArray = Array.isArray || function(obj) { + return {}.toString.call(obj) === "[object Array]"; + }; + var each = function(data, callback) { + if (isArray(data)) { + for (var i = 0, len = data.length; i < len; i++) { + callback.call(data, data[i], i, data); + } + } else { + for (i in data) { + callback.call(data, data[i], i); + } + } + }; + var resolve = function(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; + var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); + var id = dirname + to; + id = id.replace(/\/\.\//g, "/"); + while (id.match(DOUBLE_DOT_RE)) { + id = id.replace(DOUBLE_DOT_RE, "/"); + } + return id; + }; + var helpers = template.helpers = { + $include: function(uri, data, from) { + var id = resolve(from, uri); + return template.render(id, data); + }, + $string: toString, + $escape: escapeHTML, + $each: each + }; + var debug = function(e) { + var message = ""; + for (var name in e) { + message += "<" + name + ">\n" + e[name] + "\n\n"; + } + if (message && global.console) { + console.error("Template Error\n\n" + message); + } + return function() { + return "{Template Error}"; + }; + }; + template.render = function(uri, data) { + var fn = template.get(uri) || debug({ + id: uri, + name: "Render Error", + message: "No Template" + }); + return data ? fn(data) : fn; + }; + template.compile = function(uri, fn) { + var isFunction = typeof fn === "function"; + var render = cache[uri] = function(data) { + try { + return isFunction ? new fn(data, uri) + "" : fn; + } catch (e) { + return debug(e)(); + } + }; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } + render.toString = function() { + return fn + ""; + }; + return render; + }; + template.get = function(id) { + return cache[id.replace(/^\.\//, "")]; + }; + template.helper = function(name, helper) { + helpers[name] = helper; + }; + template.helper("$ubb2html", function(content) { + content = template.helpers.$escape(content); + return content.replace(/\[b\]([^\[]*?)\[\/b\]/gim, "$1").replace(/\[i\]([^\[]*?)\[\/i\]/gim, "$1").replace(/\[u\]([^\[]*?)\[\/u\]/gim, "$1").replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/gim, '$2').replace(/\[img\]([^\[]*?)\[\/img\]/gim, ''); + }); + template("index", function($data, $id) { + var $helpers = this, $string = $helpers.$string, $ubb2html = $helpers.$ubb2html, title = $data.title, $each = $helpers.$each, list = $data.list, $value = $data.$value, $index = $data.$index, $escape = $helpers.$escape, $out = ""; + $out += '

    '; + $out += $string($ubb2html(title)); + $out += "

    "; + return new String($out); + }); + if (typeof define === "function") { + define(function() { + return template; + }); + } else if (typeof exports !== "undefined") { + module.exports = template; + } else { + global.template = template; + } +}(this); \ No newline at end of file diff --git a/test/test-all/helper/package.json b/test/test-all/helper/package.json index c2cd2cc..06c6197 100644 --- a/test/test-all/helper/package.json +++ b/test/test-all/helper/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", @@ -13,6 +13,6 @@ "engine": false, "type": "default", "combo": true, - "minify": true + "minify": false } } \ No newline at end of file diff --git a/test/test-all/helper/template-helpers.js b/test/test-all/helper/template-helpers.js index 7a3bbb7..cd053ef 100644 --- a/test/test-all/helper/template-helpers.js +++ b/test/test-all/helper/template-helpers.js @@ -1,7 +1,7 @@ template.helper('$ubb2html', function (content) { // 转义 HTML 字符 - content = this.$escape(content); + content = template.helpers.$escape(content); // 解析 UBB 字符 return content diff --git a/test/test-all/include/build/include.js b/test/test-all/include/build/include.js index ab56936..15616ee 100644 --- a/test/test-all/include/build/include.js +++ b/test/test-all/include/build/include.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":1,"md5":"5bfc05c04116597857d42c256cd45c60"}*/ +/*TMODJS:{"version":2,"md5":"302a1925ef7e270689728351aa18bf36"}*/ define([ "./template", "./a", "./b", "./e", "./d" ], function(template) { return template("include", function($data, $id) { var $helpers = this, include = function(id, data) { diff --git a/test/test-all/include/build/template.js b/test/test-all/include/build/template.js index 3a46f9b..9b82a24 100644 --- a/test/test-all/include/build/template.js +++ b/test/test-all/include/build/template.js @@ -1,7 +1,7 @@ /*TMODJS:{}*/ !function(global) { "use strict"; - var template = function(path, content) { + var template = function(uri, content) { return template[/string|function/.test(typeof content) ? "compile" : "render"].apply(template, arguments); }; var cache = template.cache = {}; @@ -55,8 +55,8 @@ return id; }; var helpers = template.helpers = { - $include: function(path, data, from) { - var id = resolve(from, path); + $include: function(uri, data, from) { + var id = resolve(from, uri); return template.render(id, data); }, $string: toString, @@ -75,24 +75,27 @@ return "{Template Error}"; }; }; - template.render = function(path, data) { - var fn = template.get(path) || debug({ - id: path, + template.render = function(uri, data) { + var fn = template.get(uri) || debug({ + id: uri, name: "Render Error", message: "No Template" }); return data ? fn(data) : fn; }; - template.compile = function(path, fn) { + template.compile = function(uri, fn) { var isFunction = typeof fn === "function"; - var render = cache[path] = function(data) { + var render = cache[uri] = function(data) { try { - return isFunction ? new fn(data, path) + "" : fn; + return isFunction ? new fn(data, uri) + "" : fn; } catch (e) { return debug(e)(); } }; - render.prototype = fn.prototype = helpers; + render.prototype = helpers; + if (isFunction) { + fn.prototype = helpers; + } render.toString = function() { return fn + ""; }; diff --git a/test/test-all/include/package.json b/test/test-all/include/package.json index ff0ed81..e3fab0c 100644 --- a/test/test-all/include/package.json +++ b/test/test-all/include/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/mix-cmd/build/copyright.js b/test/test-all/mix-cmd/build/copyright.js index e4365f7..e476f50 100644 --- a/test/test-all/mix-cmd/build/copyright.js +++ b/test/test-all/mix-cmd/build/copyright.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":5,"md5":"14901fde4ceadf78329c78f68d65071b"}*/ +/*TMODJS:{"version":6,"md5":"822614428504528e6c32975732c23529"}*/ define(function(require) { return require("./template")("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/index.js b/test/test-all/mix-cmd/build/index.js index b07e34f..5a60a88 100644 --- a/test/test-all/mix-cmd/build/index.js +++ b/test/test-all/mix-cmd/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":5,"md5":"5ee9611e3fb210129ed0aafc7e55a5c5"}*/ +/*TMODJS:{"version":6,"md5":"284024eb8a7ac5ba1213cc8cd4cbf724"}*/ define(function(require) { require("./public/header"); require("./public/footer"); diff --git a/test/test-all/mix-cmd/build/public/footer.js b/test/test-all/mix-cmd/build/public/footer.js index e7a0983..bf35058 100644 --- a/test/test-all/mix-cmd/build/public/footer.js +++ b/test/test-all/mix-cmd/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":5,"md5":"4b9dd62dfb9844237c897c17244002ca"}*/ +/*TMODJS:{"version":6,"md5":"4ce5fc96554db3a43286a412b2a968b8"}*/ define(function(require) { require("../copyright"); return require("../template")("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); diff --git a/test/test-all/mix-cmd/build/public/header.js b/test/test-all/mix-cmd/build/public/header.js index de0f5f5..7727d82 100644 --- a/test/test-all/mix-cmd/build/public/header.js +++ b/test/test-all/mix-cmd/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":5,"md5":"bd392900512dc8d98c38c7c3a6165506"}*/ +/*TMODJS:{"version":6,"md5":"9004d8ea5b1456fd2f871f905bf2afc2"}*/ define(function(require) { require("./logo"); return require("../template")("public/header", ' '); diff --git a/test/test-all/mix-cmd/build/public/logo.js b/test/test-all/mix-cmd/build/public/logo.js index f29cb8b..db1ee6a 100644 --- a/test/test-all/mix-cmd/build/public/logo.js +++ b/test/test-all/mix-cmd/build/public/logo.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":5,"md5":"b88ffd3942f8bfad0ae795bc51f54051"}*/ +/*TMODJS:{"version":6,"md5":"509fff56bc3ad95846f5eb6e687974a4"}*/ define(function(require) { return require("../template")("public/logo", '

    腾讯网

    '); }); \ No newline at end of file diff --git a/test/test-all/mix-cmd/package.json b/test/test-all/mix-cmd/package.json index 4d21320..a455fce 100644 --- a/test/test-all/mix-cmd/package.json +++ b/test/test-all/mix-cmd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0-rc2" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/mix/build/copyright.js b/test/test-all/mix/build/copyright.js index 44868a2..4c01132 100644 --- a/test/test-all/mix/build/copyright.js +++ b/test/test-all/mix/build/copyright.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"162d4ecb65e3a746824f925ec57a7c7e"}*/ +/*TMODJS:{"version":2,"md5":"f79cd25e5fcbf6aa94ec3c78cf0498b0"}*/ template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/mix/build/index.js b/test/test-all/mix/build/index.js index fc6ab18..458a736 100644 --- a/test/test-all/mix/build/index.js +++ b/test/test-all/mix/build/index.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"1feef40be049b6913e9bee646e8d73bc"}*/ +/*TMODJS:{"version":2,"md5":"751ec4c8bd107d8a3d2ab0519632b638"}*/ template("index", "{{include './public/header'}}

    {{title}}

    {{include './public/footer'}}"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/footer.js b/test/test-all/mix/build/public/footer.js index 250f2d4..f2fb96c 100644 --- a/test/test-all/mix/build/public/footer.js +++ b/test/test-all/mix/build/public/footer.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"33cd8faf8ecaf3969a682d0a841e6a90"}*/ +/*TMODJS:{"version":2,"md5":"37d3c2014346a9576171af879ac8243e"}*/ template("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); \ No newline at end of file diff --git a/test/test-all/mix/build/public/header.js b/test/test-all/mix/build/public/header.js index 537bbdb..d9e0e80 100644 --- a/test/test-all/mix/build/public/header.js +++ b/test/test-all/mix/build/public/header.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"19b7578ca7e4ae65953473c882a40ea7"}*/ +/*TMODJS:{"version":2,"md5":"2c5721d735c7aaf0874b12c29569e934"}*/ template("public/header", ' '); \ No newline at end of file diff --git a/test/test-all/mix/build/public/logo.js b/test/test-all/mix/build/public/logo.js index 70e01f8..d294d4b 100644 --- a/test/test-all/mix/build/public/logo.js +++ b/test/test-all/mix/build/public/logo.js @@ -1,2 +1,2 @@ -/*TMODJS:{"version":1,"md5":"708e33cf506cd61b17b3ec6b7d6ad405"}*/ +/*TMODJS:{"version":2,"md5":"c7507025a436fbbaad8418c25b526ff6"}*/ template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/mix/build/template.js b/test/test-all/mix/build/template.js index e57f2b5..a11d7f2 100644 --- a/test/test-all/mix/build/template.js +++ b/test/test-all/mix/build/template.js @@ -273,6 +273,69 @@ global.template = template; })(this); +(function(exports) { + exports.openTag = "{{"; + exports.closeTag = "}}"; + exports.parser = function(code) { + code = code.replace(/^\s/, ""); + var split = code.split(" "); + var key = split.shift(); + var args = split.join(" "); + switch (key) { + case "if": + code = "if(" + args + "){"; + break; + + case "else": + if (split.shift() === "if") { + split = " if(" + split.join(" ") + ")"; + } else { + split = ""; + } + code = "}else" + split + "{"; + break; + + case "/if": + code = "}"; + break; + + case "each": + var object = split[0] || "$data"; + var as = split[1] || "as"; + var value = split[2] || "$value"; + var index = split[3] || "$index"; + var param = value + "," + index; + if (as !== "as") { + object = "[]"; + } + code = "$each(" + object + ",function(" + param + "){"; + break; + + case "/each": + code = "});"; + break; + + case "echo": + code = "print(" + args + ");"; + break; + + case "include": + code = "include(" + split.join(",") + ");"; + break; + + default: + if (exports.helpers.hasOwnProperty(key)) { + code = "=#" + key + "(" + split.join(",") + ");"; + } else { + code = code.replace(/[\s;]*$/, ""); + code = "=" + code; + } + break; + } + return code; + }; +})(template); + !function(global, template) { "use strict"; var get = template.get; @@ -287,73 +350,11 @@ } return id; }; - helpers.$include = function(path, data, from) { - var id = resolve(from, path); + helpers.$include = function(uri, data, from) { + var id = resolve(from, uri); return template.render(id, data); }; template.get = function(id) { return get(id.replace(/^\.\//, "")); }; - (function(exports) { - exports.openTag = "{{"; - exports.closeTag = "}}"; - exports.parser = function(code) { - code = code.replace(/^\s/, ""); - var split = code.split(" "); - var key = split.shift(); - var args = split.join(" "); - switch (key) { - case "if": - code = "if(" + args + "){"; - break; - - case "else": - if (split.shift() === "if") { - split = " if(" + split.join(" ") + ")"; - } else { - split = ""; - } - code = "}else" + split + "{"; - break; - - case "/if": - code = "}"; - break; - - case "each": - var object = split[0] || "$data"; - var as = split[1] || "as"; - var value = split[2] || "$value"; - var index = split[3] || "$index"; - var param = value + "," + index; - if (as !== "as") { - object = "[]"; - } - code = "$each(" + object + ",function(" + param + "){"; - break; - - case "/each": - code = "});"; - break; - - case "echo": - code = "print(" + args + ");"; - break; - - case "include": - code = "include(" + split.join(",") + ");"; - break; - - default: - if (exports.helpers.hasOwnProperty(key)) { - code = "=#" + key + "(" + split.join(",") + ");"; - } else { - code = code.replace(/[\s;]*$/, ""); - code = "=" + code; - } - break; - } - return code; - }; - })(template); }(this, this.template); \ No newline at end of file diff --git a/test/test-all/mix/package.json b/test/test-all/mix/package.json index c9255de..b4d68ee 100644 --- a/test/test-all/mix/package.json +++ b/test/test-all/mix/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/syntax-native/build/template.js b/test/test-all/syntax-native/build/template.js index d07c765..965e2dd 100644 --- a/test/test-all/syntax-native/build/template.js +++ b/test/test-all/syntax-native/build/template.js @@ -1,3 +1,3 @@ -/*TMODJS:{"build":1388856475738}*/ -!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:c},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/**/ -r("index",function(e){var r=this,n=r.$escape,t=e.title,i=e.i,o=e.list,u="";u+='

    ',u+=n(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/*TMODJS:{"build":1390233186500}*/ +!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},o={"<":"<",">":">",'"':""","'":"'","&":"&"},i=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return o[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),o=t+r;for(o=o.replace(/\/\.\//g,"/");o.match(n);)o=o.replace(n,"/");return o},f=r.helpers={$include:function(e,n,t){var o=a(t,e);return r.render(o,n)},$string:t,$escape:i,$each:c},l=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||l({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,o=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(o){return l(o)()}};return o.prototype=f,t&&(r.prototype=f),o.toString=function(){return r+""},o},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){f[e]=r},/*v:2*/ +r("index",function(e){var r=this,n=r.$escape,t=e.title,o=e.i,i=e.list,u="";u+='

    ',u+=n(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/test-all/syntax-native/package.json b/test/test-all/syntax-native/package.json index e12a325..e964483 100644 --- a/test/test-all/syntax-native/package.json +++ b/test/test-all/syntax-native/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/syntax/build/template.js b/test/test-all/syntax/build/template.js index 8736115..b3da80b 100644 --- a/test/test-all/syntax/build/template.js +++ b/test/test-all/syntax/build/template.js @@ -1,3 +1,3 @@ -/*TMODJS:{"build":1388856469181}*/ -!function(e){"use strict";var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},l=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+n;for(i=i.replace(/\/\.\//g,"/");i.match(r);)i=i.replace(r,"/");return i},a=n.helpers={$include:function(e,r,t){var i=l(t,e);return n.render(i,r)},$string:t,$escape:o,$each:c},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,i=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(i){return f(i)()}};return i.prototype=n.prototype=a,i.toString=function(){return n+""},i},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){a[e]=n},/**/ -n("index",function(e){var n=this,r=e.x,t=n.$escape,i=e.title,o=n.$each,u=e.list,c=(e.$value,e.$index,""),r="hello world";return c+=" ",c+=t(r),c+='

    ',c+=t(i),c+="

    ",new String(c)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file +/*TMODJS:{"build":1390233180365}*/ +!function(e){"use strict";var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},o={"<":"<",">":">",'"':""","'":"'","&":"&"},i=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return o[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},a=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),o=t+n;for(o=o.replace(/\/\.\//g,"/");o.match(r);)o=o.replace(r,"/");return o},l=n.helpers={$include:function(e,r,t){var o=a(t,e);return n.render(o,r)},$string:t,$escape:i,$each:c},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,o=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(o){return f(o)()}};return o.prototype=l,t&&(n.prototype=l),o.toString=function(){return n+""},o},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){l[e]=n},/*v:2*/ +n("index",function(e){var n=this,r=e.x,t=n.$escape,o=e.title,i=n.$each,u=e.list,c=(e.$value,e.$index,""),r="hello world";return c+=" ",c+=t(r),c+='

    ',c+=t(o),c+="

    ",new String(c)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file diff --git a/test/test-all/syntax/package.json b/test/test-all/syntax/package.json index 1c5ac3c..a99f32a 100644 --- a/test/test-all/syntax/package.json +++ b/test/test-all/syntax/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/webapp-tpl/404/main.html b/test/test-all/webapp-tpl/404/main.html new file mode 100755 index 0000000..44b46f3 --- /dev/null +++ b/test/test-all/webapp-tpl/404/main.html @@ -0,0 +1,3 @@ +
    +

    404:您访问的页面不存在!

    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/build/template.js b/test/test-all/webapp-tpl/build/template.js new file mode 100644 index 0000000..3766215 --- /dev/null +++ b/test/test-all/webapp-tpl/build/template.js @@ -0,0 +1,21 @@ +/*TMODJS:{"build":1390233272112}*/ +!function(a){"use strict";var i=function(a,s){return i[/string|function/.test(typeof s)?"compile":"render"].apply(i,arguments)},s=i.cache={},t=function(a,i){return"string"!=typeof a&&(i=typeof a,"number"===i?a+="":a="function"===i?t(a.call(a)):""),a},e={"<":"<",">":">",'"':""","'":"'","&":"&"},n=function(a){return t(a).replace(/&(?![\w#]+;)|[<>"']/g,function(a){return e[a]})},r=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},c=function(a,i){if(r(a))for(var s=0,t=a.length;t>s;s++)i.call(a,a[s],s,a);else for(s in a)i.call(a,a[s],s)},l=function(a,i){var s=/(\/)[^/]+\1\.\.\1/,t=a.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),e=t+i;for(e=e.replace(/\/\.\//g,"/");e.match(s);)e=e.replace(s,"/");return e},d=i.helpers={$include:function(a,s,t){var e=l(t,a);return i.render(e,s)},$string:t,$escape:n,$each:c},o=function(i){var s="";for(var t in i)s+="<"+t+">\n"+i[t]+"\n\n";return s&&a.console&&console.error("Template Error\n\n"+s),function(){return"{Template Error}"}};i.render=function(a,s){var t=i.get(a)||o({id:a,name:"Render Error",message:"No Template"});return s?t(s):t},i.compile=function(a,i){var t="function"==typeof i,e=s[a]=function(s){try{return t?new i(s,a)+"":i}catch(e){return o(e)()}};return e.prototype=d,t&&(i.prototype=d),e.toString=function(){return i+""},e},i.get=function(a){return s[a.replace(/^\.\//,"")]},i.helper=function(a,i){d[a]=i},/*v:1*/ +i("404/main",'

    404:\u60a8\u8bbf\u95ee\u7684\u9875\u9762\u4e0d\u5b58\u5728!

    '),/*v:1*/ +i("detail/focus",function(a){var i=this,s=i.$escape,t=a._rangeTypeText,e=a.focus,n="";return n+='

    \u70ed\u5ea6\u8d8b\u52bf

    ?

    \u70ed\u5ea6\u8d8b\u52bf\u56fe

    \u5386\u53f2',n+=s(t),n+="\u5e73\u5747",n+=s(e.HistoryAverageCount),n+='\u6761

    \u6700\u8fd1\u4e00',n+=s(t),n+='\u5171\u6709\u76f8\u5173\u5fae\u535a',n+=s(e.TotalCount),n+="\u6761\uff0c\u4e0e\u4e0a\u4e00",n+=s(t),n+='\u76f8\u6bd4--
    ',new String(n)}),/*v:1*/ +i("detail/hotTerm",function(a){var i=this,s=a.hotTerm,t=i.$each,e=(a.$value,a.$index,i.$escape),n=a.id,r=a.IsLocateProblem,c="";return c+='

    \u70ed\u8bcd\u5206\u6790

    ?
    ',c+=e(a.Word),c+='',c+=e(a.Count),c+='\u6761 ',a.IsNew&&(c+=' n '),c+=" "}),c+="
    ",1!==r?(c+='
    ',t(s.TopList,function(a){c+=' ',c+=e(a.Word),c+='',c+=e(a.Count),c+="\u6761",a.IsNew&&(c+='n'),c+=" "}),c+="
    "):c+='
    ',c+="
    ",new String(c)}),/*v:1*/ +i("detail/keyword",function(a){var i=this,s=i.$escape,t=a.id,e=a.Word,n=a.Count,r=a.IsNew,c="";return c+=' ',c+=s(e),c+='',c+=s(n),c+='\u6761 ',r&&(c+=' n '),c+=" ",new String(c)}),/*v:1*/ +i("detail/main",function(a,i){var s=this,t=s.$escape,e=a.id,n=a._period,r=function(t,e){e=e||a;var n=s.$include(t,e,i);return l+=n,n},c=a.IsAppStore,l="";return l+='
    ',l+=t(n),l+='
    ',r("./report"),l+=" ",r("./reputation"),l+=" ",r("./focus"),l+=" ",r("./hotTerm"),l+=" ",r("./weibo"),l+='
    ',new String(l)}),/*v:1*/ +i("detail/negative",function(a){var i=this,s=i.$escape,t=a.TopListTotalCount,e=i.$each,n=a.locateTerm,r=(a.$value,a.$index,a.id),c=a.TopListOtherCount,l="";return l+='

    \u8d1f\u9762\u8bc4\u8bba

    \u5171\u6536\u96c6\u5230',l+=s(t),l+="\u6761
    ",e(n.TopList,function(a,i){l+=" ",6>i?(l+=' ',l+=s(a.Count),l+='\u6761',l+=s(a.Term),l+="",a.IsNew&&(l+='n'),l+=" "):(l+=' ',l+=s(a.Count),l+='\u6761',l+=s(a.Term),l+="",a.IsNew&&(l+='n'),l+=" "),l+=" "}),l+=' ',l+=s(c),l+='\u6761\u5176\u4ed6 ',new String(l)}),/*v:1*/ +i("detail/report",function(a){var i=this,s=i.$escape,t=a.ReputationIndex,e=a.ChainIndex,n=a.TotalCount,r=a.RepuRanking,c=a.IsFavStickie,l=a.id,d=a.CompetitionReport,o=i.$each,p=(a.$value,a.$index,"");return p+='
    ',p+=s(t),p+="",p+=s(t),p+="
    ",999999999===e?p+=' --% ':0>e?(p+=' ',p+=s(-1*e),p+="% "):(p+=' ',p+=s(e),p+="% "),p+='
    ',20>=t?p+='

    \u53e3\u7891\u6307\u6570\u5f88\u4f4e\uff0c\u8981\u5f15\u8d77\u91cd\u89c6\u4e86\u54e6\uff01

    ':t>20&&40>=t?p+='

    \u53e3\u7891\u6307\u6570\u8f83\u4f4e\uff0c\u662f\u4e0d\u662f\u51fa\u4e86\u4ec0\u4e48\u95ee\u9898\uff1f

    ':t>40&&60>=t?p+='

    \u53e3\u7891\u6307\u6570\u4e00\u822c\uff0c\u9700\u6301\u7eed\u5173\u6ce8\u548c\u4f18\u5316\u3002

    ':t>60&&80>=t?p+='

    \u53e3\u7891\u6307\u6570\u8f83\u9ad8\uff0c\u8bf7\u7ee7\u7eed\u52a0\u6cb9\uff01

    ':t>80&&(p+='

    \u53e3\u7891\u6307\u6570\u5f88\u9ad8\uff0c\u5f88\u4e0d\u9519\u54e6\uff01

    '),p+='
    ',d.length&&(p+='

    \u7ade\u54c1\u60c5\u51b5

    ',o(d,function(a){p+='
    ',p+=s(a.ProductUniqueName),p+=' \u53e3\u7891\u6307\u6570 ',p+=s(a.ReputationIndex||"--"),p+=' ',p+=s(a.ProductWeiboCount||"--"),p+="
    "}),p+="
    "),p+=" ",new String(p)}),/*v:1*/ +i("detail/reputation",function(a){var i=this,s=i.$escape,t=a._rangeTypeText,e=a.reputation,n=i.$each,r=(a.$value,a.$index,a.id),c="";return c+='

    \u53e3\u7891\u8d8b\u52bf

    ?

    \u53e3\u7891\u6307\u6570\u8d8b\u52bf\u56fe

    ',c+=s(t),c+='\u6700\u9ad8--\uff0c',c+=s(t),c+='\u6700\u4f4e--\uff0c',c+=s(t),c+='\u5e73\u5747--

    \u8fd1\u4e00',c+=s(t),c+='\u7684\u53e3\u7891\u6307\u6570\u4e3a',c+=s(e.ReputationIndex),c+="\uff0c\u4e0e\u4e0a",c+=s(t),c+='\u76f8\u6bd4--

    \u6b63\u9762\u53e3\u7891 Top5\uff1a

    \u8d1f\u9762\u53e3\u7891 Top5\uff1a

    ",new String(c)}),/*v:1*/ +i("detail/weibo-list",function(a){var i=this,s=i.$each,t=a.RawDataList,e=(a.$value,a.$index,i.$escape),n=function(a){return r+=a,a},r="";return s(t,function(a){r+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ +i("detail/weibo",function(a){var i=this,s=i.$escape,t=a.id,e="";return e+='',new String(e)}),/*v:1*/ +i("home/main",function(a,i){var s=this,t=a._isValueSwitch,e=s.$escape,n=a._rangeTypeText,r=a._sortTypeText,c=s.$each,l=a.productList,d=(a.$value,a.$index,function(t,e){e=e||a;var n=s.$include(t,e,i);return o+=n,n}),o="";return o+='
    \u53e3\u7891\u6307\u6570',o+=e(r),o+='
    ',c(l,function(a){o+=' ',d("./project",a),o+=" "}),o+="
    ",new String(o)}),/*v:7*/ +i("home/project",function(a){var i=this,s=a.IsFavStickie,t=i.$escape,e=a.ProductUniqueId,n=a.ProductUniqueName,r=a.RepuIndexDiffrence,c=a.TotalCount,l=a.ReputationIndex,d=a.iCompRepuIndex,o=a.iComRepuIndexDiffrence,p=a.HasCompetition,v=a._compValue,u=a._warnClass2,g=a._updownClass,_=a.RepuIndexPercent,f="";return f+='
    ',1==s&&(f+=' '),f+='

    ',f+=t(n),f+="

    ",p&&(f+=" ",v>0?(f+='

    \u9886\u5148\u7ade\u54c1+',f+=t(v),f+="

    "):0===v?f+='

    \u4e0d\u76f8\u4e0a\u4e0b0

    ':0>v?(f+='

    \u843d\u540e\u7ade\u54c1',f+=t(v),f+="

    "):f+='

    ',f+=" "),f+="
    ",f+=r>0?' \u2191 ':' \u2193 ',f+='
    ',f+=t(l),f+='
    ',0>r?(f+=" -",f+=t(_),f+="% "):r>0?(f+=" +",f+=t(_),f+="% "):f+=" 0 ",f+='
    ',0>r?(f+=" -",f+=t(r),f+=" "):r>0?(f+=" +",f+=t(r),f+=" "):f+=" 0 ",f+='
    ',f+=t(c),f+="
    ",new String(f)}),/*v:2*/ +i("negative/main",function(a,i){var s=this,t=s.$each,e=a.TopList,n=(a.$value,a.$index,s.$escape),r=function(t,e){e=e||a;var n=s.$include(t,e,i);return l+=n,n},c=a.HasNext,l="";return l+='
    ',r("../public/tag-list"),l+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ +i("reputation/main",function(a,i){var s=this,t=s.$escape,e=a.DataSummary,n=function(t,e){e=e||a;var n=s.$include(t,e,i);return c+=n,n},r=a.HasNext,c="";return c+='
    \u5305\u542b\u201c',c+=t(e.Alias),c+="\u201d\u6216\u5176\u540c\u4e49\u8bcd\u7684\u5fae\u535a\u5171",c+=t(e.TotalCount),c+='\u6761
    ',n("../public/tag-list"),c+="
    ",c+=r?' \u70b9\u51fb\u66f4\u591a ':' \u5168\u90e8\u52a0\u8f7d\u5b8c\u6bd5 ',c+="
    ",new String(c)}),/*v:1*/ +i("setting/main",function(a){var i=a.origin,s=a.sortType,t=a.rangeType,e=a.isDebug,n="";return n+='',new String(n)}),/*v:1*/ +i("tag/main",function(a,i){var s=this,t=function(t,e){e=e||a;var r=s.$include(t,e,i);return n+=r,r},e=a.HasNext,n="";return n+='
    ',t("../public/tag-list"),n+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ +i("weibo/main",function(a,i){var s=this,t=function(t,e){e=e||a;var r=s.$include(t,e,i);return n+=r,r},e=a.HasNext,n="";return n+='
    ',t("./list"),n+="
    ",n+=e?' \u70b9\u51fb\u66f4\u591a ':' \u5168\u90e8\u52a0\u8f7d\u5b8c\u6bd5 ',n+="
    ",new String(n)}),"function"==typeof define?define(function(){return i}):"undefined"!=typeof exports?module.exports=i:a.template=i}(this); \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/focus.html b/test/test-all/webapp-tpl/detail/focus.html new file mode 100755 index 0000000..96cc11d --- /dev/null +++ b/test/test-all/webapp-tpl/detail/focus.html @@ -0,0 +1,17 @@ +
    +
    + +

    热度趋势

    + ? +
    +
    +

    热度趋势图

    +

    历史{{_rangeTypeText}}平均{{focus.HistoryAverageCount}}

    +
    + +
    +
    + 最近一{{_rangeTypeText}}共有相关微博{{focus.TotalCount}}条,与上一{{_rangeTypeText}}相比-- +
    +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/hotTerm.html b/test/test-all/webapp-tpl/detail/hotTerm.html new file mode 100755 index 0000000..c3432c8 --- /dev/null +++ b/test/test-all/webapp-tpl/detail/hotTerm.html @@ -0,0 +1,40 @@ +
    +
    + +

    热词分析

    + ? +
    +
    +
    +
    + + + + 订阅 + +
    + +
    + 编辑 + {{each hotTerm.CustomList}} + + {{$value.Word}}{{$value.Count}} + + {{if $value.IsNew}} + n + {{/if}} + + {{/each}} +
    +
    + {{if IsLocateProblem !== 1}} +
    + {{each hotTerm.TopList}} + {{$value.Word}}{{$value.Count}}{{if $value.IsNew}}n{{/if}} + {{/each}} +
    + {{else}} +
    + {{/if}} +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/keyword.html b/test/test-all/webapp-tpl/detail/keyword.html new file mode 100755 index 0000000..1813047 --- /dev/null +++ b/test/test-all/webapp-tpl/detail/keyword.html @@ -0,0 +1,7 @@ + + {{Word}}{{Count}} + + {{if IsNew}} + n + {{/if}} + \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/main.html b/test/test-all/webapp-tpl/detail/main.html new file mode 100755 index 0000000..0182a6a --- /dev/null +++ b/test/test-all/webapp-tpl/detail/main.html @@ -0,0 +1,22 @@ +
    +
    {{_period}}
    +
    +
    + {{include './report'}} + {{include './reputation'}} + {{include './focus'}} + {{include './hotTerm'}} + {{include './weibo'}} +
    +
    + +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/negative.html b/test/test-all/webapp-tpl/detail/negative.html new file mode 100755 index 0000000..93f2e3b --- /dev/null +++ b/test/test-all/webapp-tpl/detail/negative.html @@ -0,0 +1,15 @@ +
    +

    负面评论

    共收集到{{TopListTotalCount}}条 +
    +{{each locateTerm.TopList}} + {{if $index < 6}} + {{$value.Count}}{{$value.Term}}{{if $value.IsNew}}n{{/if}} + {{else}} + {{$value.Count}}{{$value.Term}}{{if $value.IsNew}}n{{/if}} + {{/if}} +{{/each}} + + + {{TopListOtherCount}}其他 + + \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/report.html b/test/test-all/webapp-tpl/detail/report.html new file mode 100755 index 0000000..3ee4bd7 --- /dev/null +++ b/test/test-all/webapp-tpl/detail/report.html @@ -0,0 +1,85 @@ +
    +
    +
    {{ReputationIndex}}{{ReputationIndex}}
    + + + {{if ChainIndex === 999999999}} + --% + {{else if ChainIndex < 0}} + {{-1 * ChainIndex}}% + {{else}} + {{ChainIndex}}% + {{/if}} + + + +
    +
    +
    +
    +
    + + + + {{if ReputationIndex <= 20}} + +

    口碑指数很低,要引起重视了哦!

    + + {{else if ReputationIndex >20 && ReputationIndex <=40}} + +

    口碑指数较低,是不是出了什么问题?

    + + {{else if ReputationIndex > 40 && ReputationIndex <= 60}} + +

    口碑指数一般,需持续关注和优化。

    + + {{else if ReputationIndex > 60 && ReputationIndex <= 80}} + +

    口碑指数较高,请继续加油!

    + + {{else if ReputationIndex > 80}} + +

    口碑指数很高,很不错哦!

    + + {{/if}} +
    + {{if IsFavStickie == 1}} + 取消置顶 + {{else}} + 点击置顶 + {{/if}} + + 点击下载 +
    + + +
    + + +{{if CompetitionReport.length}} +
    +

    竞品情况

    + {{each CompetitionReport}} +
    + {{$value.ProductUniqueName}} + 口碑指数 {{$value.ReputationIndex || '--'}} + {{$value.ProductWeiboCount || '--'}} +
    + {{/each}} +
    +{{/if}} diff --git a/test/test-all/webapp-tpl/detail/reputation.html b/test/test-all/webapp-tpl/detail/reputation.html new file mode 100755 index 0000000..5c1bb7c --- /dev/null +++ b/test/test-all/webapp-tpl/detail/reputation.html @@ -0,0 +1,43 @@ +
    +
    + +

    口碑趋势

    + ? +
    +
    +

    口碑指数趋势图

    +

    {{_rangeTypeText}}最高--,{{_rangeTypeText}}最低--,{{_rangeTypeText}}平均--

    +
    + +
    +
    + 近一{{_rangeTypeText}}的口碑指数为{{reputation.ReputationIndex}},与上{{_rangeTypeText}}相比-- +
    + +
    +
    +

    正面口碑 Top5:

    + +
    +
    +

    负面口碑 Top5:

    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/weibo-list.html b/test/test-all/webapp-tpl/detail/weibo-list.html new file mode 100755 index 0000000..4c1c9a0 --- /dev/null +++ b/test/test-all/webapp-tpl/detail/weibo-list.html @@ -0,0 +1,17 @@ +{{each RawDataList}} +
    +
    + {{$value.Nick}} + {{if $value.IsVip}}{{/if}} + {{$value.ExtractDate}} +
    + +
    + 来自 {{$value.From}} + 评论 + {{$value.Count}} +
    +
    +{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/weibo.html b/test/test-all/webapp-tpl/detail/weibo.html new file mode 100755 index 0000000..b2c3f5e --- /dev/null +++ b/test/test-all/webapp-tpl/detail/weibo.html @@ -0,0 +1,13 @@ +
    +
    + +

    热门微博

    + +
    +
    +
    + +
    + 点击更多> +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/home/main.html b/test/test-all/webapp-tpl/home/main.html new file mode 100755 index 0000000..04daced --- /dev/null +++ b/test/test-all/webapp-tpl/home/main.html @@ -0,0 +1,13 @@ +
    + +
    近一{{_rangeTypeText}}内数据 口碑指数{{_sortTypeText}}
    +
    +
    + {{each productList}} + + {{include './project' $value}} + + {{/each}} +
    +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/home/project.html b/test/test-all/webapp-tpl/home/project.html new file mode 100755 index 0000000..f13b8cc --- /dev/null +++ b/test/test-all/webapp-tpl/home/project.html @@ -0,0 +1,64 @@ +
    + {{if IsFavStickie == 1}} + + {{/if}} + +
    +

    {{ProductUniqueName}}

    + {{if HasCompetition}} + {{if _compValue > 0}} +

    领先竞品+{{_compValue}}

    + {{else if _compValue === 0}} +

    不相上下0

    + {{else if _compValue < 0}} +

    落后竞品{{_compValue}}

    + {{else}} +

    + {{/if}} + + {{/if}} +
    + + {{if RepuIndexDiffrence > 0}} + + {{else}} + + {{/if}} + +
    + {{ReputationIndex}} + +
    +
    + +
    + {{if RepuIndexDiffrence < 0}} + -{{ RepuIndexPercent}}% + {{else if RepuIndexDiffrence > 0}} + +{{ RepuIndexPercent}}% + {{else}} + 0 + {{/if}} +
    + +
    + {{if RepuIndexDiffrence < 0}} + -{{RepuIndexDiffrence}} + {{else if RepuIndexDiffrence > 0}} + +{{RepuIndexDiffrence}} + {{else}} + 0 + {{/if}} +
    + +
    + {{TotalCount}} +
    +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/negative/main.html b/test/test-all/webapp-tpl/negative/main.html new file mode 100755 index 0000000..93d645a --- /dev/null +++ b/test/test-all/webapp-tpl/negative/main.html @@ -0,0 +1,19 @@ +
    + +
    +
    + 更多 + {{each TopList}} + + {{$value.Term}} + {{$value.Count}} + + {{/each}} +
    +
    + {{include '../public/tag-list'}} +
    + 点击更多 +
    + +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/package.json b/test/test-all/webapp-tpl/package.json new file mode 100644 index 0000000..be9c5b9 --- /dev/null +++ b/test/test-all/webapp-tpl/package.json @@ -0,0 +1,18 @@ +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "~0.1.1" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": null, + "escape": true, + "engine": false, + "type": "default", + "combo": true, + "minify": true + } +} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/public/tag-list.html b/test/test-all/webapp-tpl/public/tag-list.html new file mode 100755 index 0000000..72c0bfc --- /dev/null +++ b/test/test-all/webapp-tpl/public/tag-list.html @@ -0,0 +1,17 @@ +{{each DataSummary.RawDataList}} +
    +
    + {{$value.Nick}} + {{if $value.IsVip}}{{/if}} + {{$value.ExtractDate}} +
    + +
    + 来自 {{$value.From}} + 评论 + {{$value.Count}} +
    +
    +{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/reputation/main.html b/test/test-all/webapp-tpl/reputation/main.html new file mode 100755 index 0000000..f6db662 --- /dev/null +++ b/test/test-all/webapp-tpl/reputation/main.html @@ -0,0 +1,22 @@ +
    + +
    +
    + +
    包含“{{DataSummary.Alias}}”或其同义词的微博共{{DataSummary.TotalCount}}
    +
    +
    + {{include '../public/tag-list'}} +
    + {{if HasNext}} + 点击更多 + {{else}} + 全部加载完毕 + {{/if}} +
    + +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/setting/main.html b/test/test-all/webapp-tpl/setting/main.html new file mode 100755 index 0000000..cec46e6 --- /dev/null +++ b/test/test-all/webapp-tpl/setting/main.html @@ -0,0 +1,49 @@ +
    +
    +
    + + {{if origin === 'Home'}} + + + {{/if}} + +

    时间范围

    + + + {{if origin === 'Home'}} +

    高级

    + + {{/if}} + +

    关于

    + + + {{if isDebug}} +

    Debug

    + + {{/if}} + +
    + +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/tag/main.html b/test/test-all/webapp-tpl/tag/main.html new file mode 100755 index 0000000..aa30585 --- /dev/null +++ b/test/test-all/webapp-tpl/tag/main.html @@ -0,0 +1,17 @@ +
    + +
    + +
    + {{include '../public/tag-list'}} +
    + 点击更多 +
    + +
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/weibo/list.html b/test/test-all/webapp-tpl/weibo/list.html new file mode 100755 index 0000000..4c1c9a0 --- /dev/null +++ b/test/test-all/webapp-tpl/weibo/list.html @@ -0,0 +1,17 @@ +{{each RawDataList}} +
    +
    + {{$value.Nick}} + {{if $value.IsVip}}{{/if}} + {{$value.ExtractDate}} +
    + +
    + 来自 {{$value.From}} + 评论 + {{$value.Count}} +
    +
    +{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/weibo/main.html b/test/test-all/webapp-tpl/weibo/main.html new file mode 100755 index 0000000..07a904d --- /dev/null +++ b/test/test-all/webapp-tpl/weibo/main.html @@ -0,0 +1,14 @@ +
    + +
    +
    + {{include './list'}} +
    + {{if HasNext}} + 点击更多 + {{else}} + 全部加载完毕 + {{/if}} +
    + +
    diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 28be59c..e3161cc 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ -/*TMODJS:{"build":1389970390952}*/ -!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=r.prototype=l,i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/*v:4*/ -r("copyright","(c) 2013"),/*v:4*/ -r("index",function(e,r){var n=this,t=function(t,i){i=i||e;var o=n.$include(t,i,r);return a+=o,o},i=n.$escape,o=e.title,c=n.$each,u=e.list,a=(e.$value,e.$index,"");return t("./public/header"),a+='

    ',a+=i(o),a+="

    ",t("./public/footer"),a+=" ",new String(a)}),/*v:4*/ -r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,o=function(t,i){i=i||e;var o=n.$include(t,i,r);return c+=o,o},c="";return c+='",new String(c)}),/*v:4*/ -r("public/header",function(e,r){var n=this,t=function(t,o){o=o||e;var c=n.$include(t,o,r);return i+=c,c},i="";return i+=' ',new String(i)}),/*v:4*/ +/*TMODJS:{"build":1390231214112}*/ +!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=l,t&&(r.prototype=l),i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/*v:8*/ +r("copyright","(c) 2014"),/*v:9*/ +r("index",function(e,r){var n=this,t=function(t,i){i=i||e;var o=n.$include(t,i,r);return a+=o,o},i=n.$escape,o=e.title,c=n.$each,u=e.list,a=(e.$value,e.$index,"");return t("./public/header"),a+='

    ',a+=i(o),a+="

    ",t("./public/footer"),new String(a)}),/*v:7*/ +r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,o=function(t,i){i=i||e;var o=n.$include(t,i,r);return c+=o,o},c="";return c+='",new String(c)}),/*v:7*/ +r("public/header",function(e,r){var n=this,t=function(t,o){o=o||e;var c=n.$include(t,o,r);return i+=c,c},i="";return i+=' ',new String(i)}),/*v:7*/ r("public/logo",'

    \u817e\u8baf\u7f51

    '),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file diff --git a/test/tpl/copyright.html b/test/tpl/copyright.html index 4c65dfa..beedc6a 100644 --- a/test/tpl/copyright.html +++ b/test/tpl/copyright.html @@ -1 +1 @@ -(c) 2013 \ No newline at end of file +(c) 2014 \ No newline at end of file diff --git a/test/tpl/index.html b/test/tpl/index.html index c259213..fc67525 100644 --- a/test/tpl/index.html +++ b/test/tpl/index.html @@ -9,4 +9,4 @@

    {{title}}

    -{{include './public/footer'}} \ No newline at end of file +{{include './public/footer'}} \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index cd5fa15..be9c5b9 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.0-rc3" + "tmodjs": "~0.1.1" }, "tmodjs-config": { "output": "./build", From 723cdc8c5900930a15dc2264944c7354f92ff10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= <1987.tangbin@gmail.com> Date: Sun, 11 May 2014 01:05:55 +0800 Subject: [PATCH 24/87] Update package.json --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 22053f0..1fce692 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "0.1.1", + "version": "0.1.2", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", @@ -29,9 +29,9 @@ "test2": "tmod ./test/test-all/*" }, "dependencies": { - "art-template": "latest", - "uglify-js": "~2.4.0", - "async": "~0.2.6" + "art-template": "2.0.4", + "uglify-js": "2.4.0", + "async": "0.2.6" }, "license": "BSD" -} \ No newline at end of file +} From 2efef0b3d83933d6ac5a9cfb890d18ca23880b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 16 May 2014 13:52:37 +0800 Subject: [PATCH 25/87] v1.0.0 --- .npmignore | 3 + README.md | 290 ++-- bin/tmod | 115 +- doc/helper.md | 57 +- doc/syntax-simple.md | 113 +- doc/upgrade.md | 12 +- package.json | 8 +- src/AOTcompile.js | 375 ++--- src/defaults.js | 51 + src/runtime.js | 208 +++ src/runtime/basic.js | 177 --- src/runtime/full.js | 35 - src/stdout.js | 15 +- src/syntax/native.js | 1 + src/syntax/simple.js | 1 + src/tmod.js | 1241 +++++++++-------- src/uglify2.js | 176 +++ src/watch.js | 22 +- test/test-all/amd/build/copyright.js | 2 +- test/test-all/amd/build/index.js | 32 +- test/test-all/amd/build/public/footer.js | 24 +- test/test-all/amd/build/public/header.js | 20 +- test/test-all/amd/build/public/logo.js | 2 +- test/test-all/amd/build/template.js | 176 +-- test/test-all/amd/package.json | 8 +- test/test-all/cmd-alias/build/copyright.js | 6 +- test/test-all/cmd-alias/build/index.js | 29 +- .../test-all/cmd-alias/build/public/footer.js | 24 +- .../test-all/cmd-alias/build/public/header.js | 18 +- test/test-all/cmd-alias/build/public/logo.js | 6 +- test/test-all/cmd-alias/build/template.js | 119 +- test/test-all/cmd-alias/package.json | 8 +- test/test-all/cmd/build/copyright.js | 6 +- test/test-all/cmd/build/index.js | 29 +- test/test-all/cmd/build/public/footer.js | 24 +- test/test-all/cmd/build/public/header.js | 18 +- test/test-all/cmd/build/public/logo.js | 6 +- test/test-all/cmd/build/template.js | 119 +- test/test-all/cmd/package.json | 10 +- test/test-all/combo-off/build/copyright.js | 2 +- test/test-all/combo-off/build/index.js | 32 +- .../test-all/combo-off/build/public/footer.js | 24 +- .../test-all/combo-off/build/public/header.js | 20 +- test/test-all/combo-off/build/public/logo.js | 2 +- test/test-all/combo-off/build/template.js | 176 +-- test/test-all/combo-off/package.json | 8 +- test/test-all/escape-off/build/template.js | 6 +- test/test-all/escape-off/package.json | 8 +- test/test-all/helper.html | 23 - test/test-all/helper/build/index.js | 2 + test/test-all/helper/build/template.js | 216 ++- test/test-all/helper/index.html | 8 - test/test-all/helper/package.json | 8 +- test/test-all/helper/template-helpers.js | 53 +- test/test-all/include/build/include.js | 31 +- test/test-all/include/build/template.js | 176 +-- test/test-all/include/package.json | 8 +- test/test-all/mix-cmd.html | 78 -- test/test-all/mix-cmd/build/copyright.js | 4 - test/test-all/mix-cmd/build/index.js | 6 - test/test-all/mix-cmd/build/public/footer.js | 5 - test/test-all/mix-cmd/build/public/header.js | 5 - test/test-all/mix-cmd/build/public/logo.js | 4 - test/test-all/mix-cmd/build/template.js | 360 ----- test/test-all/mix-cmd/copyright.html | 1 - test/test-all/mix-cmd/index.html | 12 - test/test-all/mix-cmd/package.json | 18 - test/test-all/mix-cmd/public/footer.html | 6 - test/test-all/mix-cmd/public/header.html | 11 - test/test-all/mix-cmd/public/logo.html | 7 - test/test-all/mix.html | 83 -- test/test-all/mix/build/copyright.js | 2 - test/test-all/mix/build/index.js | 2 - test/test-all/mix/build/public/footer.js | 2 - test/test-all/mix/build/public/header.js | 2 - test/test-all/mix/build/public/logo.js | 2 - test/test-all/mix/build/template.js | 360 ----- test/test-all/mix/copyright.html | 1 - test/test-all/mix/index.html | 12 - test/test-all/mix/package.json | 18 - test/test-all/mix/public/footer.html | 6 - test/test-all/mix/public/header.html | 11 - test/test-all/mix/public/logo.html | 7 - test/test-all/syntax-native/build/template.js | 6 +- test/test-all/syntax-native/package.json | 8 +- test/test-all/syntax/build/template.js | 6 +- test/test-all/syntax/index.html | 11 +- test/test-all/syntax/package.json | 8 +- test/test-all/syntax/template-syntax.js | 141 +- test/test-all/webapp-tpl/404/main.html | 3 - test/test-all/webapp-tpl/build/template.js | 21 - test/test-all/webapp-tpl/detail/focus.html | 17 - test/test-all/webapp-tpl/detail/hotTerm.html | 40 - test/test-all/webapp-tpl/detail/keyword.html | 7 - test/test-all/webapp-tpl/detail/main.html | 22 - test/test-all/webapp-tpl/detail/negative.html | 15 - test/test-all/webapp-tpl/detail/report.html | 85 -- .../webapp-tpl/detail/reputation.html | 43 - .../webapp-tpl/detail/weibo-list.html | 17 - test/test-all/webapp-tpl/detail/weibo.html | 13 - test/test-all/webapp-tpl/home/main.html | 13 - test/test-all/webapp-tpl/home/project.html | 64 - test/test-all/webapp-tpl/negative/main.html | 19 - test/test-all/webapp-tpl/package.json | 18 - test/test-all/webapp-tpl/public/tag-list.html | 17 - test/test-all/webapp-tpl/reputation/main.html | 22 - test/test-all/webapp-tpl/setting/main.html | 49 - test/test-all/webapp-tpl/tag/main.html | 17 - test/test-all/webapp-tpl/weibo/list.html | 17 - test/test-all/webapp-tpl/weibo/main.html | 14 - test/tpl/build/template.js | 14 +- test/tpl/copyright.html | 2 +- test/tpl/index.html | 3 +- test/tpl/package.json | 8 +- 114 files changed, 2132 insertions(+), 4022 deletions(-) create mode 100644 .npmignore create mode 100644 src/defaults.js create mode 100644 src/runtime.js delete mode 100644 src/runtime/basic.js delete mode 100644 src/runtime/full.js create mode 100644 src/syntax/native.js create mode 100644 src/syntax/simple.js create mode 100644 src/uglify2.js create mode 100644 test/test-all/helper/build/index.js delete mode 100644 test/test-all/helper/index.html delete mode 100644 test/test-all/mix-cmd.html delete mode 100644 test/test-all/mix-cmd/build/copyright.js delete mode 100644 test/test-all/mix-cmd/build/index.js delete mode 100644 test/test-all/mix-cmd/build/public/footer.js delete mode 100644 test/test-all/mix-cmd/build/public/header.js delete mode 100644 test/test-all/mix-cmd/build/public/logo.js delete mode 100644 test/test-all/mix-cmd/build/template.js delete mode 100644 test/test-all/mix-cmd/copyright.html delete mode 100644 test/test-all/mix-cmd/index.html delete mode 100644 test/test-all/mix-cmd/package.json delete mode 100644 test/test-all/mix-cmd/public/footer.html delete mode 100644 test/test-all/mix-cmd/public/header.html delete mode 100644 test/test-all/mix-cmd/public/logo.html delete mode 100644 test/test-all/mix.html delete mode 100644 test/test-all/mix/build/copyright.js delete mode 100644 test/test-all/mix/build/index.js delete mode 100644 test/test-all/mix/build/public/footer.js delete mode 100644 test/test-all/mix/build/public/header.js delete mode 100644 test/test-all/mix/build/public/logo.js delete mode 100644 test/test-all/mix/build/template.js delete mode 100644 test/test-all/mix/copyright.html delete mode 100644 test/test-all/mix/index.html delete mode 100644 test/test-all/mix/package.json delete mode 100644 test/test-all/mix/public/footer.html delete mode 100644 test/test-all/mix/public/header.html delete mode 100644 test/test-all/mix/public/logo.html delete mode 100755 test/test-all/webapp-tpl/404/main.html delete mode 100644 test/test-all/webapp-tpl/build/template.js delete mode 100755 test/test-all/webapp-tpl/detail/focus.html delete mode 100755 test/test-all/webapp-tpl/detail/hotTerm.html delete mode 100755 test/test-all/webapp-tpl/detail/keyword.html delete mode 100755 test/test-all/webapp-tpl/detail/main.html delete mode 100755 test/test-all/webapp-tpl/detail/negative.html delete mode 100755 test/test-all/webapp-tpl/detail/report.html delete mode 100755 test/test-all/webapp-tpl/detail/reputation.html delete mode 100755 test/test-all/webapp-tpl/detail/weibo-list.html delete mode 100755 test/test-all/webapp-tpl/detail/weibo.html delete mode 100755 test/test-all/webapp-tpl/home/main.html delete mode 100755 test/test-all/webapp-tpl/home/project.html delete mode 100755 test/test-all/webapp-tpl/negative/main.html delete mode 100644 test/test-all/webapp-tpl/package.json delete mode 100755 test/test-all/webapp-tpl/public/tag-list.html delete mode 100755 test/test-all/webapp-tpl/reputation/main.html delete mode 100755 test/test-all/webapp-tpl/setting/main.html delete mode 100755 test/test-all/webapp-tpl/tag/main.html delete mode 100755 test/test-all/webapp-tpl/weibo/list.html delete mode 100755 test/test-all/webapp-tpl/weibo/main.html diff --git a/ .npmignore b/ .npmignore new file mode 100644 index 0000000..6db059f --- /dev/null +++ b/ .npmignore @@ -0,0 +1,3 @@ +test +demo +doc \ No newline at end of file diff --git a/README.md b/README.md index 0461d69..c69e24e 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,51 @@ # TmodJS -TmodJS(原名 atc)是前端模板开发工具,它采用预编译技术让前端模板实现工程化。 +TmodJS(原名 atc)是一个简单易用的前端模板预编译工具,使用它可以实现前端模板模块化与自动化集成,让前端模板拥有后端模板一样的同步“文件”加载能力: -## 为什么要使用它? +一、**按文件与目录组织模板** -从前,浏览器甚至都没有提供一个好用的调试工具,我们依然能够通过 alert 完成手头上的工作,似乎就这样满足了。突然有一天你使用了 Firebug 等工具后,你才会知道以前缺乏工具的日子过得多么艰苦。 - -现在我们手上已经有了很多基础工具用来解决前端工程中不同的问题,而在前端模板领域却一直缺乏有效的自动优化工具,而 TmodJS 便是这样一个专注于前端模板的辅助开发工具。 +``` +template('tpl/home/main', data) +``` -对于前端开发者而言,TmodJS 可以像你现在手上使用的开发利器一样:一旦上手,便爱不释手。 +二、**模板支持引入子模板** -使用 TmodJS,下面美好即将发生: +``` +{{include '../public/header'}} +``` -1. 在开发阶段将编译模板为高性能的 js 文件 -2. 实现前端模板按文件与目录组织,脱离页面 -3. 支持模板之间支持引入外部模板 +## 目录 + +* [特性](#特性) +* [安装](#安装) +* [编写模板](#编写模板) +* [编译模板](#编译模板) +* [使用模板](#使用模板) +* [演示](#演示) +* [配置](#配置) +* [GruntJS](#GruntJS) +* [常见问题](#常见问题) +* [更新日志](#更新日志) +* [加入我们](#加入我们) +* [授权协议](#授权协议) + +## 特性 + +0. 在开发阶段将编译模板为高性能的 js 文件 +1. 前端模板按文件与目录组织 +3. 模板之间支持引入外部模板 4. 使用同步模板加载接口 -5. 可选输出多种 js 模块格式 -6. 支持接入第三方构建工具 -7. 编译后的模板支持前端与后端运行 +5. 支持多种 js 模块输出:AMD、CMD、CommonJS +6. 支持作为 GruntJS 的插件构建项目 +7. 支持前后台模板共享模板目录 8. 支持检测修改即时编译 -9. 支持本地调试模板 +9. 支持本地调试 -有点意思?请继续阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 +若想深入了解,请继续阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 ## 安装 -先安装 [NodeJS](http://nodejs.org) 与 Npm (最新版 NodeJS 已经附带 Npm),执行: +使用 [NodeJS](http://nodejs.org) 附带的``npm``命令,执行: ``` npm install -g tmodjs @@ -34,184 +53,131 @@ npm install -g tmodjs > Mac OSX 可能需要管理员权限运行: ``sudo npm install -g tmodjs`` +## 编写模板 -## 快速入门 - -学习 TmodJS 只需要理解这四个关键点就好,3 分钟可入门: +TmodJS 的前端模板不再耦合在业务页面中,而是和后端模板一样有专门的目录管理。目录名称只支持英文、数字、下划线的组合,一个模板对应一个``.html``文件。 -### 一、前端模板目录 +支持基本的模板语法,如输出变量、条件判断、循环、包含子模板。[模板语法参考](https://github.com/aui/tmodjs/wiki/模板语法) -TmodJS 的前端模板不再耦合在业务页面中,而是和后端模板一样有专门的目录管理。目录名称支持英文、数字、下划线。 +> 完全支持 [artTemplate](https://github.com/aui/artTemplate) 的语法 -### 二、模板与语法 - -一个模板对应一个文件,模板后缀可以是``.html``、``.htm``、``.tpl``。 - -模板支持输出变量、条件判断、循环、包含子模板,请查看:[模板语法参考](https://github.com/aui/tmodjs/wiki/模板语法) - -### 三、编译模板 +## 编译模板 ``` -tmod [模板根目录] [配置参数] +tmod [模板目录] [配置参数] ``` -#### 模板目录 +必须是模板的根目录,若无参数则为默认使用当前工作目录,tmodjs 会监控模板目录修改,每次模板修改都会增量编译。 -可以不填,默认使用当前工作目录。 - -#### 配置参数 +### 配置参数 -* ``-d``或``--debug``输出调试版本 -* ``--charset value``定义模板编码,默认``utf-8`` -* ``--output value``定义输出目录,默认``./build`` -* ``--type value``定义输出模块格式,默认``default``,可选``cmd``、``amd``、``commonjs`` -* ``--watch-off``不监控模板修改 -* ``--version``显示版本号 -* ``--help``显示帮助信息 +* ``--debug`` 输出调试版本 +* ``--charset value`` 定义模板编码,默认``utf-8`` +* ``--output value`` 定义输出目录,默认``./build`` +* ``--type value`` 定义输出模块格式,默认``default``,可选``cmd``、``amd``、``commonjs`` +* ``--no-watch`` 关闭模板目录监控 +* ``--version`` 显示版本号 +* ``--help`` 显示帮助信息 -配置参数将会保存在模板目录[配置文件](#配置)中,下次运行无需输入配置参数(``-d`` 与 ``--watch-off`` 除外)。 +配置参数将会保存在模板目录[配置文件](#配置)中,下次运行无需输入配置参数(``--no-watch`` 与 ``--debug`` 除外)。 #### 示例 ``` -tmod ./tpl --debug +tmod ./tpl --output ./build ``` > 如果需要设置模板更多的编译选项,请使用``--config``参数,它会打开模板目录的项目配置文件,可设置语法、公用辅助方法、压缩选项等,参考[配置](#配置)。 -### 四、调用模板 - -模板编译后,模板目录会生成 build 子目录,里面包含了所有的模板编译版本。编译后的模板可以使用同步接口加载模板。 +## 使用模板 -需要注意的是,TmodJS 的 ``type``参数设置会改变模板的加载方式。 +根据编译的``type``的配置不同,会有两种不同使用方式: -#### 1. 默认类型格式的使用方式 +### 使用默认的格式 -模板的``type``为默认值(``type:default``)的时候,TmodJS 默认将整个目录的模板压缩合并到一个名为 template.js 的脚本中,通常情况下你只需要在页面中引入它就好。例如: +TmodJS 默认将整个目录的模板压缩打包到一个名为 template.js 的脚本中,可直接在页面中使用它: + -除此之外 template.js 还支持 RequireJS、SeaJS、NodeJS 加载。[示例](http://aui.github.io/tmodjs/test/index.html) +> template.js 还支持 RequireJS、SeaJS、NodeJS 加载。[示例](http://aui.github.io/tmodjs/test/index.html) -加载并渲染模板示例: +### 指定格式(amd / cmd / commonjs) -``` -// 注意:模板路径不能包含后缀名 -var html = template('news/list', {hot: [...]}); -document.getElementById('list').innerHTML = html; -``` - -以上是 TmodJS 默认的模板加载方式,其他``type``请参考: - -#### 2. 其他类型格式使用方式(amd / cmd / commonjs) - -这个时候每个模板编译后都是一个 js 模块,每个模板可在模块中单独引入,无需引入 template.js 文件,加载并渲染模板示例: +此时每个模板就是一个单独的模块,无需引用 template.js: ``` -var html = require('./tpl/build/news/list'); -document.getElementById('list').innerHTML = html; +var render = require('./tpl/build/news/list'); +var html = render(_list); ``` -## 编译演示项目 +> 注意:模板路径不能包含模板后缀名 -[TmodJS 源码包](https://github.com/aui/tmodjs/archive/master.zip)中``./test/tpl``是一个演示项目的前端模板目录。你可以通过这个演示项目快速了解 TmodJS 用法以及模板语法、模板加载方式。 +## 演示 -首先,使用``cd``命令切换到 TmodJS 源码的``./test/tpl``目录后,然后运行命令: +[TmodJS 源码包](https://github.com/aui/tmodjs/archive/master.zip)中``test/tpl``是一个演示项目的前端模板目录,基于默认配置。切换到源码目录后,编译: ``` -tmod +tmod test/tpl ``` -编译完毕后你可以在浏览器中打开 [test/index.html](http://aui.github.io/tmodjs/test/index.html) 查看如何加载模板。 - -## 对外接口 - -若想集成 TmodJS 到其它自动化工具中(如 GruntJS),可以使用 TmodJS 提供的 API 来接入: - -``` -var TmodJS = require('tmodjs'); - -// 模板目录 -var path = './demo/templates'; - -// 配置(更多请参考文档) -var options = { - output: './build', - charset: 'utf-8', - debug: false // 此字段不会保存在配置中 -}; - -// 初始化 TmodJS -// path {String} 模板根目录 -// options {Object} 选项 -TmodJS.init(path, options); - -// 监听编译过程的事件 -// 支持的事件有:compile、change、load、compileError、combo -TmodJS.on('compile', function (data) {}); - -// 编译模板 -// file {String} 参数可选,无则编译整个模板目录,否则编译指定的模板文件 -// recursion {Boolean} 若为 false 则不编译依赖的模板 -TmodJS.compile(file, recursion); - -// 监控模板修改 -TmodJS.watch(); - -// 获取用户配置 -//TmodJS.getUserConfig(); - -// 保存用户设置到模板目录 package.json 文件中 -TmodJS.saveUserConfig(); - -``` +编译完毕后你可以在浏览器中打开 [test/index.html](http://aui.github.io/tmodjs/test/index.html) 查看如何使用编译后的模板。 ## 配置 -配置最终会保存在模板目录 package.json 文件中,你可以修改``"tmodjs-config"``字段,配置说明: +TmodJS 的项目配置文件保存在模板目录的 package.json 中,省去每次输入命令行参数: ``` -// 编译输出目录设置 -output: './build', - -// 模板使用的编码。(注意:非 utf-8 编码的模板缺乏测试) -charset: 'utf-8', - -// 定义模板采用哪种语法,内置可选: -// simple: 默认语法,易于读写。可参看语法文档 -// native: 功能丰富,灵活多变。语法类似微型模板引擎 tmpl -syntax: 'simple', - -// 自定义辅助方法路径 -helpers: null, - -// 是否过滤 XSS -// 如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率 -escape: true, +{ + "name": "template", + "version": "1.0.0", + "dependencies": { + "tmodjs": "1.0.0" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": null, + "escape": true, + "compress": true, + "type": "default", + "runtime": "template.js", + "combo": true, + "minify": true, + "cache": false + } +} +``` -// 是否嵌入模板引擎,否则编译为不依赖引擎的纯 js 代码 -// 选择嵌入模板引擎后,模板以字符串存储并浏览器中执行编译 -engine: false, +字段 | 类型 | 默认值| 说明 +------------ | ------------- | ------------ | ------------ +output | String | ``"./build"`` | 编译输出目录设置 +charset | String | ``"utf-8"`` | 模板使用的编码 +syntax | String | ``"simple"`` | 定义模板采用哪种语法。可选:``simple``、``native`` +helpers | String | ``null`` | 自定义辅助方法路径 +escape | Boolean | ``true`` | 是否过滤 XSS。如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率 +compress | Boolean | ``true`` | 是否压缩 HTML 多余空白字符 +type | String | ``"default"`` | 输出的模块类型,可选:``default``、``cmd``、``amd``、``commonjs`` +runtime | String | ``"template.js"`` | 设置输出的运行时名称 +alias | String | ``null`` | 设置模块依赖的运行时路径(仅针对于非``default``的类型模块配置字段。如果不指定模块内部会自动使用相对 runtime 的路径) +combo | Boolean | ``true`` | 是否合并模板(仅针对于 default 类型的模块) +minify | Boolean | ``true`` | 是否输出为压缩的格式 +cache | Boolean | ``true`` | 是否开启编译缓存 + +## GruntJS -// 输出的模块类型,可选: -// default: 模板目录将会打包后输出,可使用 script 标签直接引入,也支持 NodeJS/RequireJS/SeaJS。 -// cmd: 这是一种兼容 RequireJS/SeaJS 的模块(类似 atc v1版本编译结果) -// amd: 支持 RequireJS 等流行加载器 -// commonjs: 编译为 NodeJS 模块 -type: 'default', +让 TmodJS 作为 GruntJS 的一个插件使用: -// 运行时别名 -// 仅针对于非 default 的类型模块 -alias: null, +``` +npm install grunt-tmod --save-dev +``` -// 是否合并模板 -// 仅针对于 default 类型的模块 -combo: true, +由[@Jsonzhang](https://github.com/Jsonzhang)同学开发,项目主页: -// 是否输出为压缩的格式 -minify: true -``` - ## 常见问题 **问**:TmodJS 需要部署到服务器中吗? @@ -256,16 +222,21 @@ minify: true **问**:我需要手动合并模板,如何让 tmodjs 不合并输出? -> 编辑配置文件,设置``combo: false``。 +> 编辑配置文件,设置``combo:false``。 ## 更新日志 +### v1.0.0 + +* 使用 artTemplate3.0 作为模板引擎,NodJS 可直接共享前端的模板目录的模板,无需预编译 +* 提供 GruntJS 插件 +* 取消``engine``配置 +* 增加``runtime``配置 +* 接口重构,支持多实例 + ### v0.1.1 -* 修复无逻辑的模板在 Safari 浏览器上兼容问题 -* 默认开启模板实时监控。取消请使用``--watch-off`` * 给压缩打包合并后的每条模板增加版本标记,例如``/*v:13*/``,以便对比线上版本 -* 增加``compileStart``与``compileEnd``事件 ### v0.1.0 @@ -297,7 +268,7 @@ minify: true ### v0.0.1 -这是一个革命性的版本!同时项目更名为 **TmodJS**,内部版本号收归到 0.0.1,这是一个新的开始,未来将稳步更新。 +这是一个革命性的版本,内部大量优化!同时项目更名为 **TmodJS**,内部版本号收归到 0.0.1。 使用 TemplageJS 格式的模块作为默认输出的类型:它包含模板目录中所有模板,除了支持页面 Script 直接引入之外还支持 RequireJS、SeaJS、NodeJS 加载,并且接口统一,跨架构与前后端运行! @@ -306,8 +277,8 @@ minify: true * 吸收了来自业务的一些建议,编译方案的大调整,内部进行无数次优化,编译后的代码更小。 * 编译后的脚本使用统一的接口:``template(path, data)`` 其中 path 相对于 template.js 所在目录 * 自动打包目录与子目录的模板 -* 可选支持异步载入模板功能 -* 可选嵌入完整模板引擎(使用字符串存储模板) +* ~~可选支持异步载入模板功能~~ +* ~~可选嵌入完整模板引擎(使用字符串存储模板)~~ * 可选支持 RequireJS/SeaJS/NodeJS 模块 * 保存模板配置文件(方便多人协作中使用版本管理工具共享配置) * 可选编译调试版本 @@ -367,11 +338,16 @@ NodeJS 版本: ### 贡献名单 -* [@aui](https://github.com/aui)(糖饼,来自 QQ 空间前端团队) -* [@TooBug](https://github.com/TooBug)(来自 CDC 前端团队) -* [@Jsonzhang](https://github.com/Jsonzhang)(来自 CDC 前端团队,GruntJS 插件开发者) +* [@aui](https://github.com/aui) +* [@TooBug](https://github.com/TooBug) +* [@Jsonzhang](https://github.com/Jsonzhang) + +目前贡献者均来自腾讯前端团队。 ### 特别感谢 * [@warmhug](https://github.com/warmhug)(在工具雏形阶段的热心的测试与反馈) +## 授权协议 + +BSD. \ No newline at end of file diff --git a/bin/tmod b/bin/tmod index e13c9b2..a77a9d4 100644 --- a/bin/tmod +++ b/bin/tmod @@ -15,40 +15,24 @@ var options = {}; var help = function () { var message = [ - '\x1B[7mTmodJS\x1B[27m - Template Compiler', + '\x1B[7mTmodJS - Template Compiler\x1B[27m', '', - 'Usage:', - ' tmod [path] [options]', - 'Options:', + 'Usage', + ' tmod [options]', + '', + 'Options', [ - - ' -d, --debug', - '\x1B[90m debugging Template\x1B[39m', - - ' --watch-off', - '\x1B[90m do not use in-time compilation\x1B[39m', - - ' --type', - '\x1B[90m optional: ', - ' templatejs (Default)', - ' cmd (RequireJS/SeaJS)', - ' amd (RequireJS)', - ' commonjs (NodeJS)\x1B[39m', - - ' --output value', - '\x1B[90m defining an output directory\x1B[39m', - - ' --charset value', - '\x1B[90m charset, utf-8 by default\x1B[39m', - - ' --version', - '\x1B[90m display the version of TmodJS\x1B[39m', - - ' --help', - '\x1B[90m show this help infomation\x1B[39m' + ' -d, --debug Debugging Template', + ' --type Optional: default | cmd (RequireJS/SeaJS) | amd (RequireJS) |', + ' commonjs (NodeJS)', + ' --output value Defining an output directory', + '--charset value Charset, "utf-8" by default', + ' --no-watch Does not monitor template directory', + ' --version Print the tmodjs version', + ' --help Display this help text' ].join('\n'), '', - '\x1B[90m' + 'Documentation can be found at http://aui.github.io/tmodjs/' + '\x1B[39m' + '\x1B[90m' + 'Documentation can be found at https://github.com/aui/tmodjs' + '\x1B[39m' ]; message = message.join('\n'); @@ -57,16 +41,16 @@ var help = function () { -var dir; +var base; var value; var userConfig; -var isWatch = true; +var isWatch = false; var isEditConfig = false; var args = process.argv.slice(2); if (args[0] && /^[^-]|\//.test(args[0])) { - dir = args.shift(); + base = args.shift(); } @@ -80,7 +64,7 @@ while (args.length > 0) { isWatch = true; break; - case '--watch-off': + case '--no-watch': isWatch = false; break; @@ -90,25 +74,16 @@ while (args.length > 0) { options.debug = true; break; - case '--debug-off': + case '--no-debug': options.debug = false; break; - // 嵌入引擎 - case '--engine': - options.engine = true; - break; - - case '--engine--off': - options.engine = false; - break; - // 对输出值编码 case '--escape': options.escape = true; break; - case '--escape-off': + case '--no-escape': options.escape = false; break; @@ -117,7 +92,7 @@ while (args.length > 0) { options.combo = true; break; - case '--combo-off': + case '--no-combo': options.combo = false; break; @@ -126,10 +101,18 @@ while (args.length > 0) { options.minify = true; break; - case '--minify-off': + case '--no-minify': options.minify = false; break; + // 使用缓存 + case '--cache': + options.cache = true; + break; + case '--no-cache': + options.cache = false; + break; + // 输出目录 case '--output': options.output = args.shift(); @@ -175,52 +158,35 @@ while (args.length > 0) { default: - if (!dir) { - dir = value; + if (!base) { + base = value; } } } -if (!dir) { - dir = './'; +if (!base) { + base = './'; } -if (!fs.existsSync(dir)) { +if (!fs.existsSync(base)) { process.stdout.write('Error: directory does not exist\n'); - help(); process.exit(1); } -// 转换成相对于模板目录的路径 - -if (options.output) { - options.output = path.relative(dir, path.resolve(options.output)); -} - -if (options.syntax && /\.js$/.test(options.syntax)) {// 值可能为内置名称:native || simple - options.syntax = path.relative(dir, path.resolve(options.syntax)); -} - -if (options.helpers) { - options.helpers = path.relative(dir, path.resolve(options.helpers)); -} - +var tmodjs = new TmodJS(base, options); -TmodJS.init(dir, options); - - -TmodJS.on('compileError', function (data) { +tmodjs.on('compileError', function (data) { if (!isWatch) { process.exit(1); } }); -userConfig = TmodJS.saveUserConfig(); +userConfig = tmodjs.saveConfig(); if (isEditConfig) { @@ -232,12 +198,13 @@ if (isEditConfig) { + ' ' + userConfig, {timeout: 0}, function () {} ); + } else { - TmodJS.compile(); + tmodjs.compile(); if (isWatch) { - TmodJS.watch(); + tmodjs.watch(); } } diff --git a/doc/helper.md b/doc/helper.md index 8af993e..c0c4170 100644 --- a/doc/helper.md +++ b/doc/helper.md @@ -6,40 +6,55 @@ ## 一、新建一个辅助方法文件配置 -在模板目录新建一个 ./config/template-helper.js 文件,然后编辑 ./package.json 设置``"helpers": "./config/template-helper.js"``。 +在模板目录新建一个 config/template-helper.js 文件,然后编辑 package.json 设置``"helpers": "./config/template-helper.js"``。 ## 二、编写辅助方法 -在 ./config/template-helper.js 中声明辅助方法。 +在 config/template-helper.js 中声明辅助方法。 ### 示例 -1\. 让模板可访问全局的``Math``对象: +以日期格式化为例: ``` -template.helper('Math', Math); -``` - -2\. 扩展一个 UBB 替换方法: - -``` -template.helper('$ubb2html', function (content) { - return content - .replace(/\[b\]([^\[]*?)\[\/b\]/igm, '$1') - .replace(/\[i\]([^\[]*?)\[\/i\]/igm, '$1') - .replace(/\[u\]([^\[]*?)\[\/u\]/igm, '$1') - .replace(/\[url=([^\]]*)\]([^\[]*?)\[\/url\]/igm, '$2') - .replace(/\[img\]([^\[]*?)\[\/img\]/igm, ''); +template.helper('dateFormat', function (date, format) { + + date = new Date(date); + + var map = { + "M": date.getMonth() + 1, //月份 + "d": date.getDate(), //日 + "h": date.getHours(), //小时 + "m": date.getMinutes(), //分 + "s": date.getSeconds(), //秒 + "q": Math.floor((date.getMonth() + 3) / 3), //季度 + "S": date.getMilliseconds() //毫秒 + }; + format = format.replace(/([yMdhmsqS])+/g, function(all, t){ + var v = map[t]; + if(v !== undefined){ + if(all.length > 1){ + v = '0' + v; + v = v.substr(v.length-2); + } + return v; + } + else if(t === 'y'){ + return (date.getFullYear() + '').substr(4 - all.length); + } + return all; + }); + return format; }); ``` + ## 三、在模板中使用辅助方法 - -在模板中的使用方式: ``` -{{Math.min(1000, a, b)}} -{{$ubb2html content}} +{{time | dateFormat:'yyyy-MM-dd hh:mm:ss'}} ``` -> 注意:引擎不会对辅助方法输出的 HTML 字符进行转义。 \ No newline at end of file +---------------------------------------------- + +本文档针对 TmodJS v1.0.0+ 编写 \ No newline at end of file diff --git a/doc/syntax-simple.md b/doc/syntax-simple.md index b46b009..5fe89b5 100644 --- a/doc/syntax-simple.md +++ b/doc/syntax-simple.md @@ -4,71 +4,56 @@ TmodJS 默认采用 simple 语法,它非常易于读写。 -## 表达式 - -``{{``与``}}``符号包裹起来的语句则为模板的逻辑表达式。 - -### 输出表达式 - -输出模板变量: +## 表达式 -``` -{{content}} -``` - -默认会对变量中 HTML 字符编码输出,避免 XSS 漏洞。 - -输出原始模板变量 - 不编码: +``{{`` 与 ``}}`` 符号包裹起来的语句则为模板的逻辑表达式。 -``` -{{#content}} -``` - -### 条件表达式 - -``` -{{if admin}} - {{content}} -{{/if}} -{{if user === 'admin'}} - {{content}} -{{else if user === '007'}} - hello world -{{/if}} -``` - -### 遍历表达式 - -无论数组或者对象都可以用``each``进行遍历。 +### 输出表达式 -``` -{{each list}} -
  • {{$index}}. {{$value.user}}
  • -{{/each}} -``` +对内容编码输出: -其中 list 为要遍历的数据名称, ``$value`` 与 ``$index`` 是系统变量, ``$value`` 表示数据单条内容, ``$index`` 表示索引值,这两个变量也可以自定义: + {{content}} -``` -{{each list as value index}} -
  • {{index}}. {{value.user}}
  • -{{/each}} -``` - -### 模板包含表达式 - -例如嵌入一个 inc 目录下名为 demo 的模板: +不编码输出: -``` -{{include './inc/demo'}} -``` - -还可以传入指定的数据到子模板: + {{#content}} + +编码可以防止数据中含有 HTML 字符串,避免引起 XSS 攻击。 -``` -{{include './inc/demo' data}} -``` - +### 条件表达式 + + {{if admin}} +

    admin

    + {{else if code > 0}} +

    master

    + {{else}} +

    error!

    + {{/if}} + +### 遍历表达式 + +无论数组或者对象都可以用 each 进行遍历。 + + {{each list as value index}} +
  • {{index}} - {{value.user}}
  • + {{/each}} + +亦可以被简写: + + {{each list}} +
  • {{$index}} - {{$value.user}}
  • + {{/each}} + +### 模板包含表达式 + +用于嵌入子模板。 + + {{include 'template_name'}} + +子模板默认共享当前数据,亦可以指定数据: + + {{include 'template_name' news_list}} + #### include 路径规范约定 1. 路径不能带后缀名 @@ -78,4 +63,14 @@ TmodJS 默认采用 simple 语法,它非常易于读写。 ## 辅助方法 -这属于插件的范畴,请参考: + {{time | dateFormat:'yyyy-MM-dd hh:mm:ss'}} + +支持传入参数与嵌套使用: + + {{time | say:'cd' | ubb | link}} + +定义辅助方法请参考: + +---------------------------------------------- + +本文档针对 TmodJS v1.0.0+ 编写 \ No newline at end of file diff --git a/doc/upgrade.md b/doc/upgrade.md index d80485b..e4b412f 100644 --- a/doc/upgrade.md +++ b/doc/upgrade.md @@ -1,6 +1,6 @@ # 页面中的模板迁移指南 -如果你之前项目中采用的是 artTemplate,则很容易迁移到 TModJS 中来,因为 TModJS 也是基于 artTemplate 的子项目,不同的是模板是预编译的。 +如果你之前项目中采用的是 artTemplate 则很容易迁移到 TmodJS 中来,因为 TmodJS 也是基于 artTemplate 的子项目,不同的是模板是预编译的。 迁移过程比较简单,大约如下四个步骤: @@ -19,9 +19,9 @@ ``` -你可以将`` - - - - - - -
    loading..
    -
    - - - - - - - - - - - - diff --git a/test/test-all/mix-cmd/build/copyright.js b/test/test-all/mix-cmd/build/copyright.js deleted file mode 100644 index e476f50..0000000 --- a/test/test-all/mix-cmd/build/copyright.js +++ /dev/null @@ -1,4 +0,0 @@ -/*TMODJS:{"version":6,"md5":"822614428504528e6c32975732c23529"}*/ -define(function(require) { - return require("./template")("copyright", "(c) 2013"); -}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/index.js b/test/test-all/mix-cmd/build/index.js deleted file mode 100644 index 5a60a88..0000000 --- a/test/test-all/mix-cmd/build/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/*TMODJS:{"version":6,"md5":"284024eb8a7ac5ba1213cc8cd4cbf724"}*/ -define(function(require) { - require("./public/header"); - require("./public/footer"); - return require("./template")("index", "{{include './public/header'}}

    {{title}}

    {{include './public/footer'}}"); -}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/footer.js b/test/test-all/mix-cmd/build/public/footer.js deleted file mode 100644 index bf35058..0000000 --- a/test/test-all/mix-cmd/build/public/footer.js +++ /dev/null @@ -1,5 +0,0 @@ -/*TMODJS:{"version":6,"md5":"4ce5fc96554db3a43286a412b2a968b8"}*/ -define(function(require) { - require("../copyright"); - return require("../template")("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); -}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/header.js b/test/test-all/mix-cmd/build/public/header.js deleted file mode 100644 index 7727d82..0000000 --- a/test/test-all/mix-cmd/build/public/header.js +++ /dev/null @@ -1,5 +0,0 @@ -/*TMODJS:{"version":6,"md5":"9004d8ea5b1456fd2f871f905bf2afc2"}*/ -define(function(require) { - require("./logo"); - return require("../template")("public/header", ' '); -}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/public/logo.js b/test/test-all/mix-cmd/build/public/logo.js deleted file mode 100644 index db1ee6a..0000000 --- a/test/test-all/mix-cmd/build/public/logo.js +++ /dev/null @@ -1,4 +0,0 @@ -/*TMODJS:{"version":6,"md5":"509fff56bc3ad95846f5eb6e687974a4"}*/ -define(function(require) { - return require("../template")("public/logo", '

    腾讯网

    '); -}); \ No newline at end of file diff --git a/test/test-all/mix-cmd/build/template.js b/test/test-all/mix-cmd/build/template.js deleted file mode 100644 index a11d7f2..0000000 --- a/test/test-all/mix-cmd/build/template.js +++ /dev/null @@ -1,360 +0,0 @@ -/*TMODJS:{}*/ -(function(global) { - "use strict"; - var template = function(id, content) { - return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); - }; - template.version = "2.0.3"; - template.openTag = "<%"; - template.closeTag = "%>"; - template.isEscape = true; - template.isCompress = false; - template.parser = null; - template.render = function(id, data) { - var cache = template.get(id) || _debug({ - id: id, - name: "Render Error", - message: "No Template" - }); - return cache(data); - }; - template.compile = function(id, source) { - var params = arguments; - var isDebug = params[2]; - var anonymous = "anonymous"; - if (typeof source !== "string") { - isDebug = params[1]; - source = params[0]; - id = anonymous; - } - try { - var Render = _compile(id, source, isDebug); - } catch (e) { - e.id = id || source; - e.name = "Syntax Error"; - return _debug(e); - } - function render(data) { - try { - return new Render(data, id) + ""; - } catch (e) { - if (!isDebug) { - return template.compile(id, source, true)(data); - } - return _debug(e)(); - } - } - render.prototype = Render.prototype; - render.toString = function() { - return Render.toString(); - }; - if (id !== anonymous) { - _cache[id] = render; - } - return render; - }; - var _cache = template.cache = {}; - var _helpers = template.helpers = function() { - var toString = function(value, type) { - if (typeof value !== "string") { - type = typeof value; - if (type === "number") { - value += ""; - } else if (type === "function") { - value = toString(value.call(value)); - } else { - value = ""; - } - } - return value; - }; - var escapeMap = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - var escapeHTML = function(content) { - return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return escapeMap[s]; - }); - }; - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; - var each = function(data, callback) { - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } - } else { - for (i in data) { - callback.call(data, data[i], i); - } - } - }; - return { - $include: template.render, - $string: toString, - $escape: escapeHTML, - $each: each - }; - }(); - template.helper = function(name, helper) { - _helpers[name] = helper; - }; - template.onerror = function(e) { - var message = "Template Error\n\n"; - for (var name in e) { - message += "<" + name + ">\n" + e[name] + "\n\n"; - } - if (global.console) { - console.error(message); - } - }; - template.get = function(id) { - var cache; - if (_cache.hasOwnProperty(id)) { - cache = _cache[id]; - } else if ("document" in global) { - var elem = document.getElementById(id); - if (elem) { - var source = elem.value || elem.innerHTML; - cache = template.compile(id, source.replace(/^\s*|\s*$/g, "")); - } - } - return cache; - }; - var _debug = function(e) { - template.onerror(e); - return function() { - return "{Template Error}"; - }; - }; - var _compile = function() { - var forEach = _helpers.$each; - var KEYWORDS = "break,case,catch,continue,debugger,default,delete,do,else,false" + ",finally,for,function,if,in,instanceof,new,null,return,switch,this" + ",throw,true,try,typeof,var,void,while,with" + ",abstract,boolean,byte,char,class,const,double,enum,export,extends" + ",final,float,goto,implements,import,int,interface,long,native" + ",package,private,protected,public,short,static,super,synchronized" + ",throws,transient,volatile" + ",arguments,let,yield" + ",undefined"; - var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; - var SPLIT_RE = /[^\w$]+/g; - var KEYWORDS_RE = new RegExp([ "\\b" + KEYWORDS.replace(/,/g, "\\b|\\b") + "\\b" ].join("|"), "g"); - var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g; - var BOUNDARY_RE = /^,+|,+$/g; - var getVariable = function(code) { - return code.replace(REMOVE_RE, "").replace(SPLIT_RE, ",").replace(KEYWORDS_RE, "").replace(NUMBER_RE, "").replace(BOUNDARY_RE, "").split(/^$|,+/); - }; - return function(id, source, isDebug) { - var openTag = template.openTag; - var closeTag = template.closeTag; - var parser = template.parser; - var code = source; - var tempCode = ""; - var line = 1; - var uniq = { - $data: 1, - $id: 1, - $helpers: 1, - $out: 1, - $line: 1 - }; - var prototype = {}; - var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); - var isNewEngine = "".trim; - var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; - var concat = isNewEngine ? "$out+=$text;return $text;" : "$out.push($text);"; - var print = "function($text){" + concat + "}"; - var include = "function(id,data){" + "data=data||$data;" + "var $text=$helpers.$include(id,data,$id);" + concat + "}"; - forEach(code.split(openTag), function(code) { - code = code.split(closeTag); - var $0 = code[0]; - var $1 = code[1]; - if (code.length === 1) { - tempCode += html($0); - } else { - tempCode += logic($0); - if ($1) { - tempCode += html($1); - } - } - }); - code = tempCode; - if (isDebug) { - code = "try{" + code + "}catch(e){" + "throw {" + "id:$id," + "name:'Render Error'," + "message:e.message," + "line:$line," + "source:" + stringify(source) + ".split(/\\n/)[$line-1].replace(/^[\\s\\t]+/,'')" + "};" + "}"; - } - code = variables + replaces[0] + code + "return new String(" + replaces[3] + ");"; - try { - var Render = new Function("$data", "$id", code); - Render.prototype = prototype; - return Render; - } catch (e) { - e.temp = "function anonymous($data,$id) {" + code + "}"; - throw e; - } - function html(code) { - line += code.split(/\n/).length - 1; - if (template.isCompress) { - code = code.replace(/[\n\r\t\s]+/g, " ").replace(//g, ""); - } - if (code) { - code = replaces[1] + stringify(code) + replaces[2] + "\n"; - } - return code; - } - function logic(code) { - var thisLine = line; - if (parser) { - code = parser(code); - } else if (isDebug) { - code = code.replace(/\n/g, function() { - line++; - return "$line=" + line + ";"; - }); - } - if (code.indexOf("=") === 0) { - var isEscape = !/^=[=#]/.test(code); - code = code.replace(/^=[=#]?|[\s;]*$/g, ""); - if (isEscape && template.isEscape) { - var name = code.replace(/\s*\([^\)]+\)/, ""); - if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { - code = "$escape(" + code + ")"; - } - } else { - code = "$string(" + code + ")"; - } - code = replaces[1] + code + replaces[2]; - } - if (isDebug) { - code = "$line=" + thisLine + ";" + code; - } - getKey(code); - return code + "\n"; - } - function getKey(code) { - code = getVariable(code); - forEach(code, function(name) { - if (!uniq.hasOwnProperty(name)) { - setValue(name); - uniq[name] = true; - } - }); - } - function setValue(name) { - var value; - if (name === "print") { - value = print; - } else if (name === "include") { - prototype["$include"] = _helpers["$include"]; - value = include; - } else { - value = "$data." + name; - if (_helpers.hasOwnProperty(name)) { - prototype[name] = _helpers[name]; - if (name.indexOf("$") === 0) { - value = "$helpers." + name; - } else { - value = value + "===undefined?$helpers." + name + ":" + value; - } - } - } - variables += name + "=" + value + ","; - } - function stringify(code) { - return "'" + code.replace(/('|\\)/g, "\\$1").replace(/\r/g, "\\r").replace(/\n/g, "\\n") + "'"; - } - }; - }(); - if (typeof define === "function") { - define(function() { - return template; - }); - } else if (typeof exports !== "undefined") { - module.exports = template; - } - global.template = template; -})(this); - -(function(exports) { - exports.openTag = "{{"; - exports.closeTag = "}}"; - exports.parser = function(code) { - code = code.replace(/^\s/, ""); - var split = code.split(" "); - var key = split.shift(); - var args = split.join(" "); - switch (key) { - case "if": - code = "if(" + args + "){"; - break; - - case "else": - if (split.shift() === "if") { - split = " if(" + split.join(" ") + ")"; - } else { - split = ""; - } - code = "}else" + split + "{"; - break; - - case "/if": - code = "}"; - break; - - case "each": - var object = split[0] || "$data"; - var as = split[1] || "as"; - var value = split[2] || "$value"; - var index = split[3] || "$index"; - var param = value + "," + index; - if (as !== "as") { - object = "[]"; - } - code = "$each(" + object + ",function(" + param + "){"; - break; - - case "/each": - code = "});"; - break; - - case "echo": - code = "print(" + args + ");"; - break; - - case "include": - code = "include(" + split.join(",") + ");"; - break; - - default: - if (exports.helpers.hasOwnProperty(key)) { - code = "=#" + key + "(" + split.join(",") + ");"; - } else { - code = code.replace(/[\s;]*$/, ""); - code = "=" + code; - } - break; - } - return code; - }; -})(template); - -!function(global, template) { - "use strict"; - var get = template.get; - var helpers = template.helpers; - var resolve = function(from, to) { - var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); - var id = dirname + to; - id = id.replace(/\/\.\//g, "/"); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, "/"); - } - return id; - }; - helpers.$include = function(uri, data, from) { - var id = resolve(from, uri); - return template.render(id, data); - }; - template.get = function(id) { - return get(id.replace(/^\.\//, "")); - }; -}(this, this.template); \ No newline at end of file diff --git a/test/test-all/mix-cmd/copyright.html b/test/test-all/mix-cmd/copyright.html deleted file mode 100644 index 4c65dfa..0000000 --- a/test/test-all/mix-cmd/copyright.html +++ /dev/null @@ -1 +0,0 @@ -(c) 2013 \ No newline at end of file diff --git a/test/test-all/mix-cmd/index.html b/test/test-all/mix-cmd/index.html deleted file mode 100644 index fc67525..0000000 --- a/test/test-all/mix-cmd/index.html +++ /dev/null @@ -1,12 +0,0 @@ -{{include './public/header'}} - -
    -

    {{title}}

    - -
    - -{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/mix-cmd/package.json b/test/test-all/mix-cmd/package.json deleted file mode 100644 index a455fce..0000000 --- a/test/test-all/mix-cmd/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "template", - "version": "1.0.0", - "dependencies": { - "tmodjs": "~0.1.1" - }, - "tmodjs-config": { - "output": "./build", - "charset": "utf-8", - "syntax": "simple", - "helpers": null, - "escape": true, - "engine": true, - "type": "cmd", - "alias": null, - "minify": false - } -} \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/footer.html b/test/test-all/mix-cmd/public/footer.html deleted file mode 100644 index 956c23e..0000000 --- a/test/test-all/mix-cmd/public/footer.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/header.html b/test/test-all/mix-cmd/public/header.html deleted file mode 100644 index d93e780..0000000 --- a/test/test-all/mix-cmd/public/header.html +++ /dev/null @@ -1,11 +0,0 @@ - - - \ No newline at end of file diff --git a/test/test-all/mix-cmd/public/logo.html b/test/test-all/mix-cmd/public/logo.html deleted file mode 100644 index b02f7cb..0000000 --- a/test/test-all/mix-cmd/public/logo.html +++ /dev/null @@ -1,7 +0,0 @@ - -

    - - 腾讯网 - -

    - \ No newline at end of file diff --git a/test/test-all/mix.html b/test/test-all/mix.html deleted file mode 100644 index ef884d8..0000000 --- a/test/test-all/mix.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - -TemplateJS - 调用模板演示 - - - - - -
    loading..
    -
    - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/test-all/mix/build/copyright.js b/test/test-all/mix/build/copyright.js deleted file mode 100644 index 4c01132..0000000 --- a/test/test-all/mix/build/copyright.js +++ /dev/null @@ -1,2 +0,0 @@ -/*TMODJS:{"version":2,"md5":"f79cd25e5fcbf6aa94ec3c78cf0498b0"}*/ -template("copyright", "(c) 2013"); \ No newline at end of file diff --git a/test/test-all/mix/build/index.js b/test/test-all/mix/build/index.js deleted file mode 100644 index 458a736..0000000 --- a/test/test-all/mix/build/index.js +++ /dev/null @@ -1,2 +0,0 @@ -/*TMODJS:{"version":2,"md5":"751ec4c8bd107d8a3d2ab0519632b638"}*/ -template("index", "{{include './public/header'}}

    {{title}}

    {{include './public/footer'}}"); \ No newline at end of file diff --git a/test/test-all/mix/build/public/footer.js b/test/test-all/mix/build/public/footer.js deleted file mode 100644 index f2fb96c..0000000 --- a/test/test-all/mix/build/public/footer.js +++ /dev/null @@ -1,2 +0,0 @@ -/*TMODJS:{"version":2,"md5":"37d3c2014346a9576171af879ac8243e"}*/ -template("public/footer", "
    {{if time}}

    {{time}}

    {{/if}} {{include '../copyright'}}
    "); \ No newline at end of file diff --git a/test/test-all/mix/build/public/header.js b/test/test-all/mix/build/public/header.js deleted file mode 100644 index d9e0e80..0000000 --- a/test/test-all/mix/build/public/header.js +++ /dev/null @@ -1,2 +0,0 @@ -/*TMODJS:{"version":2,"md5":"2c5721d735c7aaf0874b12c29569e934"}*/ -template("public/header", ' '); \ No newline at end of file diff --git a/test/test-all/mix/build/public/logo.js b/test/test-all/mix/build/public/logo.js deleted file mode 100644 index d294d4b..0000000 --- a/test/test-all/mix/build/public/logo.js +++ /dev/null @@ -1,2 +0,0 @@ -/*TMODJS:{"version":2,"md5":"c7507025a436fbbaad8418c25b526ff6"}*/ -template("public/logo", '

    腾讯网

    '); \ No newline at end of file diff --git a/test/test-all/mix/build/template.js b/test/test-all/mix/build/template.js deleted file mode 100644 index a11d7f2..0000000 --- a/test/test-all/mix/build/template.js +++ /dev/null @@ -1,360 +0,0 @@ -/*TMODJS:{}*/ -(function(global) { - "use strict"; - var template = function(id, content) { - return template[typeof content === "string" ? "compile" : "render"].apply(template, arguments); - }; - template.version = "2.0.3"; - template.openTag = "<%"; - template.closeTag = "%>"; - template.isEscape = true; - template.isCompress = false; - template.parser = null; - template.render = function(id, data) { - var cache = template.get(id) || _debug({ - id: id, - name: "Render Error", - message: "No Template" - }); - return cache(data); - }; - template.compile = function(id, source) { - var params = arguments; - var isDebug = params[2]; - var anonymous = "anonymous"; - if (typeof source !== "string") { - isDebug = params[1]; - source = params[0]; - id = anonymous; - } - try { - var Render = _compile(id, source, isDebug); - } catch (e) { - e.id = id || source; - e.name = "Syntax Error"; - return _debug(e); - } - function render(data) { - try { - return new Render(data, id) + ""; - } catch (e) { - if (!isDebug) { - return template.compile(id, source, true)(data); - } - return _debug(e)(); - } - } - render.prototype = Render.prototype; - render.toString = function() { - return Render.toString(); - }; - if (id !== anonymous) { - _cache[id] = render; - } - return render; - }; - var _cache = template.cache = {}; - var _helpers = template.helpers = function() { - var toString = function(value, type) { - if (typeof value !== "string") { - type = typeof value; - if (type === "number") { - value += ""; - } else if (type === "function") { - value = toString(value.call(value)); - } else { - value = ""; - } - } - return value; - }; - var escapeMap = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "&": "&" - }; - var escapeHTML = function(content) { - return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, function(s) { - return escapeMap[s]; - }); - }; - var isArray = Array.isArray || function(obj) { - return {}.toString.call(obj) === "[object Array]"; - }; - var each = function(data, callback) { - if (isArray(data)) { - for (var i = 0, len = data.length; i < len; i++) { - callback.call(data, data[i], i, data); - } - } else { - for (i in data) { - callback.call(data, data[i], i); - } - } - }; - return { - $include: template.render, - $string: toString, - $escape: escapeHTML, - $each: each - }; - }(); - template.helper = function(name, helper) { - _helpers[name] = helper; - }; - template.onerror = function(e) { - var message = "Template Error\n\n"; - for (var name in e) { - message += "<" + name + ">\n" + e[name] + "\n\n"; - } - if (global.console) { - console.error(message); - } - }; - template.get = function(id) { - var cache; - if (_cache.hasOwnProperty(id)) { - cache = _cache[id]; - } else if ("document" in global) { - var elem = document.getElementById(id); - if (elem) { - var source = elem.value || elem.innerHTML; - cache = template.compile(id, source.replace(/^\s*|\s*$/g, "")); - } - } - return cache; - }; - var _debug = function(e) { - template.onerror(e); - return function() { - return "{Template Error}"; - }; - }; - var _compile = function() { - var forEach = _helpers.$each; - var KEYWORDS = "break,case,catch,continue,debugger,default,delete,do,else,false" + ",finally,for,function,if,in,instanceof,new,null,return,switch,this" + ",throw,true,try,typeof,var,void,while,with" + ",abstract,boolean,byte,char,class,const,double,enum,export,extends" + ",final,float,goto,implements,import,int,interface,long,native" + ",package,private,protected,public,short,static,super,synchronized" + ",throws,transient,volatile" + ",arguments,let,yield" + ",undefined"; - var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g; - var SPLIT_RE = /[^\w$]+/g; - var KEYWORDS_RE = new RegExp([ "\\b" + KEYWORDS.replace(/,/g, "\\b|\\b") + "\\b" ].join("|"), "g"); - var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g; - var BOUNDARY_RE = /^,+|,+$/g; - var getVariable = function(code) { - return code.replace(REMOVE_RE, "").replace(SPLIT_RE, ",").replace(KEYWORDS_RE, "").replace(NUMBER_RE, "").replace(BOUNDARY_RE, "").split(/^$|,+/); - }; - return function(id, source, isDebug) { - var openTag = template.openTag; - var closeTag = template.closeTag; - var parser = template.parser; - var code = source; - var tempCode = ""; - var line = 1; - var uniq = { - $data: 1, - $id: 1, - $helpers: 1, - $out: 1, - $line: 1 - }; - var prototype = {}; - var variables = "var $helpers=this," + (isDebug ? "$line=0," : ""); - var isNewEngine = "".trim; - var replaces = isNewEngine ? [ "$out='';", "$out+=", ";", "$out" ] : [ "$out=[];", "$out.push(", ");", "$out.join('')" ]; - var concat = isNewEngine ? "$out+=$text;return $text;" : "$out.push($text);"; - var print = "function($text){" + concat + "}"; - var include = "function(id,data){" + "data=data||$data;" + "var $text=$helpers.$include(id,data,$id);" + concat + "}"; - forEach(code.split(openTag), function(code) { - code = code.split(closeTag); - var $0 = code[0]; - var $1 = code[1]; - if (code.length === 1) { - tempCode += html($0); - } else { - tempCode += logic($0); - if ($1) { - tempCode += html($1); - } - } - }); - code = tempCode; - if (isDebug) { - code = "try{" + code + "}catch(e){" + "throw {" + "id:$id," + "name:'Render Error'," + "message:e.message," + "line:$line," + "source:" + stringify(source) + ".split(/\\n/)[$line-1].replace(/^[\\s\\t]+/,'')" + "};" + "}"; - } - code = variables + replaces[0] + code + "return new String(" + replaces[3] + ");"; - try { - var Render = new Function("$data", "$id", code); - Render.prototype = prototype; - return Render; - } catch (e) { - e.temp = "function anonymous($data,$id) {" + code + "}"; - throw e; - } - function html(code) { - line += code.split(/\n/).length - 1; - if (template.isCompress) { - code = code.replace(/[\n\r\t\s]+/g, " ").replace(//g, ""); - } - if (code) { - code = replaces[1] + stringify(code) + replaces[2] + "\n"; - } - return code; - } - function logic(code) { - var thisLine = line; - if (parser) { - code = parser(code); - } else if (isDebug) { - code = code.replace(/\n/g, function() { - line++; - return "$line=" + line + ";"; - }); - } - if (code.indexOf("=") === 0) { - var isEscape = !/^=[=#]/.test(code); - code = code.replace(/^=[=#]?|[\s;]*$/g, ""); - if (isEscape && template.isEscape) { - var name = code.replace(/\s*\([^\)]+\)/, ""); - if (!_helpers.hasOwnProperty(name) && !/^(include|print)$/.test(name)) { - code = "$escape(" + code + ")"; - } - } else { - code = "$string(" + code + ")"; - } - code = replaces[1] + code + replaces[2]; - } - if (isDebug) { - code = "$line=" + thisLine + ";" + code; - } - getKey(code); - return code + "\n"; - } - function getKey(code) { - code = getVariable(code); - forEach(code, function(name) { - if (!uniq.hasOwnProperty(name)) { - setValue(name); - uniq[name] = true; - } - }); - } - function setValue(name) { - var value; - if (name === "print") { - value = print; - } else if (name === "include") { - prototype["$include"] = _helpers["$include"]; - value = include; - } else { - value = "$data." + name; - if (_helpers.hasOwnProperty(name)) { - prototype[name] = _helpers[name]; - if (name.indexOf("$") === 0) { - value = "$helpers." + name; - } else { - value = value + "===undefined?$helpers." + name + ":" + value; - } - } - } - variables += name + "=" + value + ","; - } - function stringify(code) { - return "'" + code.replace(/('|\\)/g, "\\$1").replace(/\r/g, "\\r").replace(/\n/g, "\\n") + "'"; - } - }; - }(); - if (typeof define === "function") { - define(function() { - return template; - }); - } else if (typeof exports !== "undefined") { - module.exports = template; - } - global.template = template; -})(this); - -(function(exports) { - exports.openTag = "{{"; - exports.closeTag = "}}"; - exports.parser = function(code) { - code = code.replace(/^\s/, ""); - var split = code.split(" "); - var key = split.shift(); - var args = split.join(" "); - switch (key) { - case "if": - code = "if(" + args + "){"; - break; - - case "else": - if (split.shift() === "if") { - split = " if(" + split.join(" ") + ")"; - } else { - split = ""; - } - code = "}else" + split + "{"; - break; - - case "/if": - code = "}"; - break; - - case "each": - var object = split[0] || "$data"; - var as = split[1] || "as"; - var value = split[2] || "$value"; - var index = split[3] || "$index"; - var param = value + "," + index; - if (as !== "as") { - object = "[]"; - } - code = "$each(" + object + ",function(" + param + "){"; - break; - - case "/each": - code = "});"; - break; - - case "echo": - code = "print(" + args + ");"; - break; - - case "include": - code = "include(" + split.join(",") + ");"; - break; - - default: - if (exports.helpers.hasOwnProperty(key)) { - code = "=#" + key + "(" + split.join(",") + ");"; - } else { - code = code.replace(/[\s;]*$/, ""); - code = "=" + code; - } - break; - } - return code; - }; -})(template); - -!function(global, template) { - "use strict"; - var get = template.get; - var helpers = template.helpers; - var resolve = function(from, to) { - var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - var dirname = from.replace(/^([^.])/, "./$1").replace(/[^/]+$/, ""); - var id = dirname + to; - id = id.replace(/\/\.\//g, "/"); - while (id.match(DOUBLE_DOT_RE)) { - id = id.replace(DOUBLE_DOT_RE, "/"); - } - return id; - }; - helpers.$include = function(uri, data, from) { - var id = resolve(from, uri); - return template.render(id, data); - }; - template.get = function(id) { - return get(id.replace(/^\.\//, "")); - }; -}(this, this.template); \ No newline at end of file diff --git a/test/test-all/mix/copyright.html b/test/test-all/mix/copyright.html deleted file mode 100644 index 4c65dfa..0000000 --- a/test/test-all/mix/copyright.html +++ /dev/null @@ -1 +0,0 @@ -(c) 2013 \ No newline at end of file diff --git a/test/test-all/mix/index.html b/test/test-all/mix/index.html deleted file mode 100644 index fc67525..0000000 --- a/test/test-all/mix/index.html +++ /dev/null @@ -1,12 +0,0 @@ -{{include './public/header'}} - -
    -

    {{title}}

    - -
    - -{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/mix/package.json b/test/test-all/mix/package.json deleted file mode 100644 index b4d68ee..0000000 --- a/test/test-all/mix/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "template", - "version": "1.0.0", - "dependencies": { - "tmodjs": "~0.1.1" - }, - "tmodjs-config": { - "output": "./build", - "charset": "utf-8", - "syntax": "simple", - "helpers": null, - "escape": true, - "engine": true, - "type": "default", - "combo": false, - "minify": false - } -} \ No newline at end of file diff --git a/test/test-all/mix/public/footer.html b/test/test-all/mix/public/footer.html deleted file mode 100644 index 956c23e..0000000 --- a/test/test-all/mix/public/footer.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/test/test-all/mix/public/header.html b/test/test-all/mix/public/header.html deleted file mode 100644 index d93e780..0000000 --- a/test/test-all/mix/public/header.html +++ /dev/null @@ -1,11 +0,0 @@ - - - \ No newline at end of file diff --git a/test/test-all/mix/public/logo.html b/test/test-all/mix/public/logo.html deleted file mode 100644 index b02f7cb..0000000 --- a/test/test-all/mix/public/logo.html +++ /dev/null @@ -1,7 +0,0 @@ - -

    - - 腾讯网 - -

    - \ No newline at end of file diff --git a/test/test-all/syntax-native/build/template.js b/test/test-all/syntax-native/build/template.js index 965e2dd..206014e 100644 --- a/test/test-all/syntax-native/build/template.js +++ b/test/test-all/syntax-native/build/template.js @@ -1,3 +1,3 @@ -/*TMODJS:{"build":1390233186500}*/ -!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},o={"<":"<",">":">",'"':""","'":"'","&":"&"},i=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return o[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,r){if(u(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),o=t+r;for(o=o.replace(/\/\.\//g,"/");o.match(n);)o=o.replace(n,"/");return o},f=r.helpers={$include:function(e,n,t){var o=a(t,e);return r.render(o,n)},$string:t,$escape:i,$each:c},l=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||l({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,o=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(o){return l(o)()}};return o.prototype=f,t&&(r.prototype=f),o.toString=function(){return r+""},o},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){f[e]=r},/*v:2*/ -r("index",function(e){var r=this,n=r.$escape,t=e.title,o=e.i,i=e.list,u="";u+='

    ',u+=n(t),u+="

    ",new String(u)}),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/*TMODJS:{"version":"1.0.0"}*/ +!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:4*/ +b("index",function(b){"use strict";var c=this,d=(c.$helpers,c.$escape),e=b.title,f=b.i,g=b.list,h="";h+='

    ',h+=d(e),h+="

    ",new a(h)})}(); \ No newline at end of file diff --git a/test/test-all/syntax-native/package.json b/test/test-all/syntax-native/package.json index e964483..f15decd 100644 --- a/test/test-all/syntax-native/package.json +++ b/test/test-all/syntax-native/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.1" + "tmodjs": "1.0.0" }, "tmodjs-config": { "output": "./build", @@ -10,9 +10,11 @@ "syntax": "native", "helpers": null, "escape": true, - "engine": false, + "compress": true, "type": "default", + "runtime": "template.js", "combo": true, - "minify": true + "minify": true, + "cache": false } } \ No newline at end of file diff --git a/test/test-all/syntax/build/template.js b/test/test-all/syntax/build/template.js index b3da80b..8763b71 100644 --- a/test/test-all/syntax/build/template.js +++ b/test/test-all/syntax/build/template.js @@ -1,3 +1,3 @@ -/*TMODJS:{"build":1390233180365}*/ -!function(e){"use strict";var n=function(e,r){return n[/string|function/.test(typeof r)?"compile":"render"].apply(n,arguments)},r=n.cache={},t=function(e,n){return"string"!=typeof e&&(n=typeof e,"number"===n?e+="":e="function"===n?t(e.call(e)):""),e},o={"<":"<",">":">",'"':""","'":"'","&":"&"},i=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return o[e]})},u=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},c=function(e,n){if(u(e))for(var r=0,t=e.length;t>r;r++)n.call(e,e[r],r,e);else for(r in e)n.call(e,e[r],r)},a=function(e,n){var r=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),o=t+n;for(o=o.replace(/\/\.\//g,"/");o.match(r);)o=o.replace(r,"/");return o},l=n.helpers={$include:function(e,r,t){var o=a(t,e);return n.render(o,r)},$string:t,$escape:i,$each:c},f=function(n){var r="";for(var t in n)r+="<"+t+">\n"+n[t]+"\n\n";return r&&e.console&&console.error("Template Error\n\n"+r),function(){return"{Template Error}"}};n.render=function(e,r){var t=n.get(e)||f({id:e,name:"Render Error",message:"No Template"});return r?t(r):t},n.compile=function(e,n){var t="function"==typeof n,o=r[e]=function(r){try{return t?new n(r,e)+"":n}catch(o){return f(o)()}};return o.prototype=l,t&&(n.prototype=l),o.toString=function(){return n+""},o},n.get=function(e){return r[e.replace(/^\.\//,"")]},n.helper=function(e,n){l[e]=n},/*v:2*/ -n("index",function(e){var n=this,r=e.x,t=n.$escape,o=e.title,i=n.$each,u=e.list,c=(e.$value,e.$index,""),r="hello world";return c+=" ",c+=t(r),c+='

    ',c+=t(o),c+="

    ",new String(c)}),"function"==typeof define?define(function(){return n}):"undefined"!=typeof exports?module.exports=n:e.template=n}(this); \ No newline at end of file +/*TMODJS:{"version":"1.0.0"}*/ +!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:6*/ +b("index",function(b){"use strict";var c=this,d=(c.$helpers,c.$escape),e=b.title,f=c.$each,g=b.list,h=(b.$value,b.$index,"");return h+='

    ',h+=d(e),h+="

    ",new a(h)})}(); \ No newline at end of file diff --git a/test/test-all/syntax/index.html b/test/test-all/syntax/index.html index 4c27a69..602209d 100644 --- a/test/test-all/syntax/index.html +++ b/test/test-all/syntax/index.html @@ -1,11 +1,8 @@ -{{var x = 'hello world'}} -{{x}} -
    -

    {{title}}

    +

    \ No newline at end of file diff --git a/test/test-all/syntax/package.json b/test/test-all/syntax/package.json index a99f32a..137450e 100644 --- a/test/test-all/syntax/package.json +++ b/test/test-all/syntax/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.1" + "tmodjs": "1.0.0" }, "tmodjs-config": { "output": "./build", @@ -10,9 +10,11 @@ "syntax": "./template-syntax.js", "helpers": null, "escape": true, - "engine": false, + "compress": true, "type": "default", + "runtime": "template.js", "combo": true, - "minify": true + "minify": true, + "cache": false } } \ No newline at end of file diff --git a/test/test-all/syntax/template-syntax.js b/test/test-all/syntax/template-syntax.js index 2bd2958..61154c6 100644 --- a/test/test-all/syntax/template-syntax.js +++ b/test/test-all/syntax/template-syntax.js @@ -1,97 +1,88 @@ -/*! - * artTemplate - Syntax Extensions - * https://github.com/aui/artTemplate - * Released under the MIT, BSD, and GPL Licenses - */ - -(function (exports) { - - exports.openTag = '{{'; - exports.closeTag = '}}'; +template.defaults.openTag = ''; - exports.parser = function (code) { - code = code.replace(/^\s/, ''); - - var split = code.split(' '); - var key = split.shift(); - var args = split.join(' '); - switch (key) { +template.defaults.parser = function (code) { + code = code.replace(/^\s/, ''); + + var split = code.split(' '); + var key = split.shift(); + var args = split.join(' '); - case 'var': + switch (key) { - code = 'var ' + args + ';'; - break; + case 'if': - case 'if': + code = 'if(' + args + '){'; + break; - code = 'if(' + args + '){'; - break; + case 'else': + + if (split.shift() === 'if') { + split = ' if(' + split.join(' ') + ')'; + } else { + split = ''; + } - case 'else': - - if (split.shift() === 'if') { - split = ' if(' + split.join(' ') + ')'; - } else { - split = ''; - } + code = '}else' + split + '{'; + break; - code = '}else' + split + '{'; - break; + case '/if': - case '/if': + code = '}'; + break; - code = '}'; - break; + case 'each': + + var object = split[0] || '$data'; + var as = split[1] || 'as'; + var value = split[2] || '$value'; + var index = split[3] || '$index'; + + var param = value + ',' + index; + + if (as !== 'as') { + object = '[]'; + } + + code = '$each(' + object + ',function(' + param + '){'; + break; - case 'each': - - var object = split[0] || '$data'; - var as = split[1] || 'as'; - var value = split[2] || '$value'; - var index = split[3] || '$index'; - - var param = value + ',' + index; - - if (as !== 'as') { - object = '[]'; - } - - code = '$each(' + object + ',function(' + param + '){'; - break; + case '/each': - case '/each': + code = '});'; + break; - code = '});'; - break; + case 'echo': - case 'echo': + code = 'print(' + args + ');'; + break; - code = 'print(' + args + ');' - break; + case 'print': + case 'include': - case 'include': + code = key + '(' + split.join(',') + ');'; + break; - code = 'include(' + split.join(',') + ');'; - break; + default: - default: + if (template.helpers.hasOwnProperty(key)) { + + code = '=#' + key + '(' + split.join(',') + ');'; + + } else { + + code = code.replace(/[\s;]*$/, ''); + code = '=' + code; + } + + break; + } + + + return code; +}; - if (exports.helpers.hasOwnProperty(key)) { - - code = '=#' + key + '(' + split.join(',') + ');'; - - } else { - code = code.replace(/[\s;]*$/, ''); - code = '=' + code; - } - break; - } - - - return code; - }; -})(template); diff --git a/test/test-all/webapp-tpl/404/main.html b/test/test-all/webapp-tpl/404/main.html deleted file mode 100755 index 44b46f3..0000000 --- a/test/test-all/webapp-tpl/404/main.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    404:您访问的页面不存在!

    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/build/template.js b/test/test-all/webapp-tpl/build/template.js deleted file mode 100644 index 3766215..0000000 --- a/test/test-all/webapp-tpl/build/template.js +++ /dev/null @@ -1,21 +0,0 @@ -/*TMODJS:{"build":1390233272112}*/ -!function(a){"use strict";var i=function(a,s){return i[/string|function/.test(typeof s)?"compile":"render"].apply(i,arguments)},s=i.cache={},t=function(a,i){return"string"!=typeof a&&(i=typeof a,"number"===i?a+="":a="function"===i?t(a.call(a)):""),a},e={"<":"<",">":">",'"':""","'":"'","&":"&"},n=function(a){return t(a).replace(/&(?![\w#]+;)|[<>"']/g,function(a){return e[a]})},r=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},c=function(a,i){if(r(a))for(var s=0,t=a.length;t>s;s++)i.call(a,a[s],s,a);else for(s in a)i.call(a,a[s],s)},l=function(a,i){var s=/(\/)[^/]+\1\.\.\1/,t=a.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),e=t+i;for(e=e.replace(/\/\.\//g,"/");e.match(s);)e=e.replace(s,"/");return e},d=i.helpers={$include:function(a,s,t){var e=l(t,a);return i.render(e,s)},$string:t,$escape:n,$each:c},o=function(i){var s="";for(var t in i)s+="<"+t+">\n"+i[t]+"\n\n";return s&&a.console&&console.error("Template Error\n\n"+s),function(){return"{Template Error}"}};i.render=function(a,s){var t=i.get(a)||o({id:a,name:"Render Error",message:"No Template"});return s?t(s):t},i.compile=function(a,i){var t="function"==typeof i,e=s[a]=function(s){try{return t?new i(s,a)+"":i}catch(e){return o(e)()}};return e.prototype=d,t&&(i.prototype=d),e.toString=function(){return i+""},e},i.get=function(a){return s[a.replace(/^\.\//,"")]},i.helper=function(a,i){d[a]=i},/*v:1*/ -i("404/main",'

    404:\u60a8\u8bbf\u95ee\u7684\u9875\u9762\u4e0d\u5b58\u5728!

    '),/*v:1*/ -i("detail/focus",function(a){var i=this,s=i.$escape,t=a._rangeTypeText,e=a.focus,n="";return n+='

    \u70ed\u5ea6\u8d8b\u52bf

    ?

    \u70ed\u5ea6\u8d8b\u52bf\u56fe

    \u5386\u53f2',n+=s(t),n+="\u5e73\u5747",n+=s(e.HistoryAverageCount),n+='\u6761

    \u6700\u8fd1\u4e00',n+=s(t),n+='\u5171\u6709\u76f8\u5173\u5fae\u535a',n+=s(e.TotalCount),n+="\u6761\uff0c\u4e0e\u4e0a\u4e00",n+=s(t),n+='\u76f8\u6bd4--
    ',new String(n)}),/*v:1*/ -i("detail/hotTerm",function(a){var i=this,s=a.hotTerm,t=i.$each,e=(a.$value,a.$index,i.$escape),n=a.id,r=a.IsLocateProblem,c="";return c+='

    \u70ed\u8bcd\u5206\u6790

    ?
    ',c+=e(a.Word),c+='',c+=e(a.Count),c+='\u6761 ',a.IsNew&&(c+=' n '),c+=" "}),c+="
    ",1!==r?(c+='
    ',t(s.TopList,function(a){c+=' ',c+=e(a.Word),c+='',c+=e(a.Count),c+="\u6761",a.IsNew&&(c+='n'),c+=" "}),c+="
    "):c+='
    ',c+="
    ",new String(c)}),/*v:1*/ -i("detail/keyword",function(a){var i=this,s=i.$escape,t=a.id,e=a.Word,n=a.Count,r=a.IsNew,c="";return c+=' ',c+=s(e),c+='',c+=s(n),c+='\u6761 ',r&&(c+=' n '),c+=" ",new String(c)}),/*v:1*/ -i("detail/main",function(a,i){var s=this,t=s.$escape,e=a.id,n=a._period,r=function(t,e){e=e||a;var n=s.$include(t,e,i);return l+=n,n},c=a.IsAppStore,l="";return l+='
    ',l+=t(n),l+='
    ',r("./report"),l+=" ",r("./reputation"),l+=" ",r("./focus"),l+=" ",r("./hotTerm"),l+=" ",r("./weibo"),l+='
    ',new String(l)}),/*v:1*/ -i("detail/negative",function(a){var i=this,s=i.$escape,t=a.TopListTotalCount,e=i.$each,n=a.locateTerm,r=(a.$value,a.$index,a.id),c=a.TopListOtherCount,l="";return l+='

    \u8d1f\u9762\u8bc4\u8bba

    \u5171\u6536\u96c6\u5230',l+=s(t),l+="\u6761
    ",e(n.TopList,function(a,i){l+=" ",6>i?(l+=' ',l+=s(a.Count),l+='\u6761',l+=s(a.Term),l+="",a.IsNew&&(l+='n'),l+=" "):(l+=' ',l+=s(a.Count),l+='\u6761',l+=s(a.Term),l+="",a.IsNew&&(l+='n'),l+=" "),l+=" "}),l+=' ',l+=s(c),l+='\u6761\u5176\u4ed6 ',new String(l)}),/*v:1*/ -i("detail/report",function(a){var i=this,s=i.$escape,t=a.ReputationIndex,e=a.ChainIndex,n=a.TotalCount,r=a.RepuRanking,c=a.IsFavStickie,l=a.id,d=a.CompetitionReport,o=i.$each,p=(a.$value,a.$index,"");return p+='
    ',p+=s(t),p+="",p+=s(t),p+="
    ",999999999===e?p+=' --% ':0>e?(p+=' ',p+=s(-1*e),p+="% "):(p+=' ',p+=s(e),p+="% "),p+='
    ',20>=t?p+='

    \u53e3\u7891\u6307\u6570\u5f88\u4f4e\uff0c\u8981\u5f15\u8d77\u91cd\u89c6\u4e86\u54e6\uff01

    ':t>20&&40>=t?p+='

    \u53e3\u7891\u6307\u6570\u8f83\u4f4e\uff0c\u662f\u4e0d\u662f\u51fa\u4e86\u4ec0\u4e48\u95ee\u9898\uff1f

    ':t>40&&60>=t?p+='

    \u53e3\u7891\u6307\u6570\u4e00\u822c\uff0c\u9700\u6301\u7eed\u5173\u6ce8\u548c\u4f18\u5316\u3002

    ':t>60&&80>=t?p+='

    \u53e3\u7891\u6307\u6570\u8f83\u9ad8\uff0c\u8bf7\u7ee7\u7eed\u52a0\u6cb9\uff01

    ':t>80&&(p+='

    \u53e3\u7891\u6307\u6570\u5f88\u9ad8\uff0c\u5f88\u4e0d\u9519\u54e6\uff01

    '),p+='
    ',d.length&&(p+='

    \u7ade\u54c1\u60c5\u51b5

    ',o(d,function(a){p+='
    ',p+=s(a.ProductUniqueName),p+=' \u53e3\u7891\u6307\u6570 ',p+=s(a.ReputationIndex||"--"),p+=' ',p+=s(a.ProductWeiboCount||"--"),p+="
    "}),p+="
    "),p+=" ",new String(p)}),/*v:1*/ -i("detail/reputation",function(a){var i=this,s=i.$escape,t=a._rangeTypeText,e=a.reputation,n=i.$each,r=(a.$value,a.$index,a.id),c="";return c+='

    \u53e3\u7891\u8d8b\u52bf

    ?

    \u53e3\u7891\u6307\u6570\u8d8b\u52bf\u56fe

    ',c+=s(t),c+='\u6700\u9ad8--\uff0c',c+=s(t),c+='\u6700\u4f4e--\uff0c',c+=s(t),c+='\u5e73\u5747--

    \u8fd1\u4e00',c+=s(t),c+='\u7684\u53e3\u7891\u6307\u6570\u4e3a',c+=s(e.ReputationIndex),c+="\uff0c\u4e0e\u4e0a",c+=s(t),c+='\u76f8\u6bd4--

    \u6b63\u9762\u53e3\u7891 Top5\uff1a

    \u8d1f\u9762\u53e3\u7891 Top5\uff1a

    ",new String(c)}),/*v:1*/ -i("detail/weibo-list",function(a){var i=this,s=i.$each,t=a.RawDataList,e=(a.$value,a.$index,i.$escape),n=function(a){return r+=a,a},r="";return s(t,function(a){r+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ -i("detail/weibo",function(a){var i=this,s=i.$escape,t=a.id,e="";return e+='',new String(e)}),/*v:1*/ -i("home/main",function(a,i){var s=this,t=a._isValueSwitch,e=s.$escape,n=a._rangeTypeText,r=a._sortTypeText,c=s.$each,l=a.productList,d=(a.$value,a.$index,function(t,e){e=e||a;var n=s.$include(t,e,i);return o+=n,n}),o="";return o+='
    \u53e3\u7891\u6307\u6570',o+=e(r),o+='
    ',c(l,function(a){o+=' ',d("./project",a),o+=" "}),o+="
    ",new String(o)}),/*v:7*/ -i("home/project",function(a){var i=this,s=a.IsFavStickie,t=i.$escape,e=a.ProductUniqueId,n=a.ProductUniqueName,r=a.RepuIndexDiffrence,c=a.TotalCount,l=a.ReputationIndex,d=a.iCompRepuIndex,o=a.iComRepuIndexDiffrence,p=a.HasCompetition,v=a._compValue,u=a._warnClass2,g=a._updownClass,_=a.RepuIndexPercent,f="";return f+='
    ',1==s&&(f+=' '),f+='

    ',f+=t(n),f+="

    ",p&&(f+=" ",v>0?(f+='

    \u9886\u5148\u7ade\u54c1+',f+=t(v),f+="

    "):0===v?f+='

    \u4e0d\u76f8\u4e0a\u4e0b0

    ':0>v?(f+='

    \u843d\u540e\u7ade\u54c1',f+=t(v),f+="

    "):f+='

    ',f+=" "),f+="
    ",f+=r>0?' \u2191 ':' \u2193 ',f+='
    ',f+=t(l),f+='
    ',0>r?(f+=" -",f+=t(_),f+="% "):r>0?(f+=" +",f+=t(_),f+="% "):f+=" 0 ",f+='
    ',0>r?(f+=" -",f+=t(r),f+=" "):r>0?(f+=" +",f+=t(r),f+=" "):f+=" 0 ",f+='
    ',f+=t(c),f+="
    ",new String(f)}),/*v:2*/ -i("negative/main",function(a,i){var s=this,t=s.$each,e=a.TopList,n=(a.$value,a.$index,s.$escape),r=function(t,e){e=e||a;var n=s.$include(t,e,i);return l+=n,n},c=a.HasNext,l="";return l+='
    ',r("../public/tag-list"),l+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ -i("reputation/main",function(a,i){var s=this,t=s.$escape,e=a.DataSummary,n=function(t,e){e=e||a;var n=s.$include(t,e,i);return c+=n,n},r=a.HasNext,c="";return c+='
    \u5305\u542b\u201c',c+=t(e.Alias),c+="\u201d\u6216\u5176\u540c\u4e49\u8bcd\u7684\u5fae\u535a\u5171",c+=t(e.TotalCount),c+='\u6761
    ',n("../public/tag-list"),c+="
    ",c+=r?' \u70b9\u51fb\u66f4\u591a ':' \u5168\u90e8\u52a0\u8f7d\u5b8c\u6bd5 ',c+="
    ",new String(c)}),/*v:1*/ -i("setting/main",function(a){var i=a.origin,s=a.sortType,t=a.rangeType,e=a.isDebug,n="";return n+='',new String(n)}),/*v:1*/ -i("tag/main",function(a,i){var s=this,t=function(t,e){e=e||a;var r=s.$include(t,e,i);return n+=r,r},e=a.HasNext,n="";return n+='
    ',t("../public/tag-list"),n+='
    ',r+=e(a.Nick),r+=" ",a.IsVip&&(r+=''),r+=' ',r+=e(a.ExtractDate),r+='
    \u6765\u81ea ',r+=e(a.From),r+=' \u8bc4\u8bba ',r+=e(a.Count),r+="
    "}),new String(r)}),/*v:1*/ -i("weibo/main",function(a,i){var s=this,t=function(t,e){e=e||a;var r=s.$include(t,e,i);return n+=r,r},e=a.HasNext,n="";return n+='
    ',t("./list"),n+="
    ",n+=e?' \u70b9\u51fb\u66f4\u591a ':' \u5168\u90e8\u52a0\u8f7d\u5b8c\u6bd5 ',n+="
    ",new String(n)}),"function"==typeof define?define(function(){return i}):"undefined"!=typeof exports?module.exports=i:a.template=i}(this); \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/focus.html b/test/test-all/webapp-tpl/detail/focus.html deleted file mode 100755 index 96cc11d..0000000 --- a/test/test-all/webapp-tpl/detail/focus.html +++ /dev/null @@ -1,17 +0,0 @@ -
    -
    - -

    热度趋势

    - ? -
    -
    -

    热度趋势图

    -

    历史{{_rangeTypeText}}平均{{focus.HistoryAverageCount}}

    -
    - -
    -
    - 最近一{{_rangeTypeText}}共有相关微博{{focus.TotalCount}}条,与上一{{_rangeTypeText}}相比-- -
    -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/hotTerm.html b/test/test-all/webapp-tpl/detail/hotTerm.html deleted file mode 100755 index c3432c8..0000000 --- a/test/test-all/webapp-tpl/detail/hotTerm.html +++ /dev/null @@ -1,40 +0,0 @@ -
    -
    - -

    热词分析

    - ? -
    -
    -
    -
    - - - - 订阅 - -
    - -
    - 编辑 - {{each hotTerm.CustomList}} - - {{$value.Word}}{{$value.Count}} - - {{if $value.IsNew}} - n - {{/if}} - - {{/each}} -
    -
    - {{if IsLocateProblem !== 1}} -
    - {{each hotTerm.TopList}} - {{$value.Word}}{{$value.Count}}{{if $value.IsNew}}n{{/if}} - {{/each}} -
    - {{else}} -
    - {{/if}} -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/keyword.html b/test/test-all/webapp-tpl/detail/keyword.html deleted file mode 100755 index 1813047..0000000 --- a/test/test-all/webapp-tpl/detail/keyword.html +++ /dev/null @@ -1,7 +0,0 @@ - - {{Word}}{{Count}} - - {{if IsNew}} - n - {{/if}} - \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/main.html b/test/test-all/webapp-tpl/detail/main.html deleted file mode 100755 index 0182a6a..0000000 --- a/test/test-all/webapp-tpl/detail/main.html +++ /dev/null @@ -1,22 +0,0 @@ -
    -
    {{_period}}
    -
    -
    - {{include './report'}} - {{include './reputation'}} - {{include './focus'}} - {{include './hotTerm'}} - {{include './weibo'}} -
    -
    - -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/negative.html b/test/test-all/webapp-tpl/detail/negative.html deleted file mode 100755 index 93f2e3b..0000000 --- a/test/test-all/webapp-tpl/detail/negative.html +++ /dev/null @@ -1,15 +0,0 @@ -
    -

    负面评论

    共收集到{{TopListTotalCount}}条 -
    -{{each locateTerm.TopList}} - {{if $index < 6}} - {{$value.Count}}{{$value.Term}}{{if $value.IsNew}}n{{/if}} - {{else}} - {{$value.Count}}{{$value.Term}}{{if $value.IsNew}}n{{/if}} - {{/if}} -{{/each}} - - - {{TopListOtherCount}}其他 - - \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/report.html b/test/test-all/webapp-tpl/detail/report.html deleted file mode 100755 index 3ee4bd7..0000000 --- a/test/test-all/webapp-tpl/detail/report.html +++ /dev/null @@ -1,85 +0,0 @@ -
    -
    -
    {{ReputationIndex}}{{ReputationIndex}}
    - - - {{if ChainIndex === 999999999}} - --% - {{else if ChainIndex < 0}} - {{-1 * ChainIndex}}% - {{else}} - {{ChainIndex}}% - {{/if}} - - - -
    -
    -
    -
    -
    - - - - {{if ReputationIndex <= 20}} - -

    口碑指数很低,要引起重视了哦!

    - - {{else if ReputationIndex >20 && ReputationIndex <=40}} - -

    口碑指数较低,是不是出了什么问题?

    - - {{else if ReputationIndex > 40 && ReputationIndex <= 60}} - -

    口碑指数一般,需持续关注和优化。

    - - {{else if ReputationIndex > 60 && ReputationIndex <= 80}} - -

    口碑指数较高,请继续加油!

    - - {{else if ReputationIndex > 80}} - -

    口碑指数很高,很不错哦!

    - - {{/if}} -
    - {{if IsFavStickie == 1}} - 取消置顶 - {{else}} - 点击置顶 - {{/if}} - - 点击下载 -
    - - -
    - - -{{if CompetitionReport.length}} -
    -

    竞品情况

    - {{each CompetitionReport}} -
    - {{$value.ProductUniqueName}} - 口碑指数 {{$value.ReputationIndex || '--'}} - {{$value.ProductWeiboCount || '--'}} -
    - {{/each}} -
    -{{/if}} diff --git a/test/test-all/webapp-tpl/detail/reputation.html b/test/test-all/webapp-tpl/detail/reputation.html deleted file mode 100755 index 5c1bb7c..0000000 --- a/test/test-all/webapp-tpl/detail/reputation.html +++ /dev/null @@ -1,43 +0,0 @@ -
    -
    - -

    口碑趋势

    - ? -
    -
    -

    口碑指数趋势图

    -

    {{_rangeTypeText}}最高--,{{_rangeTypeText}}最低--,{{_rangeTypeText}}平均--

    -
    - -
    -
    - 近一{{_rangeTypeText}}的口碑指数为{{reputation.ReputationIndex}},与上{{_rangeTypeText}}相比-- -
    - -
    -
    -

    正面口碑 Top5:

    - -
    -
    -

    负面口碑 Top5:

    - -
    -
    - -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/weibo-list.html b/test/test-all/webapp-tpl/detail/weibo-list.html deleted file mode 100755 index 4c1c9a0..0000000 --- a/test/test-all/webapp-tpl/detail/weibo-list.html +++ /dev/null @@ -1,17 +0,0 @@ -{{each RawDataList}} -
    -
    - {{$value.Nick}} - {{if $value.IsVip}}{{/if}} - {{$value.ExtractDate}} -
    - -
    - 来自 {{$value.From}} - 评论 - {{$value.Count}} -
    -
    -{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/detail/weibo.html b/test/test-all/webapp-tpl/detail/weibo.html deleted file mode 100755 index b2c3f5e..0000000 --- a/test/test-all/webapp-tpl/detail/weibo.html +++ /dev/null @@ -1,13 +0,0 @@ -
    -
    - -

    热门微博

    - -
    -
    -
    - -
    - 点击更多> -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/home/main.html b/test/test-all/webapp-tpl/home/main.html deleted file mode 100755 index 04daced..0000000 --- a/test/test-all/webapp-tpl/home/main.html +++ /dev/null @@ -1,13 +0,0 @@ -
    - -
    近一{{_rangeTypeText}}内数据 口碑指数{{_sortTypeText}}
    -
    -
    - {{each productList}} - - {{include './project' $value}} - - {{/each}} -
    -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/home/project.html b/test/test-all/webapp-tpl/home/project.html deleted file mode 100755 index f13b8cc..0000000 --- a/test/test-all/webapp-tpl/home/project.html +++ /dev/null @@ -1,64 +0,0 @@ -
    - {{if IsFavStickie == 1}} - - {{/if}} - -
    -

    {{ProductUniqueName}}

    - {{if HasCompetition}} - {{if _compValue > 0}} -

    领先竞品+{{_compValue}}

    - {{else if _compValue === 0}} -

    不相上下0

    - {{else if _compValue < 0}} -

    落后竞品{{_compValue}}

    - {{else}} -

    - {{/if}} - - {{/if}} -
    - - {{if RepuIndexDiffrence > 0}} - - {{else}} - - {{/if}} - -
    - {{ReputationIndex}} - -
    -
    - -
    - {{if RepuIndexDiffrence < 0}} - -{{ RepuIndexPercent}}% - {{else if RepuIndexDiffrence > 0}} - +{{ RepuIndexPercent}}% - {{else}} - 0 - {{/if}} -
    - -
    - {{if RepuIndexDiffrence < 0}} - -{{RepuIndexDiffrence}} - {{else if RepuIndexDiffrence > 0}} - +{{RepuIndexDiffrence}} - {{else}} - 0 - {{/if}} -
    - -
    - {{TotalCount}} -
    -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/negative/main.html b/test/test-all/webapp-tpl/negative/main.html deleted file mode 100755 index 93d645a..0000000 --- a/test/test-all/webapp-tpl/negative/main.html +++ /dev/null @@ -1,19 +0,0 @@ -
    - -
    -
    - 更多 - {{each TopList}} - - {{$value.Term}} - {{$value.Count}} - - {{/each}} -
    -
    - {{include '../public/tag-list'}} -
    - 点击更多 -
    - -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/package.json b/test/test-all/webapp-tpl/package.json deleted file mode 100644 index be9c5b9..0000000 --- a/test/test-all/webapp-tpl/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "template", - "version": "1.0.0", - "dependencies": { - "tmodjs": "~0.1.1" - }, - "tmodjs-config": { - "output": "./build", - "charset": "utf-8", - "syntax": "simple", - "helpers": null, - "escape": true, - "engine": false, - "type": "default", - "combo": true, - "minify": true - } -} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/public/tag-list.html b/test/test-all/webapp-tpl/public/tag-list.html deleted file mode 100755 index 72c0bfc..0000000 --- a/test/test-all/webapp-tpl/public/tag-list.html +++ /dev/null @@ -1,17 +0,0 @@ -{{each DataSummary.RawDataList}} -
    -
    - {{$value.Nick}} - {{if $value.IsVip}}{{/if}} - {{$value.ExtractDate}} -
    - -
    - 来自 {{$value.From}} - 评论 - {{$value.Count}} -
    -
    -{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/reputation/main.html b/test/test-all/webapp-tpl/reputation/main.html deleted file mode 100755 index f6db662..0000000 --- a/test/test-all/webapp-tpl/reputation/main.html +++ /dev/null @@ -1,22 +0,0 @@ -
    - -
    -
    - -
    包含“{{DataSummary.Alias}}”或其同义词的微博共{{DataSummary.TotalCount}}
    -
    -
    - {{include '../public/tag-list'}} -
    - {{if HasNext}} - 点击更多 - {{else}} - 全部加载完毕 - {{/if}} -
    - -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/setting/main.html b/test/test-all/webapp-tpl/setting/main.html deleted file mode 100755 index cec46e6..0000000 --- a/test/test-all/webapp-tpl/setting/main.html +++ /dev/null @@ -1,49 +0,0 @@ -
    -
    -
    - - {{if origin === 'Home'}} - - - {{/if}} - -

    时间范围

    - - - {{if origin === 'Home'}} -

    高级

    - - {{/if}} - -

    关于

    - - - {{if isDebug}} -

    Debug

    - - {{/if}} - -
    - -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/tag/main.html b/test/test-all/webapp-tpl/tag/main.html deleted file mode 100755 index aa30585..0000000 --- a/test/test-all/webapp-tpl/tag/main.html +++ /dev/null @@ -1,17 +0,0 @@ -
    - -
    - -
    - {{include '../public/tag-list'}} -
    - 点击更多 -
    - -
    \ No newline at end of file diff --git a/test/test-all/webapp-tpl/weibo/list.html b/test/test-all/webapp-tpl/weibo/list.html deleted file mode 100755 index 4c1c9a0..0000000 --- a/test/test-all/webapp-tpl/weibo/list.html +++ /dev/null @@ -1,17 +0,0 @@ -{{each RawDataList}} -
    -
    - {{$value.Nick}} - {{if $value.IsVip}}{{/if}} - {{$value.ExtractDate}} -
    - -
    - 来自 {{$value.From}} - 评论 - {{$value.Count}} -
    -
    -{{/each}} \ No newline at end of file diff --git a/test/test-all/webapp-tpl/weibo/main.html b/test/test-all/webapp-tpl/weibo/main.html deleted file mode 100755 index 07a904d..0000000 --- a/test/test-all/webapp-tpl/weibo/main.html +++ /dev/null @@ -1,14 +0,0 @@ -
    - -
    -
    - {{include './list'}} -
    - {{if HasNext}} - 点击更多 - {{else}} - 全部加载完毕 - {{/if}} -
    - -
    diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index e3161cc..679356f 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ -/*TMODJS:{"build":1390231214112}*/ -!function(e){"use strict";var r=function(e,n){return r[/string|function/.test(typeof n)?"compile":"render"].apply(r,arguments)},n=r.cache={},t=function(e,r){return"string"!=typeof e&&(r=typeof e,"number"===r?e+="":e="function"===r?t(e.call(e)):""),e},i={"<":"<",">":">",'"':""","'":"'","&":"&"},o=function(e){return t(e).replace(/&(?![\w#]+;)|[<>"']/g,function(e){return i[e]})},c=Array.isArray||function(e){return"[object Array]"==={}.toString.call(e)},u=function(e,r){if(c(e))for(var n=0,t=e.length;t>n;n++)r.call(e,e[n],n,e);else for(n in e)r.call(e,e[n],n)},a=function(e,r){var n=/(\/)[^/]+\1\.\.\1/,t=e.replace(/^([^.])/,"./$1").replace(/[^/]+$/,""),i=t+r;for(i=i.replace(/\/\.\//g,"/");i.match(n);)i=i.replace(n,"/");return i},l=r.helpers={$include:function(e,n,t){var i=a(t,e);return r.render(i,n)},$string:t,$escape:o,$each:u},f=function(r){var n="";for(var t in r)n+="<"+t+">\n"+r[t]+"\n\n";return n&&e.console&&console.error("Template Error\n\n"+n),function(){return"{Template Error}"}};r.render=function(e,n){var t=r.get(e)||f({id:e,name:"Render Error",message:"No Template"});return n?t(n):t},r.compile=function(e,r){var t="function"==typeof r,i=n[e]=function(n){try{return t?new r(n,e)+"":r}catch(i){return f(i)()}};return i.prototype=l,t&&(r.prototype=l),i.toString=function(){return r+""},i},r.get=function(e){return n[e.replace(/^\.\//,"")]},r.helper=function(e,r){l[e]=r},/*v:8*/ -r("copyright","(c) 2014"),/*v:9*/ -r("index",function(e,r){var n=this,t=function(t,i){i=i||e;var o=n.$include(t,i,r);return a+=o,o},i=n.$escape,o=e.title,c=n.$each,u=e.list,a=(e.$value,e.$index,"");return t("./public/header"),a+='

    ',a+=i(o),a+="

    ",t("./public/footer"),new String(a)}),/*v:7*/ -r("public/footer",function(e,r){var n=this,t=e.time,i=n.$escape,o=function(t,i){i=i||e;var o=n.$include(t,i,r);return c+=o,o},c="";return c+='",new String(c)}),/*v:7*/ -r("public/header",function(e,r){var n=this,t=function(t,o){o=o||e;var c=n.$include(t,o,r);return i+=c,c},i="";return i+=' ',new String(i)}),/*v:7*/ -r("public/logo",'

    \u817e\u8baf\u7f51

    '),"function"==typeof define?define(function(){return r}):"undefined"!=typeof exports?module.exports=r:e.template=r}(this); \ No newline at end of file +/*TMODJS:{"version":"1.0.0"}*/ +!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:47*/ +b("copyright","(c) 2013"),/*v:32*/ +b("index",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var f=d.$include(a,e,c);return j+=f}),f=d.$escape,g=b.title,h=d.$each,i=b.list,j=(b.$value,b.$index,"");return e("./public/header"),j+='

    ',j+=f(g),j+="

    ",e("./public/footer"),j+=" ",new a(j)}),/*v:32*/ +b("public/footer",function(b,c){"use strict";var d=this,e=(d.$helpers,b.time),f=d.$escape,g=function(a,e){e=e||b;var f=d.$include(a,e,c);return h+=f},h="";return h+='",new a(h)}),/*v:32*/ +b("public/header",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var g=d.$include(a,e,c);return f+=g}),f="";return f+=' ',new a(f)}),/*v:32*/ +b("public/logo",'

    腾讯网

    ')}(); \ No newline at end of file diff --git a/test/tpl/copyright.html b/test/tpl/copyright.html index beedc6a..4c65dfa 100644 --- a/test/tpl/copyright.html +++ b/test/tpl/copyright.html @@ -1 +1 @@ -(c) 2014 \ No newline at end of file +(c) 2013 \ No newline at end of file diff --git a/test/tpl/index.html b/test/tpl/index.html index fc67525..3fb0301 100644 --- a/test/tpl/index.html +++ b/test/tpl/index.html @@ -1,5 +1,6 @@ {{include './public/header'}} +

    {{title}}

      @@ -9,4 +10,4 @@

      {{title}}

    -{{include './public/footer'}} \ No newline at end of file +{{include './public/footer'}} \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index be9c5b9..be39584 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "~0.1.1" + "tmodjs": "1.0.0" }, "tmodjs-config": { "output": "./build", @@ -10,9 +10,11 @@ "syntax": "simple", "helpers": null, "escape": true, - "engine": false, + "compress": true, "type": "default", + "runtime": "template.js", "combo": true, - "minify": true + "minify": true, + "cache": true } } \ No newline at end of file From 4a5983aefc13ed7e5e62995f648c31db41364275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 16 May 2014 14:10:50 +0800 Subject: [PATCH 26/87] v1.0.0 --- bin/tmod | 2 +- test/test-all/helper/index.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/test-all/helper/index.html diff --git a/bin/tmod b/bin/tmod index a77a9d4..decf324 100644 --- a/bin/tmod +++ b/bin/tmod @@ -44,7 +44,7 @@ var help = function () { var base; var value; var userConfig; -var isWatch = false; +var isWatch = true; var isEditConfig = false; var args = process.argv.slice(2); diff --git a/test/test-all/helper/index.html b/test/test-all/helper/index.html new file mode 100644 index 0000000..b82d4e5 --- /dev/null +++ b/test/test-all/helper/index.html @@ -0,0 +1 @@ +{{time | dateFormat:'yyyy-MM-dd hh:mm:ss'}} \ No newline at end of file From 63d4d5d2165589e92714f4aacf0135d9e720f578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Sat, 17 May 2014 02:17:31 +0800 Subject: [PATCH 27/87] v1.0.0 --- README.md | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c69e24e..811de77 100644 --- a/README.md +++ b/README.md @@ -31,17 +31,17 @@ template('tpl/home/main', data) ## 特性 -0. 在开发阶段将编译模板为高性能的 js 文件 +0. 编译模板为不依赖引擎的 js 文件 1. 前端模板按文件与目录组织 3. 模板之间支持引入外部模板 4. 使用同步模板加载接口 5. 支持多种 js 模块输出:AMD、CMD、CommonJS 6. 支持作为 GruntJS 的插件构建项目 -7. 支持前后台模板共享模板目录 +7. 模板目录可被 NodeJS 共享 8. 支持检测修改即时编译 -9. 支持本地调试 +9. 支持运行时调试 -若想深入了解,请继续阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 +若想深入了解,请阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 ## 安装 @@ -210,7 +210,7 @@ npm install grunt-tmod --save-dev **问**:可以使用使用类似 tmpl 那种的 js 原生语法作为模板语法吗? -> 可以。编辑配置文件,设置``"syntax": "native"``即可,这也是模板引擎 artTemplate 的默认语法,目前 TmodJS 默认使用的是 simple 语法。 +> 可以。编辑配置文件,设置``"syntax": "native"``即可,目前 TmodJS 默认使用的是 simple 语法。 **问**:如何兼容旧版本 atc 的项目? @@ -277,7 +277,7 @@ npm install grunt-tmod --save-dev * 吸收了来自业务的一些建议,编译方案的大调整,内部进行无数次优化,编译后的代码更小。 * 编译后的脚本使用统一的接口:``template(path, data)`` 其中 path 相对于 template.js 所在目录 * 自动打包目录与子目录的模板 -* ~~可选支持异步载入模板功能~~ +* ~~可选支持异步载入模板功能~~ * ~~可选嵌入完整模板引擎(使用字符串存储模板)~~ * 可选支持 RequireJS/SeaJS/NodeJS 模块 * 保存模板配置文件(方便多人协作中使用版本管理工具共享配置) diff --git a/package.json b/package.json index 1c503f3..9f492ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.0", + "version": "1.0.0-rc1", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", From 24e0a44684df14d4452cda095258df255ff91a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Tue, 20 May 2014 16:01:49 +0800 Subject: [PATCH 28/87] fix watch bug --- package.json | 2 +- src/tmod.js | 11 ++++------- src/watch.js | 6 +++--- test/tpl/build/template.js | 10 +++++----- test/tpl/package.json | 2 +- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 9f492ad..6ec7ba7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.0-rc1", + "version": "1.0.1-rc2", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/tmod.js b/src/tmod.js index 3ada2bc..b77907d 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -79,7 +79,6 @@ var Tmod = function (base, options) { watch(this.base, function (data) { this.emit('watch', data); }.bind(this), function (folderPath) { - return this.filter(folderPath) && folderPath !== this.output; }.bind(this), fs); @@ -383,11 +382,9 @@ Tmod.prototype = { * @return {Boolean} */ filter: function (file) { - - var stat = fs.statSync(file); - - if (stat) { - + + if (fs.existsSync(file)) { + var stat = fs.statSync(file); if (stat.isDirectory()) { var dirs = file.split(path.sep); @@ -447,7 +444,7 @@ Tmod.prototype = { var fullname = path.join(parent, target); - if (target && fstype === 'file' && this.filter(target)) { + if (target && fstype === 'file' && this.filter(fullname)) {// if (type === 'delete') { diff --git a/src/watch.js b/src/watch.js index f3ea0b4..32bc085 100644 --- a/src/watch.js +++ b/src/watch.js @@ -17,7 +17,7 @@ var timer = {}; var walk = function (dir, callback, filter) { fs.readdirSync(dir).forEach(function (item) { - var fullname = dir + '/' + item; + var fullname = path.join(dir, item); if (fs.statSync(fullname).isDirectory()){ @@ -40,11 +40,11 @@ var watch = function (parent, callback, filter) { watchList[parent] = fs.watch(parent, function (event, filename) { - var fullname = parent + '/' + filename; + var fullname = path.join(parent, filename); var type; var fstype; - if (!filter(filename)) { + if (!filter(fullname)) { return; } diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 679356f..20a71cb 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ /*TMODJS:{"version":"1.0.0"}*/ -!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:47*/ -b("copyright","(c) 2013"),/*v:32*/ -b("index",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var f=d.$include(a,e,c);return j+=f}),f=d.$escape,g=b.title,h=d.$each,i=b.list,j=(b.$value,b.$index,"");return e("./public/header"),j+='

    ',j+=f(g),j+="

    ",e("./public/footer"),j+=" ",new a(j)}),/*v:32*/ -b("public/footer",function(b,c){"use strict";var d=this,e=(d.$helpers,b.time),f=d.$escape,g=function(a,e){e=e||b;var f=d.$include(a,e,c);return h+=f},h="";return h+='",new a(h)}),/*v:32*/ -b("public/header",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var g=d.$include(a,e,c);return f+=g}),f="";return f+=' ',new a(f)}),/*v:32*/ +!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:3*/ +b("copyright","(c) 2013"),/*v:11*/ +b("index",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var f=d.$include(a,e,c);return j+=f}),f=d.$escape,g=b.title,h=d.$each,i=b.list,j=(b.$value,b.$index,"");return e("./public/header"),j+='

    ',j+=f(g),j+="

    ",e("./public/footer"),j+=" ",new a(j)}),/*v:3*/ +b("public/footer",function(b,c){"use strict";var d=this,e=(d.$helpers,b.time),f=d.$escape,g=function(a,e){e=e||b;var f=d.$include(a,e,c);return h+=f},h="";return h+='",new a(h)}),/*v:3*/ +b("public/header",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var g=d.$include(a,e,c);return f+=g}),f="";return f+=' ',new a(f)}),/*v:3*/ b("public/logo",'

    腾讯网

    ')}(); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index be39584..5be3737 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "1.0.0" + "tmodjs": "1.0.1-rc2" }, "tmodjs-config": { "output": "./build", From e18d13ac73c59c2d7f642bf71e2b1915fa95b54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 21 May 2014 13:45:44 +0800 Subject: [PATCH 29/87] upd doc --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 811de77..fafe7bf 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ template('tpl/home/main', data) {{include '../public/header'}} ``` +TmodJS 启动后,会生成一个 js 文件,每次模板的创建与修改都会进行同步编译,开发者过程可直接使用``template(path)``调用本地模板,直到正式上线都无需对代码进行任何修改,行云流水,一气呵成。 + ## 目录 * [特性](#特性) From aa91f352e7617fbc7fc38162e7e4d907825d5988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 21 May 2014 13:54:40 +0800 Subject: [PATCH 30/87] upd doc --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fafe7bf..fd5a793 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ template('tpl/home/main', data) {{include '../public/header'}} ``` -TmodJS 启动后,会生成一个 js 文件,每次模板的创建与修改都会进行同步编译,开发者过程可直接使用``template(path)``调用本地模板,直到正式上线都无需对代码进行任何修改,行云流水,一气呵成。 +TmodJS 一经启动,就无需人工干预,每次模板修改都会自动编译,开发过程可直接使用``template(path)``接口调用模板文件,直到正式上线都无需对代码进行任何修改,行云如流水,一气呵成。 ## 目录 @@ -37,11 +37,11 @@ TmodJS 启动后,会生成一个 js 文件,每次模板的创建与修改都 1. 前端模板按文件与目录组织 3. 模板之间支持引入外部模板 4. 使用同步模板加载接口 -5. 支持多种 js 模块输出:AMD、CMD、CommonJS +5. 可选多种规范的模块输出:AMD、CMD、CommonJS 6. 支持作为 GruntJS 的插件构建项目 7. 模板目录可被 NodeJS 共享 8. 支持检测修改即时编译 -9. 支持运行时调试 +9. 支持模板运行时调试 若想深入了解,请阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 From 57bc5a91bb9308e02905e118a3303452c1aa6372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 23 May 2014 02:16:25 +0800 Subject: [PATCH 31/87] upd doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fd5a793..c3e9a18 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TmodJS -TmodJS(原名 atc)是一个简单易用的前端模板预编译工具,使用它可以实现前端模板模块化与自动化集成,让前端模板拥有后端模板一样的同步“文件”加载能力: +TmodJS(原名 atc)是一个简单易用的前端模板预编译工具,使用它可以实现前端模板模块化与自动化集成,让前端模板拥有后端模板一样的同步“文件”加载能力,让模板组织得井井有条: 一、**按文件与目录组织模板** @@ -14,7 +14,7 @@ template('tpl/home/main', data) {{include '../public/header'}} ``` -TmodJS 一经启动,就无需人工干预,每次模板修改都会自动编译,开发过程可直接使用``template(path)``接口调用模板文件,直到正式上线都无需对代码进行任何修改,行云如流水,一气呵成。 +TmodJS 一经启动,就无需人工干预,每次模板创建与更新都会自动编译,引入一个 js 即可使用``template(path)``接口调用本地模板文件,直到正式上线都无需对代码进行任何修改,整个过程简单自然。 ## 目录 From eabd7b722396da019dd7792988bd7d3b8dde38b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Fri, 23 May 2014 02:22:57 +0800 Subject: [PATCH 32/87] upd doc --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c3e9a18..66e8566 100644 --- a/README.md +++ b/README.md @@ -324,7 +324,9 @@ NodeJS 版本: ## 加入我们 -如果你也认同 TmodJS 的理念、它能让你在开发中体会到书写模板的快乐,那么我希望你也能参与到 TmodJS 这个开源项目中来,无论是贡献代码或者撰写博文推广它等。 +TmodJS 是一个开源项目,它为我们热爱的工作服务!开源项目离不开你的支持,非常期待你通过微博或者博客等来推广 TmodJS。 + +如果你使用了 TmodJS,那么还请留下项目名,我们将在主页展示你的项目。[提交](https://github.com/aui/tmodjs/issues/1) ### 使用 TmodJS 的项目 @@ -336,8 +338,6 @@ NodeJS 版本: * Tracker(腾讯) * …… -[提交项目展示到 TmodJS 主页](https://github.com/aui/tmodjs/issues/1) - ### 贡献名单 * [@aui](https://github.com/aui) From 6517bf57e1c6838a39650142b69ba87e6e3eac3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Tue, 27 May 2014 17:52:26 +0800 Subject: [PATCH 33/87] fix bug --- package.json | 2 +- src/tmod.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6ec7ba7..d010587 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc2", + "version": "1.0.1-rc3", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/tmod.js b/src/tmod.js index b77907d..cd0d421 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -225,7 +225,7 @@ Tmod.prototype = { } - var targetVersion = json.dependencies.tmodjs; + var targetVersion = json.dependencies.tmodjs.replace(/^~/, ''); // 比较模板项目版本号 From 1d56197469bec966b0b15c08eb0987784f2a89c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 16 Jul 2014 17:49:12 +0800 Subject: [PATCH 34/87] upd doc --- README.md | 25 +- doc/upgrade.md | 14 +- doc/why-tmodjs.md | 264 ------------ package.json | 2 +- src/AOTcompile.js | 6 +- src/defaults.js | 2 +- src/runtime.js | 2 +- src/stdout.js | 2 +- src/tmod.js | 29 +- src/uglify.js | 392 ------------------ src/uglify2.js | 8 +- src/watch.js | 6 + test/test-all/combo-off/build/copyright.js | 4 +- test/test-all/combo-off/build/index.js | 17 +- .../test-all/combo-off/build/public/footer.js | 13 +- .../test-all/combo-off/build/public/header.js | 13 +- test/test-all/combo-off/build/public/logo.js | 4 +- test/test-all/combo-off/build/template.js | 81 +--- test/test-all/combo-off/index.html | 2 +- test/test-all/combo-off/package.json | 6 +- test/tpl/build/template.js | 12 +- test/tpl/index.html | 3 +- test/tpl/package.json | 4 +- test/tpl/public/header.html | 2 +- 24 files changed, 85 insertions(+), 828 deletions(-) delete mode 100644 doc/why-tmodjs.md delete mode 100644 src/uglify.js diff --git a/README.md b/README.md index 66e8566..9222895 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TmodJS -TmodJS(原名 atc)是一个简单易用的前端模板预编译工具,使用它可以实现前端模板模块化与自动化集成,让前端模板拥有后端模板一样的同步“文件”加载能力,让模板组织得井井有条: +TmodJS(原名 atc)是一个简单易用的前端模板预编译工具,使用它可以实现前端模板模块化与自动化集成,使得前端模板拥有后端模板一样的同步“文件”加载能力,从而让前端模板实现工程化管理。 一、**按文件与目录组织模板** @@ -65,20 +65,22 @@ TmodJS 的前端模板不再耦合在业务页面中,而是和后端模板一 ## 编译模板 +只需要运行``tmod``这个命令即可,默认配置参数可以满足绝大多数项目。 + ``` tmod [模板目录] [配置参数] ``` -必须是模板的根目录,若无参数则为默认使用当前工作目录,tmodjs 会监控模板目录修改,每次模板修改都会增量编译。 +模板目录必须是模板的根目录,若无参数则为默认使用当前工作目录,tmodjs 会监控模板目录修改,每次模板修改都会增量编译。 ### 配置参数 * ``--debug`` 输出调试版本 * ``--charset value`` 定义模板编码,默认``utf-8`` * ``--output value`` 定义输出目录,默认``./build`` -* ``--type value`` 定义输出模块格式,默认``default``,可选``cmd``、``amd``、``commonjs`` +* ``--type value`` 定义输出模块格式,默认``default``,可选``cmd``、``amd``、``commonjs`` * ``--no-watch`` 关闭模板目录监控 -* ``--version`` 显示版本号 +* ``--version`` 显示版本号 * ``--help`` 显示帮助信息 配置参数将会保存在模板目录[配置文件](#配置)中,下次运行无需输入配置参数(``--no-watch`` 与 ``--debug`` 除外)。 @@ -89,7 +91,7 @@ tmod [模板目录] [配置参数] tmod ./tpl --output ./build ``` -> 如果需要设置模板更多的编译选项,请使用``--config``参数,它会打开模板目录的项目配置文件,可设置语法、公用辅助方法、压缩选项等,参考[配置](#配置)。 +> 如果需要设置模板更多的编译选项,请使用``--init``参数,它生成 ## 使用模板 @@ -196,7 +198,7 @@ npm install grunt-tmod --save-dev **问**:线上运行的模板报错了如何调试? -> 开启 debug 模式编译,如``-d``,这样会输出调试版本,可以让你快速找到模板运行错误的语句以及数据。 +> 开启 debug 模式编译,如``--debug``,这样会输出调试版本,可以让你快速找到模板运行错误的语句以及数据。 **问**:如何不压缩输出 js? @@ -228,6 +230,10 @@ npm install grunt-tmod --save-dev ## 更新日志 +### v1.0.1 + +* 解决新版本设置``"minify":true``输出后,输出的脚本中文没有被编码的问题 + ### v1.0.0 * 使用 artTemplate3.0 作为模板引擎,NodJS 可直接共享前端的模板目录的模板,无需预编译 @@ -279,8 +285,8 @@ npm install grunt-tmod --save-dev * 吸收了来自业务的一些建议,编译方案的大调整,内部进行无数次优化,编译后的代码更小。 * 编译后的脚本使用统一的接口:``template(path, data)`` 其中 path 相对于 template.js 所在目录 * 自动打包目录与子目录的模板 -* ~~可选支持异步载入模板功能~~ -* ~~可选嵌入完整模板引擎(使用字符串存储模板)~~ +* 可选支持异步载入模板功能 +* 可选嵌入完整模板引擎(使用字符串存储模板) * 可选支持 RequireJS/SeaJS/NodeJS 模块 * 保存模板配置文件(方便多人协作中使用版本管理工具共享配置) * 可选编译调试版本 @@ -336,6 +342,7 @@ TmodJS 是一个开源项目,它为我们热爱的工作服务!开源项目 * Spa(迅雷) * MicroTrend(腾讯) * Tracker(腾讯) +* UR(腾讯) * …… ### 贡献名单 @@ -344,8 +351,6 @@ TmodJS 是一个开源项目,它为我们热爱的工作服务!开源项目 * [@TooBug](https://github.com/TooBug) * [@Jsonzhang](https://github.com/Jsonzhang) -目前贡献者均来自腾讯前端团队。 - ### 特别感谢 * [@warmhug](https://github.com/warmhug)(在工具雏形阶段的热心的测试与反馈) diff --git a/doc/upgrade.md b/doc/upgrade.md index e4b412f..3fd0150 100644 --- a/doc/upgrade.md +++ b/doc/upgrade.md @@ -7,7 +7,7 @@ ## 一、迁移模板 迁移模板之前,首先需要在你的项目中新建一个前端模板目录,然后寻找页面中类似这样的模板内容: - + ``` ``` - -然后将`` -# 进击!前端模板工程化 - -###### 基于 TmodJS 前端模板工程化解决方案 - -## 前言 - -现在,越来越多的前端项目采用了单页或者混合式的架构,在这种架构中后端只负责吐出 JSON 数据,前端异步来渲染 HTML,前端模板被大量使用,与此同时也将引发开发维护与发布优化的问题。为了解决这些问题,我们开发了工具 TmodJS(原名 atc),试图通过本地自动化工具让端模板走上工程化之路。很高兴这个工具经过长达 8 个月的磨砺后,它从一个简陋的工具变成如今健壮的前端开发“利器”,并且已经有多个大型项目在使用它。现在我将对前端模板工程化的一些思考与总结分享出来,希望能够让大家更好的了解 TmodJS 的意义所在。在主题开始之前,先简单总结下这些年前端模板技术的发展。 - -## 手工拼接字符串时代 - -早期,开发人员都是直接在 js 文件中采用最原始的方式直接拼接 HTML 字符串: - - var html = ''; - for (var i = 0, users = data.users; i < users.length; i ++) { - html += '
  • ' - + users[i].name - + '
  • '; - } - //... - -这种方式刚开始在一两个简单的页面中还是比较灵活的,但弊端也十分明显:UI 与逻辑代码混杂在一起,阅读起来会非常吃力。一旦随着业务复杂起来,或者多人维护的情况下,几乎会失控。 - -## 前端模板引擎时代 - -受 jquery 作者的 tmpl 模板引擎影响,从 09 年开始到现在,前端模板引擎出现了百花齐放的局面,涌现出一大批行色各异的引擎,几乎每个前端工程中都使用了模板引擎。一条前端模板类似这样: - - - -它使用一个特殊的````标签来存放模板(由于浏览器不支持这种类型的声明,它存放的代码不会当作 js 运行,代码也不会被显示出来)。使用模板引擎渲染模板的示例: - - var html = tmpl('user_tmpl', data); - document.getElementById('content').innerHTML = html; - -[在线示例](http://aui.github.io/artTemplate/demo/simple-syntax/basic.html) - -通过前端模板引擎将 UI 分离后,模板的书写与修改就变得简单多了,也提升了可维护性。但是,随着这种方式规模化后其弊端也随之而来。 - -### 页面内嵌模板之弊端 - -#### 1. 开发调试 - -每次修改前端模板需要改动页面的代码,有时候存放模板的页面又依赖服务器,这使得我们无法使用使用类似 Fiddler 的工具将页面映射到本地进行开发,从而不得不传到服务器或者自己搭建本地服务器环境,以至于开发维护过程异常繁琐。 - -#### 2. 体积优化 - -动态页面与前端模板结合的架构中,不利于浏览器缓存,通常在单页应用中,页面页面会堆砌着大量的````标签,每次进入应用都需要重新加载模板代码,造成不必要的网络开销。 - -#### 3. 模板复用 - -比如类似“好友选择器”这样的公用模块,页面内嵌模板满足不了模板复用的需求。 - -以上三个问题本质都是因为模板堆砌在一个文件中造成的,于是越来越的项目开始将前端模板从页面中迁移出来,目前主要有两种方式: - -### 优化:外置模板 - -#### 1. Ajax 拉取方案 - -通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显: - -1. 无法跨域部署。这是由浏览器同源策略限制的,导致模板无法部署到 CDN 网络。 -2. 复杂度比较高,难以接入主流的自动化部署、优化工具。 - -#### 2. 在 JS 文件中存放模板 - -为避免上述加载模板方案无法跨域的致命缺陷,模板存放在 js 文件中又成了最佳实践方式。但是 js 需要对回车符进行转义,对书写不友好,例如: - - var user_tmpl = - '{{each users as value}}\ -
  • \ - {{value.name}}\ -
  • \ - {{/each}}'; - -或者: - - var user_tmpl = - '{{each users as value}}' - + '
  • ' - + '{{value.name}}' - + '
  • ' - +'{{/each}}'; - -### 模板存放方案优劣总结 - -存放方式 | 书写友好 | CDN 部署 | 本地调试 | 代码复用 | 按需加载 ------- | ------ | ------ | ------ | ------ | ------ | ------ -内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ -Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ -嵌入 js 文件 | ✗ | ✓ | ✓ | ✓ | ✓ - -在实践中我们发现:方便优化的模式不利于开发;利于开发的模式不利于优化。 - -业界后来出现了一些工具试图解决上述问题,如 Handlebars.js 与 Hogan.js(来自 Twitter) 采用了预编译技术来完成模板到 js 的转换,以 Handlebars.js 使用为例,先使用 NodeJS 安装它: - - $ npm install -g handlebars - -然后提取模板内容(``script``标签之间)并保存到一个文件中。在这里我们把它保存为 ``user.tmpl``。运行 ``handlebars`` 命令预编译这个模板文件。 - - $ handlebars user.tmpl -f user.tmpl.js - -编译完成后就可以在前端应用中加载这个脚本,比如这样引入: - - - -在逻辑中可以如下访问到模板函数: - - var template = Handlebars.templates["user.tmpl"]; - var html = template(data); - -预编译工具在一定程度上解决了我们的问题,但由于操作实在是太繁琐,因此它们也并没有流行起来。前端模板因为大量的局部模板存在,相对于后端模板的一个显著特征是碎片化程度高,例如一个 web app 单页应用,几百条前端模板是常有的事儿,开发阶段我们会不断的添加、修改模板,如果每次都需要重新编译这简直会令人抓狂,与此同时大量零散的模板脚本也会引发新的问题,编译后的模板没有提供显式的依赖声明,对于大型项目来说,自动化工具依然难以介入。 - -于是,在这种情况下针对前端模板开发的全新工具 —— TmodJS 顺势而生,只为工程化而来。 - -## 工程化前端模板 - -TmodJS 采用一系列集成方案来最大化提升前端模板开发的效率与质量,本地预编译技术的运用使得我们不必局限于浏览器的技术限制,从而让我们更多的想法通过工具来执行,让前端模板可以大规模使用,从而创造更好的用户体验。 - -### 1. 基于文件系统 - -在 TmodJS 的规范中,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系 —— 这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在``tpl/public``目录下,首屏的模板可以放在``tpl/home``下面。 - -同时,模板内部也支持``include``语句来引入子模板,实现模块复用。例如: - - {{include './public/header'}} - -每个模板就是一个 HTML 片段文件,前端开发工程师可直接将设计师的静态页面的 HTML 拷贝过来,无需对换行符转义,这样开发过程更加便利。 - -总之,使用文件系统来管理模板已经在服务器端模板中得到广泛的验证,而在前端也同样适用,无论项目规模是多么轻量或者庞大。 - -### 2. 使用同步加载接口 - -TmodJS 的同步接口是通过通过预先合并或者使用 AMD、CMD、CommonJS 规范实现,从而避免浏览器的异步加载带来的各种问题,如网络速度、回调套嵌等。 - -例如加载模板``home/index.html``,如果编译为默认类型的模块,使用如下加载方式: - - var tpl = template('home/index'); - var html = tpl(data); - document.getElementById('content').innerHTML = html; - -如果编译为 AMD、CMD、CommonJS 类型的模块,每个模板都是一个标准模块: - - var tpl = requier('./tpl/home/index'); - var html = tpl(data); - document.getElementById('content').innerHTML = html; - -### 3. 构建自动化 - -#### 内置打包合并 - -在默认设置下,TmodJS 会将模板目录所有模板编译后再进行压缩与合并,输出后的 template.js 称之为模板包(内部名称叫 TemplateJS 格式)这种打包的方式非常适合移动端单页 WebApp,输出后的模板包可直接可作为开发阶段与线上运行的文件,适合中小型项目。 - -#### 配合外部工具 - -当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行按需打包合并(例如 GruntJS)。 - -除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 - -总之,模板转换为 js 后不但解决了跨域部署的烦恼,其优化手段也更加灵活多样。案例: - -1. **配合本地构建工具**:腾讯视频前端团队了关闭 TmodJS 的打包合并,让 TmodJS 接入 GruntJS,让 GruntJS 对 TmodJS 输出的脚本进行构建:按照网站栏目建立模板子目录,然后按栏目进行合压缩,然后让栏目页面单独引入合并后的栏目模板。 -2. **后端动态压缩合并**:QQ 空间 CDN 有线上 SeaJS 模块动态合并服务,这时候 TmodJS 编译后的模板会被当作一个普通的 SeaJS 模块引入到项目中,当 UI 模块被调用的时候逻辑与依赖的模板都会进行动态合并加载,完全无需本地构建工具操作。 -3. **自带的优化手段**:MicroTrend 是腾讯内部的一个移动端单页 WebApp 小项目,采用 TmodJS 进行模板管理后,模板被打包压缩到一个 js 文件中,开发阶段输出的模板包直接作为发布后的文件,十分便捷。[查看编译后的模板](http://microtrend.cdc.tencent.com/tpl/dist/template.js) - -### 4. 本地调试支持 - -通常开发阶段模板会经常被修改,所以 TmodJS 支持监听模板目录的修改,当模板发生修改则会进行增量编译,时间久了甚至可以让人忘记编译过程的存在,完全忘记这是在写前端模板。 - -无论如何模板最终都会转换成 js,亦可使用 Fiddler 将线上模板映射到本地进行开发调试;如果开启实时编译,开发阶段模板修改后只需要刷新浏览器即可预览到效果。 - -通常在模板开发阶段会经常遇到数据与预期不符合的情况,前端模板调试成了一个棘手的问题,于是 TmodJS 支持编译调试版本,这样可以在运行时进行调试,控制台可以定位到出错模板所在的行: - - Template Error - - - public/header - - - Render Error - - - Cannot read property '0' of undefined - - - 5 - - - {{users[0].name}} - -### 5. 前后端模板共用 - -前面提到,TmodJS 默认设置下会输出一个包含所有模板的模板包 template.js,这个文件可以兼容多种模块格式,除了通过`` +# 进击!前端模板工程化 + +###### 基于 TmodJS 前端模板工程化解决方案 + +## 前端模板 + +早期,开发人员都是直接在 js 文件中采用最原始的方式直接拼接 HTML 字符串: + + var html = ''; + for (var i = 0, users = data.users; i < users.length; i ++) { + html += '
  • ' + + users[i].name + + '
  • '; + } + //... + +这种方式刚开始在一两个简单的页面中还是比较灵活的,但弊端也十分明显:UI 与逻辑代码混杂在一起,阅读起来会非常吃力。一旦随着业务复杂起来,或者多人维护的情况下,几乎会失控。 + +受 jquery 作者的 tmpl 模板引擎影响,从 09 年开始到现在,前端模板引擎出现了百花齐放的局面,涌现出一大批行色各异的引擎,几乎每个前端工程中都使用了模板引擎。一条前端模板类似这样: + + + +它使用一个特殊的````标签来存放模板(由于浏览器不支持这种类型的声明,它存放的代码不会当作 js 运行,代码也不会被显示出来)。使用模板引擎渲染模板的示例: + + var html = tmpl('user_tmpl', data); + document.getElementById('content').innerHTML = html; + +[在线示例](http://aui.github.io/artTemplate/demo/simple-syntax/basic.html) + +通过前端模板引擎将 UI 分离后,模板的书写与修改就变得简单多了,也提升了可维护性。但是,随着这种方式规模化后其弊端也随之而来。 + +## 模板内嵌的弊端 + +### 开发调试 + +每次修改前端模板需要改动页面的代码,如果不是纯静态页面,无法使用类似 fiddler 的工具将页面映射到本地进行开发,开发调试依赖只能服务端环境,成本过高。 + +### 自动化集成 + +在现代 web 前端工程体系中,几乎每一个环节都拥有相应的优化工具,这些几乎都被 grunt 这个自动构建工具连接起来。但是前端模板若内嵌到页面中,复杂度会比较高,现有的工具难以介入进行优化(例如模板压缩)。 + +### 模块化 + +随着项目规模增大,必然会产生很多可以公用的模板,尤其是多页面共享模板的时候,处理内嵌的公用模板成了一个棘手的问题。 + +## 现有的模板外置解决方案 + +目前越来越的项目已经将模板从页面中迁移出来,目前主要有两种方式: + +### Ajax 拉取方案 + +通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显: + +1. 无法跨域部署。这是由浏览器同源策略限制的,导致模板无法部署到 CDN 网络。 +2. 复杂度比较高,难以接入主流的自动化部署、优化工具。 + +### 在 JS 文件中存放模板 + +为避免上述加载模板方案无法跨域的致命缺陷,模板存放在 js 文件中又成了最佳实践方式,但是这种情况下需要对回车符进行转义,对书写不友好,严重影响开发效率。例如: + + var user_tmpl = + '{{each users as value}}\ +
  • \ + {{value.name}}\ +
  • \ + {{/each}}'; + +或者: + + var user_tmpl = + '{{each users as value}}' + + '
  • ' + + '{{value.name}}' + + '
  • ' + +'{{/each}}'; + +## 模板组织方案优劣总结 + +存放方式 | 开发效率 | 优化空间 | 本地调试 | 代码复用 | 团队协作 +------ | ------ | ------ | ------ | ------ | ------ | ------ +内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ +Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ +嵌入 js 文件 | ✗ | ✓ | ✓ | ✓ | ✓ + +总结:方便优化的模式不利于开发;利于开发的模式不利于优化。 + +## 理想模式 + +看下服务端模板技术是如何做的: + +一、**模板按文件与目录组织模板** + +``` +template('tpl/home/main', data) +``` + +二、**模板使用 include 语句完成复用** + +``` +{{include '../public/header'}} +``` + +这一切看起来很美,前端是否也可以采用这样的模式?但是现实告诉我们,这是一个艰巨的任务。 + +## 现实难题 + +* 浏览器对文本加载会有跨域限制 +* 浏览器同步加载会引起界面卡顿 +* 加载大量的模板文件会带来 http 资源消耗问题 + +## 解决方案 + +针对上述问题,我开发了一个叫 TmodJS 的模板预编译工具。以下是它的简介: + +> TmodJS(原名 atc)是一个简单易用的前端模板预编译工具。它通过预编译技术让前端模板突破浏览器限制,实现后端模板一样的同步“文件”加载能力。它采用目录来组织维护前端模板,从而让前端模板实现工程化管理,最终保证前端模板在复杂单页 web 应用下的可维护性。同时预编译输出的代码经过多层优化,能够在最大程度节省客户端资源消耗。 + +为了实现上述“理想模式”,它采用三种方案来实现: + +### 1.本地构建 + +模板编写完成后,通过一个本地工具将模板编译成浏览器可执行的代码——js,这样就可以用脚本的方式来加载模板,不必受浏览器的同源策略限制,模板可以部署到任意 CDN,而无需处理跨域问题。 + +工具内部采用模板引擎——[artTemplate](https://github.com/aui/artTemplate)完成模板编译,输出 js 文件。[artTemplate](https://github.com/aui/artTemplate)是我另外一个开源项目,它支持预编译,编译后的代码可以无需引擎运行。 + +### 2.种子文件 + +为了实现``template(path, data)``这种同步接口,TmodJS 会不断的更新一个名为 template.js 的种子文件,这个文件合并了公用方法与编译后的模板,项目只需要引用这个文件就可以按路径同步的方式调用模板。例如: + + var tpl = template('home/index'); + var html = tpl(data); + document.getElementById('content').innerHTML = html; + +### 3.模板目录 + +为了保证工具能够管理模板,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系——这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在``tpl/public``目录下,首屏的模板可以放在``tpl/home``下面。 + +## 模板与工程化 + +TmodJS 采用了自动编译机制,一经启动后无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改,整个过程简单自然。 + +与其说 TmodJS 是一个模板预编译器,不如说它是一个前端模板工程管理工具,它能做到: + +### 模板压缩与合并 + +TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进行一次压缩,此时输出的 js 甚至会比原始模板更小(最高可减少一半的体积)。 + +在默认设置下,TmodJS 会将模板目录所有模板编译后再进行压缩与合并,输出后的 template.js 称之为模板包(内部名称叫 TemplateJS 格式)这种打包的方式非常适合移动端单页 WebApp,输出后的模板包可直接可作为开发阶段与线上运行的文件,适合中小型项目。 + +[查看编译后的模板示例](http://microtrend.cdc.tencent.com/tpl/dist/template.js) + +### 依赖管理 + +当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行按需打包合并(例如 grunt)。 + +除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 + +### 本地调试 + +模板最终对应的是 js 文件,所以可以使用 Fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: + + Template Error + + + public/header + + + Render Error + + + Cannot read property '0' of undefined + + + 5 + + + {{users[0].name}} + +### 与第三方自动化构建工具配合 + +目前支持 grunt 与 gulp 这两种自动化构建工具。 + +### 前后端模板共享 + +TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate 提供了 NodeJS 版本,可以直接读取 TmodJS 的模板目录,这意味着可以轻松的做到前后端模板共享,技术方案自由切换。 + +### 多人协作 + +* 配置可共享:TmodJS 配置保存在模板目录的 package.json 文件中 +* 强制编码规范:编译后的代码运行在沙箱中,从而避免模板访问外部对象导致依赖问题;模板需要的公用方法、变量需配置后才能使用 + +## 关于 TmodJS + +起源于腾讯内部公用组件平台的开源项目(atc),历经多次版本迭代,目前已经有多个项目在使用。 + + + +### 服务项目 + +* QQ空间 +* 腾讯视频 +* 爱奇艺 +* 爱拍原创 +* Spa(迅雷) +* MicroTrend(腾讯) +* Tracker(腾讯) +* UR(腾讯) +* …… + +### 愿景 + +希望 TmodJS 能成为每个前端开发者必备的利器! + + From a1f82a864df30387cd99c7d0e6026d03640b3f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 17:11:24 +0800 Subject: [PATCH 48/87] upd doc --- doc/why-tmodjs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index b308c8d..99b0cb1 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -119,17 +119,17 @@ template('tpl/home/main', data) ## 解决方案 -针对上述问题,我开发了一个叫 TmodJS 的模板预编译工具。以下是它的简介: +为了实现上述“理想模式”,我们推出了 TmodJS——模板预编译器,以下是它的简介: > TmodJS(原名 atc)是一个简单易用的前端模板预编译工具。它通过预编译技术让前端模板突破浏览器限制,实现后端模板一样的同步“文件”加载能力。它采用目录来组织维护前端模板,从而让前端模板实现工程化管理,最终保证前端模板在复杂单页 web 应用下的可维护性。同时预编译输出的代码经过多层优化,能够在最大程度节省客户端资源消耗。 -为了实现上述“理想模式”,它采用三种方案来实现: +它采用三种方案来解决难题: ### 1.本地构建 模板编写完成后,通过一个本地工具将模板编译成浏览器可执行的代码——js,这样就可以用脚本的方式来加载模板,不必受浏览器的同源策略限制,模板可以部署到任意 CDN,而无需处理跨域问题。 -工具内部采用模板引擎——[artTemplate](https://github.com/aui/artTemplate)完成模板编译,输出 js 文件。[artTemplate](https://github.com/aui/artTemplate)是我另外一个开源项目,它支持预编译,编译后的代码可以无需引擎运行。 +工具内部采用模板引擎——[artTemplate](https://github.com/aui/artTemplate)完成模板编译,输出 js 文件。[artTemplate](https://github.com/aui/artTemplate)也是来自腾讯的开源项目,它支持预编译,编译后的代码可以无需引擎运行。 ### 2.种子文件 @@ -141,7 +141,7 @@ template('tpl/home/main', data) ### 3.模板目录 -为了保证工具能够管理模板,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系——这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在``tpl/public``目录下,首屏的模板可以放在``tpl/home``下面。 +为了让团队成员、自动化工具能更好的管理模板,前端模板不再内嵌到页面中,而是使用专门的目录进行组织维护;使用路径作为模板的 ID,这样与源文件保持对应关系——这样好处就是极大的增加了可维护性。例如:页面头部底部的公用模板可以放在``tpl/public``目录下,首屏的模板可以放在``tpl/home``下面。 ## 模板与工程化 From d0921312611281fffd61b79fb13d9553eae2d387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 17:16:15 +0800 Subject: [PATCH 49/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 99b0cb1..91b901b 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -34,7 +34,7 @@ var html = tmpl('user_tmpl', data); document.getElementById('content').innerHTML = html; -[在线示例](http://aui.github.io/artTemplate/demo/simple-syntax/basic.html) +[在线示例](http://aui.github.io/artTemplate/demo/basic.html) 通过前端模板引擎将 UI 分离后,模板的书写与修改就变得简单多了,也提升了可维护性。但是,随着这种方式规模化后其弊端也随之而来。 From 63972f4cbba9e975eec8e70d7e44e3347f29ffa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 17:18:46 +0800 Subject: [PATCH 50/87] upd doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0da5ac4..8e7feab 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ TmodJS 一经启动,就无需人工干预,每次模板创建与更新都会 9. 支持模板运行时调试 10. 配置文件支持多人共享 -若想深入了解,请阅读:《[进击!前端模板工程化](http://aui.github.io/tmodjs/)》 +若想深入了解,请阅读:《[进击!前端模板工程化](https://github.com/aui/tmodjs/blob/master/doc/why-tmodjs.md)》 ## 文档目录 From 3bf59112714e3f51cecee887916d1245ee3b4c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 18:59:39 +0800 Subject: [PATCH 51/87] upd doc --- doc/why-tmodjs.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 91b901b..c4c0e31 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -1,4 +1,3 @@ - # 进击!前端模板工程化 ###### 基于 TmodJS 前端模板工程化解决方案 @@ -58,10 +57,7 @@ ### Ajax 拉取方案 -通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显: - -1. 无法跨域部署。这是由浏览器同源策略限制的,导致模板无法部署到 CDN 网络。 -2. 复杂度比较高,难以接入主流的自动化部署、优化工具。 +通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显:由于浏览器同源策略限制的,导致模板无法部署到 CDN 网络中。 ### 在 JS 文件中存放模板 @@ -86,7 +82,7 @@ ## 模板组织方案优劣总结 存放方式 | 开发效率 | 优化空间 | 本地调试 | 代码复用 | 团队协作 ------- | ------ | ------ | ------ | ------ | ------ | ------ +------ | ------ | ------ | ------ | ------ | ------ 内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ 嵌入 js 文件 | ✗ | ✓ | ✓ | ✓ | ✓ From 7b91291b6275a65824fcd21b8f5c17b868a58365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:00:55 +0800 Subject: [PATCH 52/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index c4c0e31..4cd6914 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -53,7 +53,7 @@ ## 现有的模板外置解决方案 -目前越来越的项目已经将模板从页面中迁移出来,目前主要有两种方式: +目前越来越的项目已经将模板从页面中迁移出来,主要有两种方式: ### Ajax 拉取方案 From a4a72d87c221be554df725a0083ca9a5e7c52a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:14:06 +0800 Subject: [PATCH 53/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 4cd6914..1076294 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -213,6 +213,6 @@ TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate ### 愿景 -希望 TmodJS 能成为每个前端开发者必备的利器! +请忘记前端模板。 From c909d7c3388d7fb2f2159c8363836fab064d9409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:22:30 +0800 Subject: [PATCH 54/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 1076294..025ed4e 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -213,6 +213,6 @@ TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate ### 愿景 -请忘记前端模板。 +忘记前端模板。 From 58eaacefc3b5daf216cf9bc2e396abc46e01f4dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:29:02 +0800 Subject: [PATCH 55/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 025ed4e..244e025 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -141,7 +141,7 @@ template('tpl/home/main', data) ## 模板与工程化 -TmodJS 采用了自动编译机制,一经启动后无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改,整个过程简单自然。 +TmodJS 采用了自动编译机制,一经启动后就无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改,整个过程简单自然。 与其说 TmodJS 是一个模板预编译器,不如说它是一个前端模板工程管理工具,它能做到: From 8ca2aa54e4b567368b42ad19f9275abc88dfc263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:30:06 +0800 Subject: [PATCH 56/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 244e025..0c6fa0c 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -161,7 +161,7 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 本地调试 -模板最终对应的是 js 文件,所以可以使用 Fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: +因为模板已经被独立出来,所以可以使用 Fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: Template Error From df8decb033e1417dd7d487acc966b0992ac15711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:33:58 +0800 Subject: [PATCH 57/87] upd doc --- doc/why-tmodjs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 0c6fa0c..18be7fc 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -157,11 +157,11 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行按需打包合并(例如 grunt)。 -除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入 RequireJS 优化工具 r.js 或者 SeaJS 的 spm 可以完成精准依赖合并。 +除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入对应的 grunt 插件可自动完成依赖合并。 ### 本地调试 -因为模板已经被独立出来,所以可以使用 Fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: +因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: Template Error From 2f8d2c7c65af7abdba2fdf44cca26a750df99144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:44:09 +0800 Subject: [PATCH 58/87] upd doc --- doc/why-tmodjs.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 18be7fc..3505679 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -79,9 +79,9 @@ + '' +'{{/each}}'; -## 模板组织方案优劣总结 +## 现有模板组织方案优劣总结 -存放方式 | 开发效率 | 优化空间 | 本地调试 | 代码复用 | 团队协作 +组织方式 | 开发效率 | 优化空间 | 本地调试 | 代码复用 | 团队协作 ------ | ------ | ------ | ------ | ------ | ------ 内嵌业务页中 | ✓ | ✗ | ✗ | ✗ | ✗ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ @@ -91,7 +91,7 @@ Ajax 远程加载 | ✓ | ✗| ✓ | ✓ | ✓ ## 理想模式 -看下服务端模板技术是如何做的: +看下服务端模板是如何做的: 一、**模板按文件与目录组织模板** @@ -193,6 +193,13 @@ TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate * 配置可共享:TmodJS 配置保存在模板目录的 package.json 文件中 * 强制编码规范:编译后的代码运行在沙箱中,从而避免模板访问外部对象导致依赖问题;模板需要的公用方法、变量需配置后才能使用 +## 成果 + +组织方式 | 开发效率 | 优化空间 | 本地调试 | 代码复用 | 团队协作 +------ | ------ | ------ | ------ | ------ | ------ +TmodJS | ✓ | ✓ | ✓ | ✓ | ✓ + + ## 关于 TmodJS 起源于腾讯内部公用组件平台的开源项目(atc),历经多次版本迭代,目前已经有多个项目在使用。 From d4dc811f5b746ce19c0d0e0d53bad9f2f556d74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 19:47:18 +0800 Subject: [PATCH 59/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 3505679..8adfa5f 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -161,7 +161,7 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 本地调试 -因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板以及所在行号,例如: +因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板路径以及所在行号,例如: Template Error From 0f54c0fb3c033bb6ba5ace1a249d9cf44b603f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 21:42:52 +0800 Subject: [PATCH 60/87] upd doc --- README.md | 4 ++-- doc/why-tmodjs.md | 20 +++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8e7feab..811cd0d 100644 --- a/README.md +++ b/README.md @@ -345,8 +345,6 @@ NodeJS 版本: TmodJS 是一个开源项目,如果你喜欢,非常期待你通过微博或者博客等来宣传 TmodJS。 -如果你使用了 TmodJS,那么还请留下项目名,我们将在主页展示你的项目。[提交](https://github.com/aui/tmodjs/issues/1) - ### 使用 TmodJS 的项目 * QQ空间 @@ -359,6 +357,8 @@ TmodJS 是一个开源项目,如果你喜欢,非常期待你通过微博或 * UR(腾讯) * …… +如果你使用了 TmodJS 敬请留下项目名,我们将在 TmodJS 主页展示你的项目。[提交](https://github.com/aui/tmodjs/issues/1) + ### 代码贡献名单 * [@aui](https://github.com/aui) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 8adfa5f..d9408d6 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -41,7 +41,7 @@ ### 开发调试 -每次修改前端模板需要改动页面的代码,如果不是纯静态页面,无法使用类似 fiddler 的工具将页面映射到本地进行开发,开发调试依赖只能服务端环境,成本过高。 +每次修改前端模板需要改动页面的代码,如果不是纯静态页面,无法使用类似 fiddler 的工具将页面映射到本地进行开发,开发调试依赖只能服务端环境,效率低下。 ### 自动化集成 @@ -55,9 +55,9 @@ 目前越来越的项目已经将模板从页面中迁移出来,主要有两种方式: -### Ajax 拉取方案 +### ajax 拉取方案 -通过 Ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显:由于浏览器同源策略限制的,导致模板无法部署到 CDN 网络中。 +通过 ajax 加载远程模板,然后再使用模板引擎解析。这种方式的好处就是模板可以按文件存放,书写起来也是十分便利,但弊端相当明显:由于浏览器同源策略限制的,导致模板无法部署到 CDN 网络中。 ### 在 JS 文件中存放模板 @@ -125,7 +125,7 @@ template('tpl/home/main', data) 模板编写完成后,通过一个本地工具将模板编译成浏览器可执行的代码——js,这样就可以用脚本的方式来加载模板,不必受浏览器的同源策略限制,模板可以部署到任意 CDN,而无需处理跨域问题。 -工具内部采用模板引擎——[artTemplate](https://github.com/aui/artTemplate)完成模板编译,输出 js 文件。[artTemplate](https://github.com/aui/artTemplate)也是来自腾讯的开源项目,它支持预编译,编译后的代码可以无需引擎运行。 +工具内部采用模板引擎——[artTemplate](https://github.com/aui/artTemplate) 完成模板编译,输出 js 文件。artTemplate 也是来自腾讯的开源项目,它支持预编译,编译后的代码可以无需引擎运行。 ### 2.种子文件 @@ -141,9 +141,7 @@ template('tpl/home/main', data) ## 模板与工程化 -TmodJS 采用了自动编译机制,一经启动后就无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改,整个过程简单自然。 - -与其说 TmodJS 是一个模板预编译器,不如说它是一个前端模板工程管理工具,它能做到: +TmodJS 采用了自动编译机制,一经启动后就无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改。与其说 TmodJS 是一个模板预编译器,不如说它是一个前端模板工程管理工具,它能做到: ### 模板压缩与合并 @@ -161,7 +159,7 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 本地调试 -因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,TmodJS 开启``debug``模式后可以直接找到出错的模板路径以及所在行号,例如: +因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,开启 TmodJS 的``debug``模式后可以直接找到出错的模板路径以及所在行号,例如: Template Error @@ -182,7 +180,7 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 与第三方自动化构建工具配合 -目前支持 grunt 与 gulp 这两种自动化构建工具。 +目前提供 grunt 与 gulp 这两种自动化构建工具的插件。 ### 前后端模板共享 @@ -191,7 +189,7 @@ TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate ### 多人协作 * 配置可共享:TmodJS 配置保存在模板目录的 package.json 文件中 -* 强制编码规范:编译后的代码运行在沙箱中,从而避免模板访问外部对象导致依赖问题;模板需要的公用方法、变量需配置后才能使用 +* 强制编码规范:编译后的代码运行在沙箱中,从而避免模板访问页面中全局对象,导致依赖问题;模板需要的公用方法、变量需配置后才能使用 ## 成果 @@ -206,7 +204,7 @@ TmodJS | ✓ | ✓ | ✓ | ✓ | ✓ -### 服务项目 +### 案例 * QQ空间 * 腾讯视频 From f97ddb311d96c469187afb74bf5284b0b6e211d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 23:00:20 +0800 Subject: [PATCH 61/87] upd doc --- doc/why-tmodjs.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index d9408d6..f157a9b 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -178,6 +178,13 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 {{users[0].name}} +### 沙箱与扩展 + +很多开发者习惯在模板中访问页面中全局定义的函数,如果模板内嵌到页面中问题似乎不大,一旦模板外置后这种隐含的依赖关系将会导致严重的维护问题,而 TmodJS 采用沙箱机制来解决此问题: + +1. 限制开发者访问外部对象:模板用到的所有变量在闭包中被强制指向模板数据 +2. 公用方法需要注册才能使用:可指定一个外部 js 作为公用方法(辅助方法),这个 js 会被合并到到 template.js 中,只有这样模板才能访问外部方法 + ### 与第三方自动化构建工具配合 目前提供 grunt 与 gulp 这两种自动化构建工具的插件。 @@ -185,11 +192,6 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 前后端模板共享 TmodJS 与 artTemplate 模板引擎使用同样的模板语法,而 artTemplate 提供了 NodeJS 版本,可以直接读取 TmodJS 的模板目录,这意味着可以轻松的做到前后端模板共享,技术方案自由切换。 - -### 多人协作 - -* 配置可共享:TmodJS 配置保存在模板目录的 package.json 文件中 -* 强制编码规范:编译后的代码运行在沙箱中,从而避免模板访问页面中全局对象,导致依赖问题;模板需要的公用方法、变量需配置后才能使用 ## 成果 From b313cd4444e0aacffac7ca3c07e0b67305feb656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Wed, 23 Jul 2014 23:06:17 +0800 Subject: [PATCH 62/87] upd doc --- doc/why-tmodjs.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index f157a9b..4efc1af 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -180,10 +180,9 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 沙箱与扩展 -很多开发者习惯在模板中访问页面中全局定义的函数,如果模板内嵌到页面中问题似乎不大,一旦模板外置后这种隐含的依赖关系将会导致严重的维护问题,而 TmodJS 采用沙箱机制来解决此问题: +很多开发者习惯在模板中访问页面中全局定义的函数,如果模板内嵌到页面中问题似乎不大,一旦模板外置后这种隐含的依赖关系将会导致严重的维护问题,而 TmodJS 采用沙箱机制来解决此问题:限制开发者访问外部对象,模板用到的所有变量在闭包中被强制指向模板数据。 -1. 限制开发者访问外部对象:模板用到的所有变量在闭包中被强制指向模板数据 -2. 公用方法需要注册才能使用:可指定一个外部 js 作为公用方法(辅助方法),这个 js 会被合并到到 template.js 中,只有这样模板才能访问外部方法 +为了方便扩展模板的功能,可指定一个外部 js 作为公用方法(辅助方法),这个 js 会被合并到到 template.js 中。 ### 与第三方自动化构建工具配合 From 2f762e3e48e664ca7dba0717d10a58a26c99a1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 24 Jul 2014 01:15:33 +0800 Subject: [PATCH 63/87] upd doc --- doc/why-tmodjs.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 4efc1af..f372a25 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -141,7 +141,7 @@ template('tpl/home/main', data) ## 模板与工程化 -TmodJS 采用了自动编译机制,一经启动后就无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改。与其说 TmodJS 是一个模板预编译器,不如说它是一个前端模板工程管理工具,它能做到: +TmodJS 采用了自动编译机制,一经启动后就无需人工干预,每次模板创建与更新都会自动编译,直到正式上线都无需对代码进行任何修改。实现文件系统的前端模板只是 TmodJS 最基本的任务,它在背后还做了这些优化: ### 模板压缩与合并 @@ -153,13 +153,11 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 依赖管理 -当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,所以 TmodJS 还可以选择输出单个的 js 文件,这样这些模板脚本可以交给外部工具进行按需打包合并(例如 grunt)。 - -除此之外,如果编译为 AMD、CMD、CommonJS 类型的的模块,模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入对应的 grunt 插件可自动完成依赖合并。 +当然,将所有前端模板都打包在一个文件中不一定适合每一个项目,因为很多大型项目需要更细致的优化,将模板编译为 AMD、CMD、CommonJS 类型的的模块是一个不错的选择,此时模板内部的``include``语句会编译成``requier('xxx/xxx')``形式声明依赖,接入对应的 grunt 插件可自动完成依赖合并。 ### 本地调试 -因为模板已经被独立出来,所以可以使用 fiddler 将线上模板映射到本地进行开发调试。如果线上模板报错,开启 TmodJS 的``debug``模式后可以直接找到出错的模板路径以及所在行号,例如: +因为模板已经被独立出来,所以使用 fiddler 将线上模板映射到本地进行开发调试将十分容易。如果线上模板报错,开启 TmodJS 的``debug``模式后可以直接找到出错的模板路径以及所在行号,例如: Template Error @@ -184,9 +182,21 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 为了方便扩展模板的功能,可指定一个外部 js 作为公用方法(辅助方法),这个 js 会被合并到到 template.js 中。 +### 安全过滤 + +模板的变量输出默认都会被过滤函数包裹,在运行时进行过滤,从而避免模板开发者因为疏忽导致站点 XSS 漏洞。例如: + +模板 + +

    {{title}}

    + +编译代码 + + "

    " + $escape(title) + "

    " + ### 与第三方自动化构建工具配合 -目前提供 grunt 与 gulp 这两种自动化构建工具的插件。 +目前 TmodJS 已有 grunt 与 gulp 这两种流行的自动化构建工具的插件,未来将支持更多的自动化工具。 ### 前后端模板共享 @@ -201,7 +211,7 @@ TmodJS | ✓ | ✓ | ✓ | ✓ | ✓ ## 关于 TmodJS -起源于腾讯内部公用组件平台的开源项目(atc),历经多次版本迭代,目前已经有多个项目在使用。 +起源于腾讯内部公用组件平台的开源项目(atc),历经无数次的改进,目前已经有多个项目在使用。 From 75e6fcb14decfaa37875fe3c250a2262bd63e189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 24 Jul 2014 01:22:06 +0800 Subject: [PATCH 64/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index f372a25..efd9718 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -178,7 +178,7 @@ TmodJS 编译之前会压缩掉模板的空白字符,编译为 js 后又会进 ### 沙箱与扩展 -很多开发者习惯在模板中访问页面中全局定义的函数,如果模板内嵌到页面中问题似乎不大,一旦模板外置后这种隐含的依赖关系将会导致严重的维护问题,而 TmodJS 采用沙箱机制来解决此问题:限制开发者访问外部对象,模板用到的所有变量在闭包中被强制指向模板数据。 +很多开发者习惯在模板中访问页面中全局定义的函数,如果模板内嵌到页面中问题似乎不大,一旦模板外置后这种隐含的依赖关系将会导致严重的维护问题,TmodJS 采用沙箱机制来解决此问题:限制开发者访问外部对象,模板用到的所有变量在闭包中被强制指向模板数据。 为了方便扩展模板的功能,可指定一个外部 js 作为公用方法(辅助方法),这个 js 会被合并到到 template.js 中。 From c615c4d9fc45e98213e9d796c178682af8015f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 24 Jul 2014 01:32:31 +0800 Subject: [PATCH 65/87] upd doc --- doc/why-tmodjs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index efd9718..86b4120 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -49,7 +49,7 @@ ### 模块化 -随着项目规模增大,必然会产生很多可以公用的模板,尤其是多页面共享模板的时候,处理内嵌的公用模板成了一个棘手的问题。 +前端模板集中在一个文件中这必然会引起多人协作的问题,随着项目复杂度增加,按文件模块化迫在眉睫。 ## 现有的模板外置解决方案 From 4fa26ed99c0ac98f4e5892fd5e344e035ac836f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 24 Jul 2014 01:34:05 +0800 Subject: [PATCH 66/87] upd doc --- doc/why-tmodjs.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 86b4120..9b2b429 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -43,10 +43,6 @@ 每次修改前端模板需要改动页面的代码,如果不是纯静态页面,无法使用类似 fiddler 的工具将页面映射到本地进行开发,开发调试依赖只能服务端环境,效率低下。 -### 自动化集成 - -在现代 web 前端工程体系中,几乎每一个环节都拥有相应的优化工具,这些几乎都被 grunt 这个自动构建工具连接起来。但是前端模板若内嵌到页面中,复杂度会比较高,现有的工具难以介入进行优化(例如模板压缩)。 - ### 模块化 前端模板集中在一个文件中这必然会引起多人协作的问题,随着项目复杂度增加,按文件模块化迫在眉睫。 From cdbae59f425d446d5788bc45a999508bb8f7ea60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 24 Jul 2014 01:37:40 +0800 Subject: [PATCH 67/87] upd doc --- doc/why-tmodjs.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/why-tmodjs.md b/doc/why-tmodjs.md index 9b2b429..53882e5 100644 --- a/doc/why-tmodjs.md +++ b/doc/why-tmodjs.md @@ -43,6 +43,10 @@ 每次修改前端模板需要改动页面的代码,如果不是纯静态页面,无法使用类似 fiddler 的工具将页面映射到本地进行开发,开发调试依赖只能服务端环境,效率低下。 +### 自动化构建 + +在现代 web 前端工程体系中,几乎每一个环节都拥有相应的优化工具,这些几乎都被 grunt 这个自动构建工具连接起来。但是前端模板若内嵌到页面中,复杂度会比较高,现有工具因为受限难以进行自动优化。 + ### 模块化 前端模板集中在一个文件中这必然会引起多人协作的问题,随着项目复杂度增加,按文件模块化迫在眉睫。 From 0c4b374690ef2d58f27aea93fd95888a0783ad6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 31 Jul 2014 12:36:38 +0800 Subject: [PATCH 68/87] =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B9=9F=E9=81=B5=E5=BE=AA``type``=E7=9A=84=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E5=90=8C=E6=97=B6=E6=96=B0=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?``global``=E7=B1=BB=E5=9E=8B=E6=A8=A1=E5=9D=97=20#45=20#39?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/defaults.js | 6 +- src/runtime.js | 66 ++++++++++--- src/tmod.js | 3 +- test/test-all/amd/build/copyright.js | 2 +- test/test-all/amd/build/index.js | 2 +- test/test-all/amd/build/public/footer.js | 2 +- test/test-all/amd/build/public/header.js | 2 +- test/test-all/amd/build/public/logo.js | 4 +- test/test-all/amd/build/template.js | 6 +- test/test-all/amd/package.json | 2 +- test/test-all/global.html | 49 ++++++++++ test/test-all/global/build/template.js | 115 +++++++++++++++++++++++ test/test-all/global/copyright.html | 1 + test/test-all/global/index.html | 12 +++ test/test-all/global/package.json | 20 ++++ test/test-all/global/public/footer.html | 6 ++ test/test-all/global/public/header.html | 11 +++ test/test-all/global/public/logo.html | 7 ++ test/tpl/build/template.js | 12 +-- test/tpl/package.json | 2 +- 21 files changed, 295 insertions(+), 37 deletions(-) create mode 100644 test/test-all/global.html create mode 100644 test/test-all/global/build/template.js create mode 100644 test/test-all/global/copyright.html create mode 100644 test/test-all/global/index.html create mode 100644 test/test-all/global/package.json create mode 100644 test/test-all/global/public/footer.html create mode 100644 test/test-all/global/public/header.html create mode 100644 test/test-all/global/public/logo.html diff --git a/package.json b/package.json index ad1b45e..7b4a2db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc5", + "version": "1.0.1-rc6", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/defaults.js b/src/defaults.js index a0047ab..34ee362 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -31,15 +31,15 @@ module.exports = { // commonjs: 编译为 NodeJS 模块 type: 'default', - // 设置输出的运行时名称 + // 设置输出的运行时文件名 runtime: 'template.js', // 设置模块依赖的运行时路径 - // 仅针对于非``default``的类型模块配置字段。如果不指定模块内部会自动使用相对 runtime 的路径 + // 仅针对于非``type:'default'``的模块配置字段。如果不指定模块内部会自动使用相对``runtime``的路径 alias: null, // 是否合并模板 - // 仅针对于 default 类型的模块 + // 仅针对于``type:'default'``的模块 combo: true, // 是否输出为压缩的格式 diff --git a/src/runtime.js b/src/runtime.js index 39d7eba..04ab3e2 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -1,6 +1,6 @@ /*! Template Runtime */ -var runtime = function (String) { +var runtime = function () { function template (filename, content) { return ( @@ -72,7 +72,6 @@ var runtime = function (String) { function resolve (from, to) { var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/; - //var dirname = from.replace(/^([^.])/, './$1').replace(/[^/]+$/, ""); var dirname = ('./' + from).replace(/[^/]+$/, ""); var filename = dirname + to; filename = filename.replace(/\/\.\//g, "/"); @@ -174,30 +173,67 @@ var runtime = function (String) { }; - // RequireJS && SeaJS - if (typeof define === 'function') { - define(function() { - return template; - }); - - // NodeJS - } else if (typeof exports !== 'undefined') { - module.exports = template; - } else { - this.template = template; - } - + '<:namespace:>' '<:helpers:>' '<:templates:>' }.toString(); +var getNamespaceCode = function (type) { + var code = ''; + + switch (type) { + + // RequireJS / SeaJS 兼容模块格式 + case 'cmd': + // RequireJS 模块格式 + case 'amd': + + code + = "define(function(){" + + "return template;" + + "});"; + break; + + // NodeJS 模块格式 + case 'commonjs': + + code = "module.exports = template;" + break; + + // 在全局定义 + case 'global': + + code = 'this.template = template;'; + break; + + // 自适应格式 + default: + + code + = "if (typeof define === 'function') {" + + "define(function() {" + + "return template;" + + "});" + + "} else if (typeof exports !== 'undefined') {" + + "module.exports = template;" + + "} else {" + + "this.template = template;" + + "}"; + + } + + return code; +}; var VAR_RE = /['"]<\:(.*)\:>['"]/g; module.exports = function (data) { + + data.namespace = getNamespaceCode(data.type); + var code = runtime .replace(VAR_RE, function ($1, $2) { return data[$2] || ''; diff --git a/src/tmod.js b/src/tmod.js index 88bc061..1d68cf9 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -524,7 +524,7 @@ Tmod.prototype = { // 根据生成模块的类型删除不支持的配置字段 - if (options.type === 'default') { + if (options.type === 'default' || options.type === 'global') { delete options.alias; } else { delete options.combo; @@ -692,6 +692,7 @@ Tmod.prototype = { var error = null; var runtimeCode = runtime({ + type: this.options.type, helpers: this._helpersCode, templates: templates }); diff --git a/test/test-all/amd/build/copyright.js b/test/test-all/amd/build/copyright.js index cbd23f7..06ced26 100644 --- a/test/test-all/amd/build/copyright.js +++ b/test/test-all/amd/build/copyright.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":9,"md5":"51b386488e7a5ee49862702bf4f9ff1e"}*/ +/*TMODJS:{"version":10,"md5":"dab122db77ff232c93de9fb84a59141d"}*/ define([ "./template", "" ], function(template) { return template("copyright", "(c) 2013"); }); \ No newline at end of file diff --git a/test/test-all/amd/build/index.js b/test/test-all/amd/build/index.js index 2a3d142..cfd6da2 100644 --- a/test/test-all/amd/build/index.js +++ b/test/test-all/amd/build/index.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":9,"md5":"d7b17ca0cce05bcb005720730d51c288"}*/ +/*TMODJS:{"version":10,"md5":"8804d1e566c02e1752278f059521998e"}*/ define([ "./template", "./public/header", "./public/footer" ], function(template) { return template("index", function($data, $filename) { "use strict"; diff --git a/test/test-all/amd/build/public/footer.js b/test/test-all/amd/build/public/footer.js index 38ac4b7..1e14ce4 100644 --- a/test/test-all/amd/build/public/footer.js +++ b/test/test-all/amd/build/public/footer.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":9,"md5":"611dbb7dc160f8466b57a49c418d9e68"}*/ +/*TMODJS:{"version":10,"md5":"59c2faf9fb8603adb836ad83babc2cd3"}*/ define([ "../template", "../copyright" ], function(template) { return template("public/footer", function($data, $filename) { "use strict"; diff --git a/test/test-all/amd/build/public/header.js b/test/test-all/amd/build/public/header.js index be221fb..7e770db 100644 --- a/test/test-all/amd/build/public/header.js +++ b/test/test-all/amd/build/public/header.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":9,"md5":"1b08291b07538e26d351d02c635b2d46"}*/ +/*TMODJS:{"version":10,"md5":"f131eb7300f7037adc98bcd30bfc38db"}*/ define([ "../template", "./logo" ], function(template) { return template("public/header", function($data, $filename) { "use strict"; diff --git a/test/test-all/amd/build/public/logo.js b/test/test-all/amd/build/public/logo.js index d677467..43e1209 100644 --- a/test/test-all/amd/build/public/logo.js +++ b/test/test-all/amd/build/public/logo.js @@ -1,4 +1,4 @@ -/*TMODJS:{"version":9,"md5":"c396ecdfa7e711aeb80796f2f5f6fd08"}*/ +/*TMODJS:{"version":10,"md5":"72904bcb9592df68a9bd08dca5c1f013"}*/ define([ "../template", "" ], function(template) { - return template("public/logo", '

    腾讯网

    '); + return template("public/logo", ""); }); \ No newline at end of file diff --git a/test/test-all/amd/build/template.js b/test/test-all/amd/build/template.js index 7afd16b..3b4ffe7 100644 --- a/test/test-all/amd/build/template.js +++ b/test/test-all/amd/build/template.js @@ -1,5 +1,5 @@ /*TMODJS:{}*/ -!function(String) { +!function() { function template(filename, content) { return (/string|function/.test(typeof content) ? compile : renderFile)(filename, content); } @@ -75,7 +75,7 @@ return cache[filename.replace(/^\.\//, "")]; }, template.helper = function(name, helper) { helpers[name] = helper; - }, "function" == typeof define ? define(function() { + }, define(function() { return template; - }) : "undefined" != typeof exports ? module.exports = template : this.template = template; + }); }(); \ No newline at end of file diff --git a/test/test-all/amd/package.json b/test/test-all/amd/package.json index c6a0bd1..a3dc82a 100644 --- a/test/test-all/amd/package.json +++ b/test/test-all/amd/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.0", "dependencies": { - "tmodjs": "1.0.0" + "tmodjs": "1.0.1-rc5" }, "tmodjs-config": { "output": "./build", diff --git a/test/test-all/global.html b/test/test-all/global.html new file mode 100644 index 0000000..6d571e1 --- /dev/null +++ b/test/test-all/global.html @@ -0,0 +1,49 @@ + + + + +TemplateJS - 调用模板演示 + + + + +
    loading..
    + + + + + + + + diff --git a/test/test-all/global/build/template.js b/test/test-all/global/build/template.js new file mode 100644 index 0000000..cb8928d --- /dev/null +++ b/test/test-all/global/build/template.js @@ -0,0 +1,115 @@ +/*TMODJS:{"version":"1.0.1"}*/ +!function() { + function template(filename, content) { + return (/string|function/.test(typeof content) ? compile : renderFile)(filename, content); + } + function toString(value, type) { + return "string" != typeof value && (type = typeof value, "number" === type ? value += "" : value = "function" === type ? toString(value.call(value)) : ""), + value; + } + function escapeFn(s) { + return escapeMap[s]; + } + function escapeHTML(content) { + return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, escapeFn); + } + function each(data, callback) { + if (isArray(data)) for (var i = 0, len = data.length; len > i; i++) callback.call(data, data[i], i, data); else for (i in data) callback.call(data, data[i], i); + } + function resolve(from, to) { + var DOUBLE_DOT_RE = /(\/)[^/]+\1\.\.\1/, dirname = ("./" + from).replace(/[^/]+$/, ""), filename = dirname + to; + for (filename = filename.replace(/\/\.\//g, "/"); filename.match(DOUBLE_DOT_RE); ) filename = filename.replace(DOUBLE_DOT_RE, "/"); + return filename; + } + function renderFile(filename, data) { + var fn = template.get(filename) || showDebugInfo({ + filename: filename, + name: "Render Error", + message: "Template not found" + }); + return data ? fn(data) : fn; + } + function compile(filename, fn) { + if ("string" == typeof fn) { + var string = fn; + fn = function() { + return new String(string); + }; + } + var render = cache[filename] = function(data) { + try { + return new fn(data, filename) + ""; + } catch (e) { + return showDebugInfo(e)(); + } + }; + return render.prototype = fn.prototype = utils, render.toString = function() { + return fn + ""; + }, render; + } + function showDebugInfo(e) { + var type = "{Template Error}", message = e.stack || ""; + if (message) message = message.split("\n").slice(0, 2).join("\n"); else for (var name in e) message += "<" + name + ">\n" + e[name] + "\n\n"; + return function() { + return "object" == typeof console && console.error(type + "\n\n" + message), type; + }; + } + var cache = template.cache = {}, String = this.String, escapeMap = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "&": "&" + }, isArray = Array.isArray || function(obj) { + return "[object Array]" === {}.toString.call(obj); + }, utils = template.utils = { + $helpers: {}, + $include: function(filename, data, from) { + return filename = resolve(from, filename), renderFile(filename, data); + }, + $string: toString, + $escape: escapeHTML, + $each: each + }, helpers = template.helpers = utils.$helpers; + template.get = function(filename) { + return cache[filename.replace(/^\.\//, "")]; + }, template.helper = function(name, helper) { + helpers[name] = helper; + }, this.template = template, /*v:1*/ + template("copyright", "(c) 2013"), /*v:1*/ + template("index", function($data, $filename) { + "use strict"; + var $utils = this, include = ($utils.$helpers, function(filename, data) { + data = data || $data; + var text = $utils.$include(filename, data, $filename); + return $out += text; + }), $escape = $utils.$escape, title = $data.title, $each = $utils.$each, list = $data.list, $out = ($data.$value, + $data.$index, ""); + return include("./public/header"), $out += '

    ', $out += $escape(title), + $out += "

    ", include("./public/footer"), new String($out); + }), /*v:1*/ + template("public/footer", function($data, $filename) { + "use strict"; + var $utils = this, time = ($utils.$helpers, $data.time), $escape = $utils.$escape, include = function(filename, data) { + data = data || $data; + var text = $utils.$include(filename, data, $filename); + return $out += text; + }, $out = ""; + return $out += '", new String($out); + }), /*v:1*/ + template("public/header", function($data, $filename) { + "use strict"; + var $utils = this, include = ($utils.$helpers, function(filename, data) { + data = data || $data; + var text = $utils.$include(filename, data, $filename); + return $out += text; + }), $out = ""; + return $out += ' ', + new String($out); + }), /*v:1*/ + template("public/logo", ""); +}(); \ No newline at end of file diff --git a/test/test-all/global/copyright.html b/test/test-all/global/copyright.html new file mode 100644 index 0000000..4c65dfa --- /dev/null +++ b/test/test-all/global/copyright.html @@ -0,0 +1 @@ +(c) 2013 \ No newline at end of file diff --git a/test/test-all/global/index.html b/test/test-all/global/index.html new file mode 100644 index 0000000..fc67525 --- /dev/null +++ b/test/test-all/global/index.html @@ -0,0 +1,12 @@ +{{include './public/header'}} + +
    +

    {{title}}

    + +
    + +{{include './public/footer'}} \ No newline at end of file diff --git a/test/test-all/global/package.json b/test/test-all/global/package.json new file mode 100644 index 0000000..fd2b961 --- /dev/null +++ b/test/test-all/global/package.json @@ -0,0 +1,20 @@ +{ + "name": "template", + "version": "1.0.1", + "dependencies": { + "tmodjs": "1.0.1-rc5" + }, + "tmodjs-config": { + "output": "./build", + "charset": "utf-8", + "syntax": "simple", + "helpers": null, + "escape": true, + "compress": true, + "type": "global", + "runtime": "template.js", + "combo": true, + "minify": false, + "cache": true + } +} \ No newline at end of file diff --git a/test/test-all/global/public/footer.html b/test/test-all/global/public/footer.html new file mode 100644 index 0000000..956c23e --- /dev/null +++ b/test/test-all/global/public/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/test/test-all/global/public/header.html b/test/test-all/global/public/header.html new file mode 100644 index 0000000..267582e --- /dev/null +++ b/test/test-all/global/public/header.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/test-all/global/public/logo.html b/test/test-all/global/public/logo.html new file mode 100644 index 0000000..b02f7cb --- /dev/null +++ b/test/test-all/global/public/logo.html @@ -0,0 +1,7 @@ + +

    + + 腾讯网 + +

    + \ No newline at end of file diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index a003a64..cc60a24 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ /*TMODJS:{"version":"1.0.1"}*/ -!function(a){function b(a,b){return(/string|function/.test(typeof b)?i:h)(a,b)}function c(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?c(a.call(a)):""),a}function d(a){return l[a]}function e(a){return c(a).replace(/&(?![\w#]+;)|[<>"']/g,d)}function f(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function g(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function h(a,c){var d=b.get(a)||j({filename:a,name:"Render Error",message:"Template not found"});return c?d(c):d}function i(b,c){if("string"==typeof c){var d=c;c=function(){return new a(d)}}var e=k[b]=function(a){try{return new c(a,b)+""}catch(d){return j(d)()}};return e.prototype=c.prototype=n,e.toString=function(){return c+""},e}function j(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var k=b.cache={},a=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=b.utils={$helpers:{},$include:function(a,b,c){return a=g(c,a),h(a,b)},$string:c,$escape:e,$each:f},o=b.helpers=n.$helpers;b.get=function(a){return k[a.replace(/^\.\//,"")]},b.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return b}):"undefined"!=typeof exports?module.exports=b:this.template=b,/*v:6*/ -b("copyright","(c) 2013"),/*v:15*/ -b("index",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var f=d.$include(a,e,c);return j+=f}),f=d.$escape,g=b.title,h=d.$each,i=b.list,j=(b.$value,b.$index,"");return e("./public/header"),j+='

    ',j+=f(g),j+="

    ",e("./public/footer"),new a(j)}),/*v:6*/ -b("public/footer",function(b,c){"use strict";var d=this,e=(d.$helpers,b.time),f=d.$escape,g=function(a,e){e=e||b;var f=d.$include(a,e,c);return h+=f},h="";return h+='",new a(h)}),/*v:7*/ -b("public/header",function(b,c){"use strict";var d=this,e=(d.$helpers,function(a,e){e=e||b;var g=d.$include(a,e,c);return f+=g}),f="";return f+=' ',new a(f)}),/*v:6*/ -b("public/logo",'

    腾讯网

    ')}(); \ No newline at end of file +!function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:13*/ +a("copyright","(c) 2013"),/*v:24*/ +a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)}),/*v:13*/ +a("public/footer",function(a,b){"use strict";var c=this,d=(c.$helpers,a.time),e=c.$escape,f=function(d,e){e=e||a;var f=c.$include(d,e,b);return g+=f},g="";return g+='",new k(g)}),/*v:14*/ +a("public/header",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,f){f=f||a;var g=c.$include(d,f,b);return e+=g}),e="";return e+=' ',new k(e)}),/*v:13*/ +a("public/logo","")}(); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index ac5be46..46feb27 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.1", "dependencies": { - "tmodjs": "1.0.1-rc4" + "tmodjs": "1.0.1-rc5" }, "tmodjs-config": { "output": "./build", From 3339998d97956373bafbec4a906d01d36ae82b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= <1987.tangbin@gmail.com> Date: Thu, 7 Aug 2014 13:34:40 +0800 Subject: [PATCH 69/87] fix bug --- package.json | 2 +- src/AOTcompile.js | 3 +-- src/runtime.js | 2 +- src/stdout.js | 2 +- src/tmod.js | 4 ++-- test/tpl/build/template.js | 10 +++++----- test/tpl/package.json | 2 +- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 7b4a2db..770c7bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc6", + "version": "1.0.1-rc7", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/AOTcompile.js b/src/AOTcompile.js index fd44ea3..4708461 100644 --- a/src/AOTcompile.js +++ b/src/AOTcompile.js @@ -151,8 +151,7 @@ module.exports = function (template) { var DIRNAME_RE = /[^/]+$/; var ANONYMOUS_RE = /^function\s+anonymous/; var EXTNAME_RE = /\.(html|htm|tpl)$/i; - var INCLUDE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*\*\/|\/(?:\\\/|[^/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*include|(?:^|[^$])\binclude\s*\(\s*(["'])(.+?)\1/g; //" - + var INCLUDE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*include|(?:^|[^$])\binclude\s*\(\s*(["'])(.+?)\1/g; //" // 编译模板 diff --git a/src/runtime.js b/src/runtime.js index 04ab3e2..866d2e7 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -228,7 +228,7 @@ var getNamespaceCode = function (type) { }; -var VAR_RE = /['"]<\:(.*)\:>['"]/g; +var VAR_RE = /['"]<\:(.*?)\:>['"]/g; module.exports = function (data) { diff --git a/src/stdout.js b/src/stdout.js index a1ba316..f7b10ad 100644 --- a/src/stdout.js +++ b/src/stdout.js @@ -30,7 +30,7 @@ styles['i'] = styles['italic']; styles['u'] = styles['underline']; module.exports = function (message) { - message = message.replace(/\[([^\]]*)\]/igm, function ($1, $2) { + message = message.replace(/\[([^\]]*?)\]/igm, function ($1, $2) { return $2.indexOf('/') === 0 ? styles[$2.slice(1)][1] : styles[$2][0]; diff --git a/src/tmod.js b/src/tmod.js index 1d68cf9..7b035cd 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -629,7 +629,7 @@ Tmod.prototype = { // 获取元数据 _getMetadata: function (js) { - var data = js.match(/\/\*TMODJS\:(.*)\*\//); + var data = js.match(/\/\*TMODJS\:(.*?)\*\//); if (data) { return JSON.parse(data[1]); } @@ -654,7 +654,7 @@ Tmod.prototype = { _setMetadata: function (js, data) { data = JSON.stringify(data || {}); js = '/*TMODJS:' + data + '*/\n' + js - .replace(/\/\*TMODJS\:(.*)\*\//, ''); + .replace(/\/\*TMODJS\:(.*?)\*\//, ''); return js; }, diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index cc60a24..9266701 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,7 @@ /*TMODJS:{"version":"1.0.1"}*/ -!function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:13*/ -a("copyright","(c) 2013"),/*v:24*/ -a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)}),/*v:13*/ -a("public/footer",function(a,b){"use strict";var c=this,d=(c.$helpers,a.time),e=c.$escape,f=function(d,e){e=e||a;var f=c.$include(d,e,b);return g+=f},g="";return g+='",new k(g)}),/*v:14*/ -a("public/header",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,f){f=f||a;var g=c.$include(d,f,b);return e+=g}),e="";return e+=' ',new k(e)}),/*v:13*/ +!function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:15*/ +a("copyright","(c) 2013"),/*v:26*/ +a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)}),/*v:15*/ +a("public/footer",function(a,b){"use strict";var c=this,d=(c.$helpers,a.time),e=c.$escape,f=function(d,e){e=e||a;var f=c.$include(d,e,b);return g+=f},g="";return g+='",new k(g)}),/*v:16*/ +a("public/header",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,f){f=f||a;var g=c.$include(d,f,b);return e+=g}),e="";return e+=' ',new k(e)}),/*v:15*/ a("public/logo","")}(); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index 46feb27..1de2f19 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.1", "dependencies": { - "tmodjs": "1.0.1-rc5" + "tmodjs": "1.0.1-rc7" }, "tmodjs-config": { "output": "./build", From 1c34312ebe1b3a063f1dc8930e3323db0c2c8a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= Date: Thu, 14 Aug 2014 14:01:10 +0800 Subject: [PATCH 70/87] fix bug #104 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 给引入后缀名的模板给予报错提示 --- README.md | 1 + package.json | 2 +- src/AOTcompile.js | 1 - src/tmod.js | 2 +- test/tpl/build/template.js | 5 +---- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 811cd0d..225e998 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,7 @@ npm install gulp-tmod --save-dev ### v1.0.1 * 解决新版本设置``"minify":true``输出后,输出的脚本中文没有被编码的问题 +* 给引入后缀名的模板给予报错提示 ### v1.0.0 diff --git a/package.json b/package.json index 770c7bb..ed59a79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc7", + "version": "1.0.1-rc8", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/AOTcompile.js b/src/AOTcompile.js index 4708461..b890770 100644 --- a/src/AOTcompile.js +++ b/src/AOTcompile.js @@ -190,7 +190,6 @@ module.exports = function (template) { var error = { name: 'Syntax Error', - type: 1, line: line, source: source, message: 'Template must be a relative path, and can not have a suffix.' diff --git a/src/tmod.js b/src/tmod.js index 7b035cd..1bb83e7 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -128,7 +128,6 @@ var Tmod = function (base, options) { if (error) { this.log(' [inverse][red]{{Syntax Error}}[/red][/inverse]\n\n'); - } else { this.log(this.options.debug ? ' [grey][/grey]' : ''); @@ -1064,6 +1063,7 @@ Tmod.prototype = { if (compileError || writeError) { + this.emit('debug', compileError || writeError); return null; } else { return compileInfo; diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 9266701..60b3273 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,7 +1,4 @@ /*TMODJS:{"version":"1.0.1"}*/ !function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:15*/ a("copyright","(c) 2013"),/*v:26*/ -a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)}),/*v:15*/ -a("public/footer",function(a,b){"use strict";var c=this,d=(c.$helpers,a.time),e=c.$escape,f=function(d,e){e=e||a;var f=c.$include(d,e,b);return g+=f},g="";return g+='",new k(g)}),/*v:16*/ -a("public/header",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,f){f=f||a;var g=c.$include(d,f,b);return e+=g}),e="";return e+=' ',new k(e)}),/*v:15*/ -a("public/logo","")}(); \ No newline at end of file +a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)})}(); \ No newline at end of file From 2701276d4f5320d99b35c9dcf6fe0cde34cb4de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E6=96=8C?= Date: Thu, 4 Sep 2014 17:40:06 +0800 Subject: [PATCH 71/87] bug fix #52 --- package.json | 2 +- src/AOTcompile.js | 2 +- test/tpl/build/template.js | 9 ++++++--- test/tpl/package.json | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ed59a79..3aa9238 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc8", + "version": "1.0.1-rc9", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", diff --git a/src/AOTcompile.js b/src/AOTcompile.js index b890770..462399a 100644 --- a/src/AOTcompile.js +++ b/src/AOTcompile.js @@ -252,7 +252,7 @@ module.exports = function (template) { var compressHTML = function (code) { code = code .replace(/\s+/g, ' ') - .replace(//g, ''); + .replace(//g, ''); return code; }; diff --git a/test/tpl/build/template.js b/test/tpl/build/template.js index 60b3273..2263cd3 100644 --- a/test/tpl/build/template.js +++ b/test/tpl/build/template.js @@ -1,4 +1,7 @@ /*TMODJS:{"version":"1.0.1"}*/ -!function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:15*/ -a("copyright","(c) 2013"),/*v:26*/ -a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)})}(); \ No newline at end of file +!function(){function a(a,b){return(/string|function/.test(typeof b)?h:g)(a,b)}function b(a,c){return"string"!=typeof a&&(c=typeof a,"number"===c?a+="":a="function"===c?b(a.call(a)):""),a}function c(a){return l[a]}function d(a){return b(a).replace(/&(?![\w#]+;)|[<>"']/g,c)}function e(a,b){if(m(a))for(var c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)}function f(a,b){var c=/(\/)[^/]+\1\.\.\1/,d=("./"+a).replace(/[^/]+$/,""),e=d+b;for(e=e.replace(/\/\.\//g,"/");e.match(c);)e=e.replace(c,"/");return e}function g(b,c){var d=a.get(b)||i({filename:b,name:"Render Error",message:"Template not found"});return c?d(c):d}function h(a,b){if("string"==typeof b){var c=b;b=function(){return new k(c)}}var d=j[a]=function(c){try{return new b(c,a)+""}catch(d){return i(d)()}};return d.prototype=b.prototype=n,d.toString=function(){return b+""},d}function i(a){var b="{Template Error}",c=a.stack||"";if(c)c=c.split("\n").slice(0,2).join("\n");else for(var d in a)c+="<"+d+">\n"+a[d]+"\n\n";return function(){return"object"==typeof console&&console.error(b+"\n\n"+c),b}}var j=a.cache={},k=this.String,l={"<":"<",">":">",'"':""","'":"'","&":"&"},m=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},n=a.utils={$helpers:{},$include:function(a,b,c){return a=f(c,a),g(a,b)},$string:b,$escape:d,$each:e},o=a.helpers=n.$helpers;a.get=function(a){return j[a.replace(/^\.\//,"")]},a.helper=function(a,b){o[a]=b},"function"==typeof define?define(function(){return a}):"undefined"!=typeof exports?module.exports=a:this.template=a,/*v:25*/ +a("copyright","(c) 2013"),/*v:38*/ +a("index",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,e){e=e||a;var f=c.$include(d,e,b);return i+=f}),e=c.$escape,f=a.title,g=c.$each,h=a.list,i=(a.$value,a.$index,"");return d("./public/header"),i+='

    ',i+=e(f),i+="

    ",d("./public/footer"),new k(i)}),/*v:26*/ +a("public/footer",function(a,b){"use strict";var c=this,d=(c.$helpers,a.time),e=c.$escape,f=function(d,e){e=e||a;var f=c.$include(d,e,b);return g+=f},g="";return g+='",new k(g)}),/*v:26*/ +a("public/header",function(a,b){"use strict";var c=this,d=(c.$helpers,function(d,f){f=f||a;var g=c.$include(d,f,b);return e+=g}),e="";return e+=' ',new k(e)}),/*v:27*/ +a("public/logo",'

    \u817e\u8baf\u7f51

    ')}(); \ No newline at end of file diff --git a/test/tpl/package.json b/test/tpl/package.json index 1de2f19..67216ae 100644 --- a/test/tpl/package.json +++ b/test/tpl/package.json @@ -2,7 +2,7 @@ "name": "template", "version": "1.0.1", "dependencies": { - "tmodjs": "1.0.1-rc7" + "tmodjs": "1.0.1-rc9" }, "tmodjs-config": { "output": "./build", @@ -15,6 +15,6 @@ "runtime": "template.js", "combo": true, "minify": true, - "cache": true + "cache": false } } \ No newline at end of file From 33a8aec071ebd21c6778af7b0327ec19a845ed6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9C=86=E7=A9=BA?= Date: Tue, 2 Dec 2014 20:41:10 +0800 Subject: [PATCH 72/87] =?UTF-8?q?=E5=B0=86=E5=8E=9F=E5=85=88=E6=94=BE?= =?UTF-8?q?=E5=9C=A8prototype=E4=B8=8A=E7=9A=84=5Fcache=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=94=B9=E5=88=B0this=E4=B8=8A=E9=9D=A2=EF=BC=8C=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E5=A4=9A=E4=B8=AA=E5=AE=9E=E4=BE=8B=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=B7=B7=E5=9C=A8=E4=B8=80=E8=B5=B7=EF=BC=8C=E4=BE=8B?= =?UTF-8?q?=E5=A6=82=E8=A6=81build=E4=B8=A4=E4=B8=AA=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E8=AF=9D=EF=BC=8C=E7=AC=AC=E4=BA=8C=E4=B8=AA=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=9A=84=E7=BC=96=E8=AF=91=E7=BB=93=E6=9E=9C=E4=BC=9A?= =?UTF-8?q?=E5=8C=85=E5=90=AB=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tmod.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tmod.js b/src/tmod.js index 1bb83e7..21dc6ca 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -57,6 +57,10 @@ var Tmod = function (base, options) { this.runtime = path.resolve(this.output, options.runtime); + // 编译结果存储 + this._cache = {}; + + // 清理模板项目临时文件 this._clear(); @@ -1077,9 +1081,6 @@ Tmod.prototype = { }, - _cache: {}, - - // 获取缓存 _getCache: function (id) { if (typeof id === 'undefined') { From 2ece33403dc89bae62bfeeb36c90e610912afa6b Mon Sep 17 00:00:00 2001 From: daddybh Date: Thu, 29 Jan 2015 17:08:33 +0800 Subject: [PATCH 73/87] fixed issue #78 --- src/tmod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tmod.js b/src/tmod.js index 21dc6ca..5882cf4 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -649,7 +649,7 @@ Tmod.prototype = { newText = '/*v:' + data.version + '*/'; } - return js.replace(/^\/\*TMODJS\:[\w\W]*\*\//, newText); + return js.replace(/^\/\*TMODJS\:(?:.*)\*\//, newText); }, From 75efa3ef66a77bb34066c2d4862f5ed1f4153634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= <1987.tangbin@gmail.com> Date: Thu, 29 Jan 2015 19:18:51 +0800 Subject: [PATCH 74/87] up version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3aa9238..ca2b2f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.1-rc9", + "version": "1.0.2", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", From 63398c86b892b6e13cc1707f52579fe59e200096 Mon Sep 17 00:00:00 2001 From: Yuliang-Lee Date: Tue, 1 Sep 2015 15:37:55 +0800 Subject: [PATCH 75/87] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAMD=E6=89=93=E5=8C=85?= =?UTF-8?q?=E7=A9=BA=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=BE=9D=E8=B5=96=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题如https://github.com/aui/tmodjs/issues/77 描述,在模板中没有依赖其他模板的情况下,去掉打包后的define里面多一个空字符串依赖项的问题。我在Dojo中使用AMD打包形式的模板的时候也遇到了这个错误。 --- src/AOTcompile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AOTcompile.js b/src/AOTcompile.js index 462399a..c597976 100644 --- a/src/AOTcompile.js +++ b/src/AOTcompile.js @@ -98,7 +98,7 @@ module.exports = function (template) { code = "define(" - + "['" + getRuntime() + "','" + requires.join("','") + "']," + + (requires.length > 0 )? "['" + getRuntime() + "','" + requires.join("','") + "']," : "['" + getRuntime() + "']," + "function(template){" + "return template('" + filename + "', " + code + ");" + "});"; From 864cfd39babc1853304fd59e4bb2384107ea2cad Mon Sep 17 00:00:00 2001 From: Yuliang-Lee Date: Tue, 1 Sep 2015 16:06:32 +0800 Subject: [PATCH 76/87] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAMD=E6=89=93=E5=8C=85?= =?UTF-8?q?=E7=A9=BA=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=BE=9D=E8=B5=96=E9=97=AE?= =?UTF-8?q?=E9=A2=982?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第一次提交有点小bug --- src/AOTcompile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AOTcompile.js b/src/AOTcompile.js index c597976..27a6877 100644 --- a/src/AOTcompile.js +++ b/src/AOTcompile.js @@ -95,10 +95,10 @@ module.exports = function (template) { // RequireJS 模块格式 case 'amd': - + var dependencies = (requires.length > 0 )? "['" + getRuntime() + "','" + requires.join("','") + "']," : "['" + getRuntime() + "'],"; code = "define(" - + (requires.length > 0 )? "['" + getRuntime() + "','" + requires.join("','") + "']," : "['" + getRuntime() + "']," + + dependencies + "function(template){" + "return template('" + filename + "', " + code + ");" + "});"; From 58e0422f8577469d0b7f949a1e01af3c93312d82 Mon Sep 17 00:00:00 2001 From: zk <1345744500@qq.com> Date: Fri, 9 Oct 2015 15:40:15 +0800 Subject: [PATCH 77/87] Update tmod.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重新发起pr --- src/tmod.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tmod.js b/src/tmod.js index 5882cf4..936c936 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -37,7 +37,7 @@ var log = function (message) { console.log(message); }; - + var Tmod = function (base, options) { @@ -227,6 +227,13 @@ Tmod.prototype = { } + //有些项目的package.json里只有devDependencies而没有dependencies + //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined的错误 + //这里添加容错逻辑 + + if (!json.dependencies) { + json.dependencies = json.devDependencies; + } var targetVersion = json.dependencies.tmodjs.replace(/^~/, ''); From b2346910e3340dd166c2eb236df24e6ed6f42b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= <1987.tangbin@gmail.com> Date: Fri, 9 Oct 2015 16:04:55 +0800 Subject: [PATCH 78/87] v1.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca2b2f6..a671c65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.2", + "version": "1.0.3", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", From 1e6c5617b785b48728ccaa2aee1714ed146e5f74 Mon Sep 17 00:00:00 2001 From: liulong Date: Mon, 21 Dec 2015 21:47:59 +0800 Subject: [PATCH 79/87] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AE=B9=E9=94=99?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过grunt-tmod或者其他方式安装tmodjs,并不会在package.json中加入devDependencies或者dependencies中添加tmodjs的字段,这里完善容错逻辑,并添加提示信息 --- src/tmod.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tmod.js b/src/tmod.js index 936c936..a7c8488 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -227,12 +227,16 @@ Tmod.prototype = { } - //有些项目的package.json里只有devDependencies而没有dependencies - //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined的错误 + //有些项目的package.json里只有devDependencies而没有dependencies或者dependencies为空 + //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined + // 或者 Cannot read property 'replace' of undefined 的错误 //这里添加容错逻辑 - - if (!json.dependencies) { + if (!json.dependencies || Object.keys(json.dependencies).length === 0) { json.dependencies = json.devDependencies; + + } + if(!json.dependencies.hasOwnProperty('tmodjs')) { + return this.log("[red]Cannot find tmodjs in package.json's dependencies or devDependencies, please check[/red]\n"); } var targetVersion = json.dependencies.tmodjs.replace(/^~/, ''); From 60d8cd36c82f050232bb5ef5d75d760c0cbe0788 Mon Sep 17 00:00:00 2001 From: liulong Date: Mon, 21 Dec 2015 22:03:52 +0800 Subject: [PATCH 80/87] =?UTF-8?q?Revert=20"=E5=AE=8C=E5=96=84=E5=AE=B9?= =?UTF-8?q?=E9=94=99=E9=80=BB=E8=BE=91"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1e6c5617b785b48728ccaa2aee1714ed146e5f74. --- src/tmod.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tmod.js b/src/tmod.js index a7c8488..936c936 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -227,16 +227,12 @@ Tmod.prototype = { } - //有些项目的package.json里只有devDependencies而没有dependencies或者dependencies为空 - //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined - // 或者 Cannot read property 'replace' of undefined 的错误 + //有些项目的package.json里只有devDependencies而没有dependencies + //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined的错误 //这里添加容错逻辑 - if (!json.dependencies || Object.keys(json.dependencies).length === 0) { + + if (!json.dependencies) { json.dependencies = json.devDependencies; - - } - if(!json.dependencies.hasOwnProperty('tmodjs')) { - return this.log("[red]Cannot find tmodjs in package.json's dependencies or devDependencies, please check[/red]\n"); } var targetVersion = json.dependencies.tmodjs.replace(/^~/, ''); From 88ef76f358832286eb02998c31269d731b5dc7fc Mon Sep 17 00:00:00 2001 From: calledt Date: Thu, 4 Feb 2016 16:46:09 +0800 Subject: [PATCH 81/87] Tweaks options --- README.md | 8 ++++- package.json | 4 +-- src/defaults.js | 7 ++-- src/tmod.js | 96 ++++++++++++++++++++++++++----------------------- 4 files changed, 66 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 225e998..a18dbec 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ TmodJS 的项目配置文件保存在模板目录的 package.json 文件中: 字段 | 类型 | 默认值| 说明 ------------ | ------------- | ------------ | ------------ -output | String | ``"./build"`` | 编译输出目录设置 +output | String | ``"./build"`` | 编译输出目录设置。如果设置为 false 则不输出 charset | String | ``"utf-8"`` | 模板使用的编码(暂时只支持 utf-8) syntax | String | ``"simple"`` | 定义模板采用哪种语法。可选:``simple``、``native`` helpers | String | ``null`` | 自定义辅助方法路径 @@ -170,6 +170,7 @@ alias | String | ``null`` | 设置模块依赖的运行时路径(仅针对于 combo | Boolean | ``true`` | 是否合并模板(仅针对于 default 类型的模块) minify | Boolean | ``true`` | 是否输出为压缩的格式 cache | Boolean | ``true`` | 是否开启编译缓存 +verbose | Boolean | ``true`` | 是否打印日志 ## grunt @@ -243,6 +244,11 @@ npm install gulp-tmod --save-dev ## 更新日志 +### v1.0.4 + +* 设置``"output":false``则不进行输出,方便Gulp插件利用 +* 新增``"verbose": true``选项,选择是否显示日志 + ### v1.0.1 * 解决新版本设置``"minify":true``输出后,输出的脚本中文没有被编码的问题 diff --git a/package.json b/package.json index a671c65..fdef2d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tmodjs", - "version": "1.0.3", + "version": "1.0.4", "readmeFilename": "README.md", "description": "Template Compiler", "homepage": "https://github.com/aui/tmodjs", @@ -34,4 +34,4 @@ "semver": "2.3.0" }, "license": "BSD" -} \ No newline at end of file +} diff --git a/src/defaults.js b/src/defaults.js index 34ee362..71ae5c6 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -46,6 +46,9 @@ module.exports = { minify: true, // 是否开启编译缓存 - cache: true + cache: true, -}; \ No newline at end of file + // 是否输出日志 + verbose: true + +}; diff --git a/src/tmod.js b/src/tmod.js index 936c936..62bd4c7 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -37,7 +37,7 @@ var log = function (message) { console.log(message); }; - + var Tmod = function (base, options) { @@ -48,13 +48,16 @@ var Tmod = function (base, options) { // 项目配置选项 this.options = options = this.getConfig(options); - - // 输出路径 - this.output = path.resolve(this.base, options.output); + if (options.output !== false) { + // 输出路径 + this.output = path.resolve(this.base, options.output); - // 运行时输出路径 - this.runtime = path.resolve(this.output, options.runtime); + // 运行时输出路径 + this.runtime = path.resolve(this.output, options.runtime); + } else { + this.output = defaults.output + } // 编译结果存储 @@ -146,7 +149,7 @@ var Tmod = function (base, options) { // 调试事件(异步事件) this.on('debug', function (error) { - + this.log('[red]Debug info:[/red]\n'); if (error.line && error.source) { @@ -230,7 +233,7 @@ Tmod.prototype = { //有些项目的package.json里只有devDependencies而没有dependencies //那么下面的replace那行代码就会出现can't read property 'tmodjs' of undefined的错误 //这里添加容错逻辑 - + if (!json.dependencies) { json.dependencies = json.devDependencies; } @@ -238,7 +241,7 @@ Tmod.prototype = { var targetVersion = json.dependencies.tmodjs.replace(/^~/, ''); - + try { // 比较模板项目版本号 if (semver.lt(version, targetVersion)) { @@ -326,7 +329,7 @@ Tmod.prototype = { if (file) { - + var fileList = typeof file === 'string' ? [file] : file; fileList = fileList.map(function (file) { @@ -396,14 +399,14 @@ Tmod.prototype = { * @return {Boolean} */ filter: function (file) { - + if (fs.existsSync(file)) { var stat = fs.statSync(file); if (stat.isDirectory()) { - + var dirs = file.split(path.sep); var basedir = dirs[dirs.length - 1]; - + return this.filterBasename(basedir) ? true : false; } else { @@ -504,7 +507,9 @@ Tmod.prototype = { * @param {String} 消息 */ log: function (message) { - stdout(message); + if (this.options.verbose) { + stdout(message); + } }, @@ -516,7 +521,7 @@ Tmod.prototype = { // 忽略大小写 options.type = options.type.toLowerCase(); - + // 模板合并规则 // 兼容 0.0.3-rc3 之前的配置 @@ -708,20 +713,22 @@ Tmod.prototype = { }); - runtimeCode = this._setMetadata(runtimeCode, metadata); + this.runtimeCode = runtimeCode = this._setMetadata(runtimeCode, metadata); - try { - this._fsMkdir(path.dirname(this.runtime)); - fs.writeFileSync(this.runtime, runtimeCode, this.options.charset); - } catch (e) { - error = e; - } + if (this.options.output !== false) { + try { + this._fsMkdir(path.dirname(this.runtime)); + fs.writeFileSync(this.runtime, runtimeCode, this.options.charset); + } catch (e) { + error = e; + } - if (this.options.debug || !this.options.minify) { - this._beautify(this.runtime); - } else { - this._minify(this.runtime); + if (this.options.debug || !this.options.minify) { + this._beautify(this.runtime); + } else { + this._minify(this.runtime); + } } callback.call(this, error, runtimeCode); @@ -871,7 +878,7 @@ Tmod.prototype = { var modObject = {}; var metadata = {}; var count = 0; - + var isDebug = this.options.debug; var isCacheDir = this.options.combo; @@ -974,20 +981,21 @@ Tmod.prototype = { md5: newMd5 }); - - try { - this._fsMkdir(path.dirname(target));////// - fs.writeFileSync(target, mod, this.options.charset); - } catch (e) { - writeError = e; - } + if (this.options.output !== false) { + try { + this._fsMkdir(path.dirname(target));////// + fs.writeFileSync(target, mod, this.options.charset); + } catch (e) { + writeError = e; + } - if (!isCacheDir && !writeError) { - if (isDebug || !this.options.minify) { - this._beautify(target); - } else { - this._minify(target); + if (!isCacheDir && !writeError) { + if (isDebug || !this.options.minify) { + this._beautify(target); + } else { + this._minify(target); + } } } @@ -1069,9 +1077,9 @@ Tmod.prototype = { this._setCache(file, mod); } - + this.emit('compile', compileError || writeError, compileInfo); - + if (compileError || writeError) { this.emit('debug', compileError || writeError); @@ -1127,7 +1135,7 @@ Tmod.prototype = { // 不再推荐使用动态加载自定义语法 // 为了兼容 < v1.0 的功能 default: - + var syntaxFile = path.resolve(this.base, options.syntax); if (fs.existsSync(syntaxFile)) { @@ -1146,7 +1154,7 @@ Tmod.prototype = { this.log('[red]Not found: ' + syntaxFile + '[/red]'); process.exit(1); - } + } } @@ -1173,7 +1181,7 @@ Tmod.prototype = { this.template = AOTcompile(template); - + }, From cd0321a48796a6bd84204071acb6949bbc0c8bcc Mon Sep 17 00:00:00 2001 From: calledt Date: Thu, 4 Feb 2016 17:09:41 +0800 Subject: [PATCH 82/87] Tweaks options --- src/tmod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tmod.js b/src/tmod.js index 62bd4c7..53f8edb 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -713,7 +713,7 @@ Tmod.prototype = { }); - this.runtimeCode = runtimeCode = this._setMetadata(runtimeCode, metadata); + runtimeCode = this._setMetadata(runtimeCode, metadata); if (this.options.output !== false) { try { From 40597fd18f9a8fd5e28b5681490316f7ff3f2573 Mon Sep 17 00:00:00 2001 From: calledT Date: Fri, 5 Feb 2016 10:41:18 +0800 Subject: [PATCH 83/87] not alway beautify or minify output --- src/tmod.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tmod.js b/src/tmod.js index 53f8edb..cfc0c67 100644 --- a/src/tmod.js +++ b/src/tmod.js @@ -724,9 +724,11 @@ Tmod.prototype = { } - if (this.options.debug || !this.options.minify) { + if (this.options.debug) { this._beautify(this.runtime); - } else { + } + + if (!this.options.debug && this.options.minify) { this._minify(this.runtime); } } @@ -991,9 +993,11 @@ Tmod.prototype = { if (!isCacheDir && !writeError) { - if (isDebug || !this.options.minify) { + if (isDebug) { this._beautify(target); - } else { + } + + if (!isDebug && this.options.minify) { this._minify(target); } } From 161fc755f99a731cbac022a5985c78be89e7645b Mon Sep 17 00:00:00 2001 From: tofishes Date: Sat, 16 Jul 2016 16:26:52 +0800 Subject: [PATCH 84/87] Update uglify2.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修订path.dirname调用兼容node v6版本 --- src/uglify2.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uglify2.js b/src/uglify2.js index bfb17a3..5ae63a6 100644 --- a/src/uglify2.js +++ b/src/uglify2.js @@ -36,7 +36,7 @@ module.exports = function (files, dest, options) { // The src file name must be relative to the source map for things to work var basename = path.basename(file); var fileDir = path.dirname(file); - var sourceMapDir = path.dirname(options.generatedSourceMapName); + var sourceMapDir = path.dirname(options.generatedSourceMapName || ''); var relativePath = path.relative(sourceMapDir, fileDir); var pathPrefix = relativePath ? (relativePath + path.sep) : ''; @@ -179,4 +179,4 @@ var getOutputOptions = function (options, dest) { } return outputOptions; -}; \ No newline at end of file +}; From 2f454ff27c6f336fc3d38377220e567d12edde5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= Date: Wed, 12 Oct 2016 11:27:54 +0800 Subject: [PATCH 85/87] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 225e998..bf6ade2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TmodJS +> 项目停止维护 + TmodJS(原名 atc)是一个简单易用的前端模板预编译工具。它通过预编译技术让前端模板突破浏览器限制,实现后端模板一样的同步“文件”加载能力。它采用目录来组织维护前端模板,从而让前端模板实现工程化管理,最终保证前端模板在复杂单页 web 应用下的可维护性。同时预编译输出的代码经过多层优化,能够在最大程度节省客户端资源消耗。 一、**按文件与目录组织模板** From 348c98ebfa61d38179da372cde14be866a2f6238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= Date: Sun, 16 Apr 2017 16:13:06 +0800 Subject: [PATCH 86/87] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d39e11..c7b4cb9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# TmodJS +# TmodJS -> 项目停止维护 +> 项目已经停止维护,请使用更好的代替方案:[art-template-loader](art-template-loader) TmodJS(原名 atc)是一个简单易用的前端模板预编译工具。它通过预编译技术让前端模板突破浏览器限制,实现后端模板一样的同步“文件”加载能力。它采用目录来组织维护前端模板,从而让前端模板实现工程化管理,最终保证前端模板在复杂单页 web 应用下的可维护性。同时预编译输出的代码经过多层优化,能够在最大程度节省客户端资源消耗。 From ec6c6e070b6f089a6ca4bfc4ff4231cfd4296d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=96=E9=A5=BC?= Date: Sun, 16 Apr 2017 16:14:00 +0800 Subject: [PATCH 87/87] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7b4cb9..ce84088 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TmodJS -> 项目已经停止维护,请使用更好的代替方案:[art-template-loader](art-template-loader) +> 项目已经停止维护,请使用更好的代替方案:[art-template-loader](https://github.com/aui/art-template-loader) TmodJS(原名 atc)是一个简单易用的前端模板预编译工具。它通过预编译技术让前端模板突破浏览器限制,实现后端模板一样的同步“文件”加载能力。它采用目录来组织维护前端模板,从而让前端模板实现工程化管理,最终保证前端模板在复杂单页 web 应用下的可维护性。同时预编译输出的代码经过多层优化,能够在最大程度节省客户端资源消耗。