- Notifications
You must be signed in to change notification settings - Fork38
A feature rich code block preprocessing tool.
License
zswang/jdists
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
标签: jdists 教程
通常软件发布时会将源文件做一次「预处理」再编译成可执行文件,才发布到市场。
- 配置线上运行环境,如调试服务地址需变更为实现线上地址;
- 减少执行程序的大小,移除没有使用的代码或资源并压缩;
- 增加逆向工程的成本,给代码做混淆(包括改变标识符和代码结构),降低可读性;
- 移除或增加调试功能,关闭或开启一些特权后门。
一些 IDE 已在「编译」时集成了「预处理」功能。
jdists 是一款强大的代码块预处理工具。
通常就是注释或注释包裹的代码片段,用于表达各种各样的含义。
举个栗子
- TODO 注释,表示代码中待完善的地方
/* TODO 功能待开发 */
- wiredep 注释,表示引入 bower 组件依赖的 css 资源
<!-- bower:css --><linkrel="stylesheet"href="bower_components/css/bootstrap.css"/><!-- endbower -->
- jshint.js 顶部注释,表示版权声明
/*! * JSHint, by JSHint Community. * * This file (and this file only) is licensed under the same slightly modified * MIT license that JSLint is. It stops evil-doers everywhere: * * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) * ......... */
- jshint.js 另一部分注释,表示代码检查配置项
/*jshint quotmark:double *//*global console:true *//*exported console */
总之,本文所指「代码块」就是有特殊意义的注释。
指在代码编译之前,将代码文件按代码块粒度做一次编码或解析。
举个栗子,原本无效的代码片段,经过编码后变成了有效代码。
预处理前:
/*<jdists>console.log('Hello World!');</jdists>*/
预处理后:
console.log('Hello World!');
市面上有不少,这里只列两个比较典型的。
- 已被普遍使用的JSDoc,功能是将代码中的注释抽离成 API 文档。
/** * Represents a book. *@constructor *@param {string} title - The title of the book. *@param {string} author - The author of the book. */functionBook(title,author){}
- JSDev 是由 JSON 之父 Douglas Crockford 编写。jdists 与 JSDev 的功能类似,但 jdists 功能要复杂很多。
C command line example:
jsdev -comment"Devel Edition."<input>output test_expose enter:trace.enter exit:trace.exit unless:alert
#"auto" data-snippet-clipboard-copy-content="output = JSDEV(input, ["test_expose","enter:trace.enter","exit:trace.exit","unless:alert"] , ["Devel Edition."]);">
output=JSDEV(input,["test_expose","enter:trace.enter","exit:trace.exit","unless:alert"],["Devel Edition."]);
input:
// This is a sample file.functionConstructor(number){/*enter 'Constructor'*//*unless(typeof number !== 'number') 'number', "Type error"*/functionprivate_method(){/*enter 'private_method'*//*exit 'private_method'*/}/*test_exposethis.private_method = private_method;*/this.priv=function(){/*enter 'priv'*/private_method();/*exit 'priv'*/}/*exit "Constructor"*/}
output:
// Devel Edition.// This is a sample file.functionConstructor(number){{trace.enter('Constructor');}if(typeofnumber!=='number'){alert('number',"Type error");}functionprivate_method(){{trace.enter('private_method');}{trace.exit('private_method');}}{this.private_method=private_method;}this.priv=function(){{trace.enter('priv');}private_method();{trace.exit('priv');}}{trace.exit("Constructor");}}
lightly minified:
functionConstructor(number){functionprivate_method(){}this.priv=function(){private_method();}}
- 处理速度快,按需对代码块部分进行指定编码;
- 控制力更强,可以控制每个字符的变化;
- 不干扰编译器,编译器天然忽略注释。
- 不容易学习和记忆。
begin
还是start
,前缀还是后缀?
<!-- 乐居广告脚本 begin-->/* jshint ignore:start *//* TODO 待开发功能 */
- 是否存在闭合不明显。什么时候生效,什么时候失效?
/*jshint unused:true, eqnull:true*//*test_exposethis.private_method = private_method;*/
- 没有标准,不能跨语言。JSDev 和 JSDoc 不能用于其他主流语言,如 Python、Lua 等。
问题也就是:怎么定义、怎么处理、什么情况下触发。
本人拟订了一个基于「XML 标签」+「多行注释」的代码块规范:CBML
优势:
- 学习成本低,XML、多行注释都是大家熟知的东西;
- 标签是否闭合很明显;
- 支持多种主流编程语言。
处理的步骤无外乎就是:输入、编码、输出
经过解析 CBML 的语法树,获取tag
和attribute
两个关键信息。
如果tag
值为<jdists>
就开始按 jdists 的规则进行处理。
整个处理过程由四个关键属性决定:
import=
指定输入媒介export=
指定输出媒介encoding=
指定编码集合trigger=
指定触发条件
举个例子
/*<jdists export="template.js" trigger="@version < '1.0.0'">var template = /*<jdists encoding="base64,quoted" import="main.html?template" />*//*</jdists>
这里有两个代码块,还是一个嵌套结构
- 外层代码块属性
export="template.js"
指定内容导出到文件template.js
(目录相对于当前代码块所在的文件)。 - 外层代码块属性
trigger="@version < '1.0.0'"
指定命令行参数version
小于'1.0.0'
才触发。 - 内层代码块属性
encoding="base64,quoted"
表示先给内容做一次base64
编码再做一次quoted
即,编码成字符串字面量。
有两个触发条件:
- 当
tag
值为<jdists>
或者是被配置为jdists
标签 - 当属性
trigger=
表达式判断为true
由 tag 标识的代码区域
代码块主要有如下三种形式:
- 空内容代码块,没有包裹任何代码
/*<jdists import="main.js" />*/
- 有效内容代码块,包裹的内容是编译器会解析
/*<jdists encoding="uglify">*/functionformat(template,json){if(typeoftemplate==='function'){// 函数多行注释处理template=String(template).replace(/[^]*\/\*!?\s*|\s*\*\/[^]*/g,// 替换掉函数前后部分'');}returntemplate.replace(/#\{(.*?)\}/g,function(all,key){returnjson&&(keyinjson) ?json[key] :"";});}/*</jdists>*/
- 无效内容代码块,包裹的内容也在注释中
/*<jdists>console.log('version: %s', version);<jdists>*/
<jdists>
| 自定义
import=
指定输入媒介export=
指定输出媒介encoding=
指定编码集合trigger=
指定触发条件
&content
默认为 "&"file
文件> 如:>main.js
>index.html
#variant
变量> 如:>#name
>#data
[file]?block
readonly 代码块,默认file
为当前文件> 如:>filename?tagName
>filename?tagName[attrName=attrValue]
>filename?tagName[attrName=attrValue][attrName2=attrValue2]
@argument
readonly 控制台参数> 如:>@output
>@version
:environment
readonly 环境变量> 如:>:HOME
>:USER
[...]
、{...}
readonly 字面量> 如:>[1, 2, 3, 4]
>{title: 'jdists'}
'string'
readonly 字符串> 如:>'zswang'
触发器有两种表达式
- 触发器名列表与控制台参数
--trigger
是否存在交集,存在则被触发
当
$ jdists ... --trigger release
触发
<!--remove trigger="release"--><label>release</label><!--/remove-->
- 将变量、属性、环境变量表达式替换后的字面量结果是否为 true
当
$ jdists ... --version 0.0.9
触发
<!--remove trigger="@version < '1.0.0'"--><label>1.0.0+</label><!--/remove-->
可以参考项目中 processor 目录,中自带编码器的写法
举个栗子
varejs=require('ejs');/** * ejs 模板渲染 * *@param {string} content 文本内容 *@param {Object} attrs 属性 *@param {string} attrs.data 数据项 *@param {Object} scope 作用域 *@param {Function} scope.execImport 导入数据 *@param {Function} scope.compile 二次编译 jdists 文本 */module.exports=functionprocessor(content,attrs,scope){if(!content){returncontent;}varrender=ejs.compile(content);vardata;if(attrs.data){/*jslint evil: true */data=newFunction('return ('+scope.execImport(attrs.data)+');')();}else{data=null;}returnscope.compile(render(data));};
详情参考:jdists Scope
通过块导入
<!--remove--><script>/*<jdists encoding="base64">*/console.log('hello world!');/*</jdists>*/</script><!--/remove--><!--jdists><script src="data:application/javascript;base64,/*<jdists import="?[id=code]" />*/"></script></jdists-->
通过变量导入
<!--remove--><script>/*<jdists encoding="base64" export="#code">*/console.log('hello world!');/*</jdists>*/</script><!--/remove--><!--jdists><script src="data:application/javascript;base64,/*<jdists import="#code" />*/"></script></jdists-->
jdists 依赖 node v0.10.0 以上的环境
$ npm install jdists [-g]
Usage:jdists <input list> [options]Options:-r, --remove Remove block tag name list (default "remove,test")-o, --output Output file (default STDOUT)-v, --version Output jdists version-t, --trigger Trigger name list (default "release")-c, --config Path to config file (default ".jdistsrc")
varcontent=jdists.build(filename,{remove:'remove,debug',trigger:'release'});
https://github.com/zswang/jdists/issues
$ git clone https://github.com/zswang/jdists.git
$ npm install
$ npm test
$ npm run dist
$ npm run cover
[lib] --- 发布后的代码目录jdists.js --- jdists 业务代码scope.js --- jdists 作用域[processor] --- 预制编码器[processor-extend] --- 未预制的编码器,可能会常用的[src] --- 开发期代码[test] --- 测试目录[fixtures] --- 测试用例test.js --- 测试调度文件index.js --- jdists 声明cli.js --- jdists 控制台
About
A feature rich code block preprocessing tool.