Comparing version 3.0.1 to 3.2.0
193
doc/api.md
## API | ||
- [methods](#methods) | ||
- [classes](#classes) | ||
ETpl 是一个多 Engine 实例的模板引擎。初始化时其自动创建一个默认的 Engine 实例,并将其暴露。大多数应用场景可直接使用默认的引擎实例。 | ||
```javascript | ||
var etpl = require( 'etpl' ); | ||
``` | ||
### methods | ||
你也可以通过 `new etpl.Engine` 初始化自己的引擎实例。 *不同的引擎实例可有效避免target命名冲突的问题。* | ||
ETPL初始化时自动创建一个默认的Engine实例,并将其暴露。大多数应用场景可直接使用默认的引擎实例。 | ||
```javascript | ||
var etpl = require( 'etpl' ); | ||
var etplEngine = new etpl.Engine(); | ||
``` | ||
引擎实例的初始化允许传入引擎参数。支持的引擎参数见下面的`config`方法。 | ||
```javascript | ||
var etpl = require( 'etpl' ); | ||
var etplEngine = new etpl.Engine({ | ||
commandOpen: '<%', | ||
commandClose: '%>' | ||
}); | ||
``` | ||
##### {void} addFilter( {string}name, {function({string}, {...*}):string}filter ) | ||
下面是 Engine 的实例方法列表。 | ||
为默认引擎添加过滤器。过滤函数的第一个参数为过滤源字符串,其后的参数可由模板开发者传入。过滤函数必须返回string。 | ||
- [addCommand](#addCommand) | ||
- [addFilter](#addFilter) | ||
- [compile](#compile) | ||
- [config](#config) | ||
- [getRenderer](#getRenderer) | ||
- [load](#load) | ||
- [loadFromFile](#loadFromFile) | ||
- [parse](#parse) | ||
- [render](#render) | ||
### addCommand | ||
`{void} addCommand( {string}name, {Object}command )` | ||
`常用` 自定义命令标签。 | ||
- `{string}`name - 命令标签名称 | ||
- `{Object}`command - 命令对象 | ||
command 参数是一个对象,包含一些在命令标签编译时各个阶段的处理方法。 | ||
- `init(context)` - 初始化 | ||
- `open(context)` - 标签起始 | ||
- `close(context)` - 标签闭合 | ||
- `getRendererBody():string` - 生成编译代码 | ||
```javascript | ||
etpl.addCommand('dump', { | ||
init: function () { | ||
var match = this.value.match(/^\s*([a-z0-9_]+)\s*$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + this.value); | ||
} | ||
this.name = match[1]; | ||
this.cloneProps = ['name']; | ||
}, | ||
open: function (context) { | ||
context.stack.top().addChild(this); | ||
}, | ||
getRendererBody: function () { | ||
var util = etpl.util; | ||
var options = this.engine.options; | ||
return util.stringFormat( | ||
'console.log({0});', | ||
util.compileVariable(options.variableOpen + this.name + options.variableClose, this.engine) | ||
); | ||
} | ||
}); | ||
``` | ||
### addFilter | ||
`{void} addFilter( {string}name, {function({string}, {...*}):string}filter )` | ||
`常用` 为默认引擎添加过滤器。过滤函数的第一个参数为过滤源字符串,其后的参数可由模板开发者传入。过滤函数必须返回string。 | ||
- `{string}`name - 过滤器名称 | ||
@@ -28,6 +99,8 @@ - `{Function}`filter - 过滤函数 | ||
##### {Function} compile( {string}source ) | ||
### compile | ||
使用默认引擎编译模板。返回第一个target编译后的renderer函数。 | ||
`{Function} compile( {string}source )` | ||
`常用` 使用默认引擎编译模板。返回第一个target编译后的renderer函数。 | ||
- `{string}`source - 模板源代码 | ||
@@ -40,6 +113,8 @@ | ||
##### {void} config( {Object}options ) | ||
### config | ||
对默认引擎进行配置,配置参数将合并到引擎现有的参数中。[查看配置参数](config.md)。 | ||
`{void} config( {Object}options )` | ||
`常用` 对默认引擎进行配置,配置参数将合并到引擎现有的参数中。[查看配置参数](config.md)。 | ||
```javascript | ||
@@ -51,6 +126,8 @@ etplEngine.config( { | ||
##### {Function} getRenderer( {string}name ) | ||
### getRenderer | ||
从默认引擎中,根据target名称获取编译后的renderer函数。 | ||
`{Function} getRenderer( {string}name )` | ||
`常用` 从默认引擎中,根据target名称获取编译后的renderer函数。 | ||
- `{string}`name - target名称 | ||
@@ -64,106 +141,50 @@ | ||
##### {Function} parse( {string}source ) | ||
同`compile`方法。该方法的存在是为了兼容老版本的模板引擎api,不建议使用。 | ||
### load | ||
`{Function} load( {string}name )` | ||
`仅node环境` 加载并编译target文件 | ||
##### {string} render( {string}name, {Object}data ) | ||
使用默认引擎执行模板渲染,返回渲染后的字符串。 | ||
- `{string}`name - target名称 | ||
- `{Object}`data - 模板数据。可以是plain object,也可以是带有 **{string}get({string}name)** 方法的对象 | ||
```javascript | ||
etpl.compile( '<!-- target: hello -->Hello ${name}!' ); | ||
etpl.render( 'hello', {name: 'ETPL'} ); // Hello ETPL! | ||
var mainRenderer = etpl.load( 'main' ); | ||
mainRenderer( {name: 'ETPL'} ); | ||
``` | ||
### classes | ||
### loadFromFile | ||
#### Engine | ||
`{Function} loadFromFile( {string}file )` | ||
*不同的引擎实例可有效避免target命名冲突的问题。* | ||
`仅node环境` 加载并编译模板文件 | ||
##### 初始化 | ||
- `{string}`file - 模板文件路径 | ||
下面的代码可以初始化一个新的引擎实例。 | ||
```javascript | ||
var etpl = require( 'etpl' ); | ||
var etplEngine = new etpl.Engine(); | ||
var mainRenderer = etpl.loadFromFile( path.resolve(__dirname, 'main.etpl') ); | ||
mainRenderer( {name: 'ETPL'} ); | ||
``` | ||
引擎实例的初始化允许传入引擎参数。支持的引擎参数见下面的`config`方法。 | ||
```javascript | ||
var etpl = require( 'etpl' ); | ||
var etplEngine = new etpl.Engine({ | ||
commandOpen: '<%', | ||
commandClose: '%>' | ||
}); | ||
``` | ||
### parse | ||
##### {void} addFilter( {string}name, {function({string}, {...*}):string}filter ) | ||
同`compile`方法。该方法的存在是为了兼容老版本的模板引擎api,不建议使用。 | ||
添加过滤器。过滤函数的第一个参数为过滤源字符串,其后的参数可由模板开发者传入。过滤函数必须返回string。 | ||
- `{string}`name - 过滤器名称 | ||
- `{Function}`filter - 过滤函数 | ||
### render | ||
```javascript | ||
etplEngine.addFilter( 'markdown', function ( source, useExtra ) { | ||
// ...... | ||
} ); | ||
``` | ||
`{string} render( {string}name, {Object}data )` | ||
##### {Function} compile( {string}source ) | ||
使用默认引擎执行模板渲染,返回渲染后的字符串。 | ||
编译模板。返回第一个target编译后的renderer函数。 | ||
- `{string}`source - 模板源代码 | ||
```javascript | ||
var helloRenderer = etplEngine.compile( 'Hello ${name}!' ); | ||
helloRenderer( {name: 'ETPL'} ); // Hello ETPL! | ||
``` | ||
##### {void} config( {Object}options ) | ||
对引擎进行配置,配置参数将合并到引擎现有的参数中。[查看配置参数](config.md)。 | ||
```javascript | ||
etplEngine.config( { | ||
defaultFilter: '' | ||
} ); | ||
``` | ||
##### {Function} getRenderer( {string}name ) | ||
根据target名称获取编译后的renderer函数。 | ||
- `{string}`name - target名称 | ||
```javascript | ||
etplEngine.compile( '<!-- target: hello -->Hello ${name}!' ); | ||
var helloRenderer = etplEngine.getRenderer( 'hello' ); | ||
helloRenderer( {name: 'ETPL'} ); // Hello ETPL! | ||
``` | ||
##### {string} render( {string}name, {Object}data ) | ||
执行模板渲染,返回渲染后的字符串。 | ||
- `{string}`name - target名称 | ||
- `{Object}`data - 模板数据。可以是plain object,也可以是带有 **{string}get({string}name)** 方法的对象 | ||
```javascript | ||
etplEngine.compile( '<!-- target: hello -->Hello ${name}!' ); | ||
etplEngine.render( 'hello', {name: 'ETPL'} ); // Hello ETPL! | ||
etpl.compile( '<!-- target: hello -->Hello ${name}!' ); | ||
etpl.render( 'hello', {name: 'ETPL'} ); // Hello ETPL! | ||
``` | ||
## Config | ||
- [commandOpen](#commandOpen) | ||
- [commandClose](#commandClose) | ||
- [commandSyntax](#commandSyntax) | ||
- [variableOpen](#variableOpen) | ||
- [variableClose](#variableClose) | ||
- [defaultFilter](#defaultFilter) | ||
- [commandOpen](#commandopen) | ||
- [commandClose](#commandclose) | ||
- [commandSyntax](#commandsyntax) | ||
- [variableOpen](#variableopen) | ||
- [variableClose](#variableclose) | ||
- [defaultFilter](#defaultfilter) | ||
- [strip](#strip) | ||
- [namingConflict](#namingConflict) | ||
- [namingConflict](#namingconflict) | ||
- [missTarget](#misstarget) | ||
- [dir](#dir) | ||
- [extname](#extname) | ||
- [encoding](#encoding) | ||
@@ -81,3 +85,3 @@ | ||
target或master名字冲突时的处理策略,值可以是: | ||
target名字冲突时的处理策略,值可以是: | ||
@@ -88,1 +92,32 @@ - `error`: 抛出错误。此项为默认值 | ||
### missTarget | ||
`string` | ||
target不存在时的处理策略,值可以是: | ||
- `error`: 抛出错误 | ||
- `ignore`: 静默处理,无错误。此项为默认值 | ||
### dir | ||
`string` `仅node环境有效` | ||
模板文件目录,用于加载target文件时的路径查找,默认值为 *process.pwd()* | ||
### extname | ||
`string` `仅node环境有效` | ||
模板文件后缀名,默认值为 *.etpl* | ||
### encoding | ||
`string` `仅node环境有效` | ||
模板文件编码,默认值为 *UTF-8* | ||
@@ -276,3 +276,3 @@ ## Syntax | ||
使用引用代入功能,import需要手工编写`指令结束`,并且imort中只允许包含`block`,其他指令将被忽略。 | ||
使用引用代入功能,import需要手工编写`指令结束`,并且import中只允许包含`block`,其他指令将被忽略。 | ||
@@ -279,0 +279,0 @@ ##### 示例 |
/** | ||
* ETPL (Enterprise Template) | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* | ||
* @file Node入口 | ||
@@ -9,2 +9,2 @@ * @author firede(firede@firede.us) | ||
module.exports = require('./src/main'); | ||
module.exports = require('./src/node'); |
{ | ||
"name": "etpl", | ||
"version": "3.0.1", | ||
"version": "3.2.0", | ||
"contributors": [ | ||
@@ -5,0 +5,0 @@ { "name": "erik", "email": "errorrik@gmail.com" }, |
@@ -8,2 +8,15 @@ # ETPL (Enterprise Template) | ||
## Download | ||
除了通过github clone外,你可以通过`右键另存`的方式获得ETpl: | ||
- [压缩代码 (Compressed)](http://s1.bdstatic.com/r/www/cache/ecom/etpl/3-2-0/etpl.js) | ||
- [源码 (Source)](http://s1.bdstatic.com/r/www/cache/ecom/etpl/3-2-0/etpl.source.js) | ||
也可以通过CDN引用: | ||
```html | ||
<script src="http://s1.bdstatic.com/r/www/cache/ecom/etpl/3-2-0/etpl.js"></script> | ||
``` | ||
## Start | ||
@@ -79,2 +92,11 @@ | ||
## Related | ||
* Sublime Text 语法高亮插件:[sublime-etpl](https://github.com/ecomfe/sublime-etpl) | ||
* vim 语法高亮插件:[vim-etpl](https://github.com/hushicai/vim-etpl) | ||
* Atom 语法高亮插件:[atom-etpl](https://github.com/ecomfe/atom-etpl) | ||
## Compatibility | ||
@@ -120,1 +142,2 @@ | ||
``` | ||
291
src/main.js
@@ -129,2 +129,3 @@ /** | ||
/* jshint -W054 */ | ||
var subClassProto = subClass.prototype; | ||
var F = new Function(); | ||
@@ -134,5 +135,4 @@ F.prototype = superClass.prototype; | ||
subClass.prototype.constructor = subClass; | ||
extend(subClass.prototype, subClassProto); | ||
/* jshint +W054 */ | ||
// 由于引擎内部的使用场景都是inherits后,逐个编写子类的prototype方法 | ||
// 所以,不考虑将原有子类prototype缓存再逐个拷贝回去 | ||
} | ||
@@ -294,6 +294,6 @@ | ||
// HACK: IE8-时,编译后的renderer使用join Array的策略进行字符串拼接 | ||
if (typeof navigator !== 'undefined' | ||
&& /msie\s*([0-9]+)/i.test(navigator.userAgent) | ||
&& RegExp.$1 - 0 < 8 | ||
) { | ||
var ieVersionMatch = typeof navigator !== 'undefined' | ||
&& navigator.userAgent.match(/msie\s*([0-9]+)/i); | ||
if (ieVersionMatch && ieVersionMatch[1] - 0 < 8) { | ||
RENDER_STRING_DECLATION = 'var r=[],ri=0;'; | ||
@@ -463,8 +463,8 @@ RENDER_STRING_ADD_START = 'r[ri++]='; | ||
var seg = filterSegs[i]; | ||
var segMatch = seg.match(/^\s*([a-z0-9_-]+)(\((.*)\))?\s*$/i); | ||
if (segMatch) { | ||
variableCode.unshift('fs["' + segMatch[1] + '"]('); | ||
if (/^\s*([a-z0-9_-]+)(\((.*)\))?\s*$/i.test(seg)) { | ||
variableCode.unshift('fs["' + RegExp.$1 + '"]('); | ||
if (RegExp.$3) { | ||
variableCode.push(',', RegExp.$3); | ||
if (segMatch[3]) { | ||
variableCode.push(',', segMatch[3]); | ||
} | ||
@@ -707,3 +707,4 @@ | ||
/* jshint ignore:start */ | ||
if (!/^\s*([a-z0-9\/_-]+)\s*(\(\s*master\s*=\s*([a-z0-9\/_-]+)\s*\))?\s*/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9\/\._-]+)\s*(\(\s*master\s*=\s*([a-z0-9\/\._-]+)\s*\))?\s*/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
@@ -713,4 +714,4 @@ } | ||
this.master = RegExp.$3; | ||
this.name = RegExp.$1; | ||
this.master = match[3]; | ||
this.name = match[1]; | ||
Command.call(this, value, engine); | ||
@@ -733,7 +734,8 @@ | ||
function BlockCommand(value, engine) { | ||
if (!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9\/\._-]+)\s*$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.name = RegExp.$1; | ||
this.name = match[1]; | ||
Command.call(this, value, engine); | ||
@@ -755,9 +757,10 @@ this.cloneProps = ['name']; | ||
function ImportCommand(value, engine) { | ||
if (!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9\/\._-]+)\s*$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.name = RegExp.$1; | ||
this.name = match[1]; | ||
Command.call(this, value, engine); | ||
this.cloneProps = ['name', 'state', 'blocks']; | ||
this.cloneProps = ['name', 'state', 'blocks', 'target']; | ||
this.blocks = {}; | ||
@@ -778,8 +781,9 @@ } | ||
function VarCommand(value, engine) { | ||
if (!/^\s*([a-z0-9_]+)\s*=([\s\S]*)$/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9_]+)\s*=([\s\S]*)$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.name = RegExp.$1; | ||
this.expr = RegExp.$2; | ||
this.name = match[1]; | ||
this.expr = match[2]; | ||
Command.call(this, value, engine); | ||
@@ -801,8 +805,9 @@ this.cloneProps = ['name', 'expr']; | ||
function FilterCommand(value, engine) { | ||
if (!/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.name = RegExp.$1; | ||
this.args = RegExp.$3; | ||
this.name = match[1]; | ||
this.args = match[3]; | ||
Command.call(this, value, engine); | ||
@@ -824,8 +829,9 @@ this.cloneProps = ['name', 'args']; | ||
function UseCommand(value, engine) { | ||
if (!/^\s*([a-z0-9\/_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)) { | ||
var match = value.match(/^\s*([a-z0-9\/\._-]+)\s*(\(([\s\S]*)\))?\s*$/i); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.name = RegExp.$1; | ||
this.args = RegExp.$3; | ||
this.name = match[1]; | ||
this.args = match[3]; | ||
Command.call(this, value, engine); | ||
@@ -859,9 +865,10 @@ this.cloneProps = ['name', 'args']; | ||
if (!rule.test(value)) { | ||
var match = value.match(rule); | ||
if (!match) { | ||
throw new Error('Invalid ' + this.type + ' syntax: ' + value); | ||
} | ||
this.list = RegExp.$1; | ||
this.item = RegExp.$2; | ||
this.index = RegExp.$4; | ||
this.list = match[1]; | ||
this.item = match[2]; | ||
this.index = match[4]; | ||
Command.call(this, value, engine); | ||
@@ -920,2 +927,29 @@ this.cloneProps = ['list', 'item', 'index']; | ||
/** | ||
* 初始化的额外行为,保存依赖信息 | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
TargetCommand.prototype.init = function (context) { | ||
if (this.master) { | ||
context.deps[this.master] = true; | ||
} | ||
}; | ||
/** | ||
* 初始化的额外行为,保存依赖信息 | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
UseCommand.prototype.init = | ||
/** | ||
* 初始化的额外行为,保存依赖信息 | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
ImportCommand.prototype.init = function (context) { | ||
context.deps[this.name] = true; | ||
}; | ||
/** | ||
* Target的节点状态 | ||
@@ -968,8 +1002,16 @@ * | ||
var master = this.engine.targets[masterName]; | ||
if (master && master.applyMaster(master.master)) { | ||
this.children = master.clone().children; | ||
replaceBlock(this); | ||
this.state = TargetState.APPLIED; | ||
return 1; | ||
if (master) { | ||
if (master.applyMaster(master.master)) { | ||
this.children = master.clone().children; | ||
replaceBlock(this); | ||
this.state = TargetState.APPLIED; | ||
return 1; | ||
} | ||
} | ||
else if (this.engine.options.missTarget === 'error') { | ||
throw new Error('[ETPL_MISS_TARGET]' + masterName | ||
+ ', when extended by ' | ||
+ (this.target ? this.target.name : this.name) | ||
); | ||
} | ||
}; | ||
@@ -989,2 +1031,3 @@ | ||
var engine = this.engine; | ||
var targetName = this.name; | ||
var readyState = 1; | ||
@@ -1001,6 +1044,11 @@ | ||
var child = node.children[i]; | ||
if (child instanceof ImportCommand) { | ||
var target = engine.targets[child.name]; | ||
readyState = readyState | ||
&& target && target.isReady(engine); | ||
if (!target && engine.options.missTarget === 'error') { | ||
throw new Error('[ETPL_MISS_TARGET]' + child.name | ||
+ ', when imported by ' + targetName); | ||
} | ||
readyState = readyState && target && target.isReady(engine); | ||
} | ||
@@ -1082,3 +1130,3 @@ else if (child instanceof Command) { | ||
default: | ||
throw new Error('Target exists: ' + name); | ||
throw new Error('[ETPL_TARGET_EXISTS]' + name); | ||
} | ||
@@ -1238,47 +1286,5 @@ } | ||
*/ | ||
UseCommand.prototype.beforeOpen = | ||
Command.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
ImportCommand.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
VarCommand.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
ForCommand.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
FilterCommand.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
BlockCommand.prototype.beforeOpen = | ||
/** | ||
* 节点open前的处理动作:节点不在target中时,自动创建匿名target | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
IfCommand.prototype.beforeOpen = | ||
/** | ||
* 文本节点被添加到分析环境前的处理动作:节点不在target中时,自动创建匿名target | ||
@@ -1298,2 +1304,9 @@ * | ||
/** | ||
* 节点open前的处理动作:target 不需要自动创建target,所以清空方法 | ||
* | ||
* @param {Object} context 语法分析环境对象 | ||
*/ | ||
TargetCommand.prototype.beforeOpen = function () {}; | ||
/** | ||
* 获取renderer body的生成代码 | ||
@@ -1415,32 +1428,14 @@ * | ||
/** | ||
* 命令类型集合 | ||
* | ||
* @type {Object} | ||
*/ | ||
var commandTypes = {}; | ||
/** | ||
* 添加命令类型 | ||
* | ||
* @inner | ||
* @param {Engine} engine etpl引擎 | ||
* @param {string} name 命令名称 | ||
* @param {Function} Type 处理命令用到的类 | ||
*/ | ||
function addCommandType(name, Type) { | ||
commandTypes[name] = Type; | ||
function addCommandType(engine, name, Type) { | ||
engine.commandTypes[name] = Type; | ||
Type.prototype.type = name; | ||
} | ||
addCommandType('target', TargetCommand); | ||
addCommandType('block', BlockCommand); | ||
addCommandType('import', ImportCommand); | ||
addCommandType('use', UseCommand); | ||
addCommandType('var', VarCommand); | ||
addCommandType('for', ForCommand); | ||
addCommandType('if', IfCommand); | ||
addCommandType('elif', ElifCommand); | ||
addCommandType('else', ElseCommand); | ||
addCommandType('filter', FilterCommand); | ||
/** | ||
@@ -1458,2 +1453,3 @@ * etpl引擎类 | ||
* @param {string=} options.namingConflict target名字冲突时的处理策略 | ||
* @param {string=} options.missTarget target不存在时的处理策略 | ||
*/ | ||
@@ -1464,3 +1460,3 @@ function Engine(options) { | ||
commandClose: '-->', | ||
commandSyntax: /^\s*(\/)?([a-z]+)\s*(?::([\s\S]*))?$/, | ||
commandSyntax: /^\s*(\/)?([a-z]*)\s*(?::([\s\S]*))?$/, | ||
variableOpen: '${', | ||
@@ -1471,2 +1467,14 @@ variableClose: '}', | ||
this.commandTypes = {}; | ||
addCommandType(this, 'target', TargetCommand); | ||
addCommandType(this, 'block', BlockCommand); | ||
addCommandType(this, 'import', ImportCommand); | ||
addCommandType(this, 'use', UseCommand); | ||
addCommandType(this, 'var', VarCommand); | ||
addCommandType(this, 'for', ForCommand); | ||
addCommandType(this, 'if', IfCommand); | ||
addCommandType(this, 'elif', ElifCommand); | ||
addCommandType(this, 'else', ElseCommand); | ||
addCommandType(this, 'filter', FilterCommand); | ||
this.config(options); | ||
@@ -1488,2 +1496,3 @@ this.targets = {}; | ||
* @param {string=} options.namingConflict target名字冲突时的处理策略 | ||
* @param {string=} options.missTarget target不存在时的处理策略 | ||
*/ | ||
@@ -1510,12 +1519,17 @@ Engine.prototype.config = function (options) { | ||
Engine.prototype.parse = function (source) { | ||
/* jshint -W054 */ | ||
var renderer = new Function('return ""'); | ||
/* jshint +W054 */ | ||
if (source) { | ||
var targetNames = parseSource(source, this); | ||
var parseInfo = parseSource(source, this); | ||
var targetNames = parseInfo.targets; | ||
if (targetNames.length) { | ||
return this.targets[targetNames[0]].getRenderer(); | ||
renderer = this.targets[targetNames[0]].getRenderer(); | ||
} | ||
} | ||
/* jshint -W054 */ | ||
return new Function('return ""'); | ||
/* jshint +W054 */ | ||
return renderer; | ||
}; | ||
@@ -1555,2 +1569,22 @@ | ||
/** | ||
* 增加命令 | ||
* | ||
* @param {string} name 命令名称 | ||
* @param {Object|Function} command 命令对象或命令类 | ||
*/ | ||
Engine.prototype.addCommand = function (name, command) { | ||
var CommandType = command; | ||
if ('function' !== typeof CommandType) { | ||
CommandType = function (value, engine) { | ||
Command.call(this, value, engine); | ||
}; | ||
CommandType.prototype = command; | ||
} | ||
inherits(CommandType, Command); | ||
addCommandType(this, name, CommandType); | ||
}; | ||
/** | ||
* 增加过滤器 | ||
@@ -1562,3 +1596,3 @@ * | ||
Engine.prototype.addFilter = function (name, filter) { | ||
if (typeof filter === 'function') { | ||
if ('function' === typeof filter) { | ||
this.filters[name] = filter; | ||
@@ -1586,3 +1620,4 @@ } | ||
stack: stack, | ||
target: null | ||
target: null, | ||
deps: {} | ||
}; | ||
@@ -1623,2 +1658,3 @@ | ||
var match = commandSyntax.exec(text); | ||
var nodeName; | ||
@@ -1628,3 +1664,4 @@ // 符合command规则,并且存在相应的Command类,说明是合法有含义的Command | ||
if (match | ||
&& (NodeType = commandTypes[match[2].toLowerCase()]) | ||
&& (nodeName = match[2] || 'target') | ||
&& (NodeType = engine.commandTypes[nodeName.toLowerCase()]) | ||
&& typeof NodeType === 'function' | ||
@@ -1646,5 +1683,8 @@ ) { | ||
currentNode = new NodeType(match[3], engine); | ||
if (typeof currentNode.beforeOpen === 'function') { | ||
currentNode.beforeOpen(analyseContext); | ||
if ('function' === typeof currentNode.init) { | ||
currentNode.init(analyseContext); | ||
} | ||
currentNode.beforeOpen(analyseContext); | ||
currentNode.open(analyseContext); | ||
@@ -1673,8 +1713,27 @@ } | ||
return analyseContext.targets; | ||
var deps = []; | ||
for (var key in analyseContext.deps) { | ||
deps.push(key); | ||
} | ||
return { | ||
targets: analyseContext.targets, | ||
deps: deps | ||
}; | ||
} | ||
// export object | ||
var etpl = new Engine(); | ||
etpl.Engine = Engine; | ||
etpl.version = '3.2.0'; | ||
etpl.Command = Command; | ||
etpl.util = { | ||
inherits: inherits, | ||
stringFormat: stringFormat, | ||
stringLiteralize: stringLiteralize, | ||
compileVariable: compileVariable, | ||
parseSource: parseSource | ||
}; | ||
if (typeof exports === 'object' && typeof module === 'object') { | ||
@@ -1681,0 +1740,0 @@ // For CommonJS |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
1633
141
81667
2