bee

xml build tool write in Node

npm install bee
12 downloads in the last week
26 downloads in the last month

bee

一个前端build工具

Unit Test (the service down?)

travis build status

Usage

npm install bee -g

Summary

一个bee工程可以包含多个task,一个task可以依赖多个其它task,当执行某个task,其依赖的task会现执行,其设计模式跟ant类似。

build.xml所在文件夹下执行:

bee

或者执行某个文件中的某个特殊的任务:

bee publish -f ./build/publish.xml

build.xml文件:

<?xml version='1.0' encoding='utf-8'?>
<project name='using bee to build a project example'  basedir='.'>
  <description>clean, create, combo</description>
  <!-- 可以包含properties文件 -->
  <property file="version.properties"/>
  <property name="src" value="../src"/>
  <property name='build' value='../dist'/>
  <!--clean-->
  <target name='clean'>
    <delete dir='${build}'/>
  </target>
  <!--create-->
  <target name="create">
    <mkdir dir='${build}'/>
  </target>
  <!--combo-->
  <target name='combo'>
    <concat dir='${src}/css' file='dialog.css,photoswipe.css,slider.css,app.css'
         destfile='${build}/css/combo.css'/>
  <target>
  <!--default-->
  <target name='build' depends='clean,create,combo'/>
</project>

Why XML?

  • XML是国际标准格式,具有严谨的格式规范,编写起来简单明了,学习门槛低。
  • 作为通用的数据格式,便于程序动态生成和建模,为可视化编辑提供支持。
  • 支持注释。

Config

bee project有name, description, level等属性

  • name 项目名次
  • description 项目描述
  • level 日志level,可以是log, info, debug, warn, error。默认是debug,即项目运行过程中不会输出loginfo信息到控制台。

npm

在build过程中,可能需要自定义脚本,这些脚本或许依赖某个npm包。基于这个需求场景,bee支持在build启动时根据配置来自动下载npm包。

<npm>node-uploader,node-uuid</npm>

安装到全局:

<npm>coffee-script</npm>

taskdef

在build过程中,很有可能依赖第三方npm插件,所以使用taskdef能很方便的加载使用到的插件,插件的下载和装配过程是bee自动完成的。

<taskdef npm="bee-less,bee-min@0.3.0"/>

当然也可以使用taskdef自定义插件

<taskdef>path/custom.js</taskdef>

详细的使用方法,可以参考后面的自定义插件这一段。

Tasks

bee内置了一些常用的命令,很多都是从ant工具上获得灵感。除此之外,我们可以很方便地定义自己的命令来扩充功能。

available

判断某个文件或文件夹是否存在

<available target='file-or-dir' property='target-exist'/>

basename

获得某个文件的basename

<basename file='${input}' property='file-basename'/>

dirname

获取某个文件的目录名

<dirname file='${input}' property='file-dirname'/>

extname

获取文件的扩展名

<extname file='${input}' property='file-extname'/>

touch

创建一个文件

<touch file='readme.md'/>
<touch file='readme.txt' permission='755'>text content</touch>

copy

复制某个文件或文件夹

<copy file='src/myfile.js' tofile='build/myfile.js'/>
<copy dir='src/lib' todir='dest'/>

<copy todir='dest'>
  <fileset src='src'>
    <exclude value='.svn'/>
  </fileset>
</copy>

delete

删除文件

<delete file='myfile'/>

<delete>
  <fileset dir="src">
    <exclude value='.svn'/>
  </fileset>
</delete>
<delete>
  <fileset dir='.'>
    <include glob='.svn'/>
  </fileset>
</delete>

mkdir

创建文件夹

<mkdir dir='~/Documents/app/bee-test/example'/>
<mkdir dir='${src}/lib'/>

move

复制某个文件或文件夹

<move file='tmp/myfile.js' tofile='build/myfile.js'/>
<move dir='tmp/lib' todir='dest'/>

<move todir='dest'>
  <fileset src='tmp'>
    <exclude value='.svn'/>
  </fileset>
</move>

rename

重命名某个文件或文件夹

<rename file='myfile.js' destfile='my-file.js'/>

echo

打印字符串控制台

<echo>the server is running on port ${port}.</echo>

exec

执行一段shell脚本

<exec>node myNodeApp.js</exec>

concat

合并文件

<concat files='a.js,b.js,c.js' destfile='combo.js'/>

更多的合并选项。

<concat>
  <!-- 自定义header,并删除每行的行前空白字符 -->
  <header trimleading="yes">
  ========
  header
  ========
  </header>

  <!-- 文件列表 -->
  <file>a.js</file>
  <file dir="optional-dir" name="b.js"/>
  <filelist dir="some-dir">
    <file>m.js</file>
    <file name="n.js"/>
  </filelist>

  <!-- 自定义footer -->
  <footer>
=========
  footer
=========
  </footer>
</concat>

condition

条件处理

<condition property="result">
  <os platform='win32'/>
  <equals arg1='${arg1}' arg2='${arg2}'/>
  <or>
    <available file='myfile'/>
  </or>
</condition>

<condition property='result' if='target-if' unless='target-unless'>
  <os platform='win32'/>
  <equals arg1='${arg1}' arg2='${arg2}'/>
  <or>
    <available file='myfile'/>
  </or>
</condition>

get

通过URL下载某个文件

<get url="https://raw.github.com/colorhook/bee/master/README.md" dest="README.md"/>

input

与用户交互在控制台中输入

<input property='username' message='please type your username?' defaultvalue='default-user'/>
<input property='password' password='true' message='please type your password?'/>
<echo>username: ${username} password: ${password}</echo>

loadfile

载入一个文件

<loadfile file='src/lib/myfile.js' encoding='utf-8' property="content"/>
<echo>${content}</echo>
<!--write to file-->
<touch file="build/lib/myfile.js'>${content}</touch>

replace

替换文件中的某个标识

<!--替换combo.js中的@version@字符串-->
<tstamp property='timestamp'/>
<replace token='@time@' value='${timestamp}' file='combo.js'/>

多值替换。需要注意的是,对于替换?这类正则语义字符需要在token定义中加入\进行转义:

<replace file="replace/hello.txt">
    <replacefilter token="@1@" value="#1#"/>
    <replacefilter token="#1#" value="?1?"/>
    <replacefilter token="\?1\?" value="!1!"/>
    <replacefilter token="!1!" value="z1z"/>
</replace>

sleep

让程序sleep一段时间

<sleep value='1000'/>
<sleep>1000</sleep>

tstamp

获取时间戳

<tstamp property='tstamp' pattern='yyyy-MM-dd HH:mm:ss'/>

node

用node环境执行脚本,在该环境下,存在一个名为bee的全局变量。

<node>
<![CDATA[
console.log(require('os').platform);
//当前运行的project
console.log(bee.project);
]]>
</node>

自定义插件

除了有内置的task插件可以用之外,还可以用第三方插件或者自己定义插件。

使用第三方插件:

less

将less文件编译成css文件

<?xml version='1.0' encoding='utf-8'?>
<project name='using bee-less to build a project example'  basedir='.'>
  <description>less</description>
  <deskdef npm='bee-less'/>
  <target name="build">
    <less src='reset.less' dest='reset.css' encoding='utf-8'/>
  </target>
</project>

datauri

datauri某个css或图片

<?xml version='1.0' encoding='utf-8'?>
<project name='using bee-min to build a project example'  basedir='.'>
  <description>datauri</description>
  <deskdef npm='bee-min'/>
  <target name="build">
    <datauri src='a.css' dest='my-new-dir/a.css'/>
  </target>
</project>

min

压缩优化js、css、图片文件

<?xml version='1.0' encoding='utf-8'?>
<project name='using bee-min to build a project example'  basedir='.'>
  <description>min</description>
  <deskdef npm='bee-min'/>
  <target name="build">
    <min src='reset.css' dest='reset.min.css'/>
    <min file='myfie' destfile='myfile.min.js' type='js'/>
    <min file='logo.png' destfile='logo.png'/>
  </target>
</project>

mail

发送邮件

<?xml version="1.0" encoding="utf-8"?>
<project name="bee mail project" description="bee mail test">

  <property name="user" value="colorhook@gmail.com"/>
  <property name="from" value="colorhook@gmail.com"/>
  <property name="to" value="colorhook@gmail.com"/>

  <target name="default">
    <input setproperty='gmail.password' message='your gmail password? ' password='true'/>
    <echo>${gmail.password}</echo>
    <mail service='Gmail'>
      <auth user='${user}' pass='${gmail.password}'/>
      <message from="${from}" to="${to}">
        <html>
            <![CDATA[
            <b>Hello</b>, <span style='color:red'>bee-mail</span>
            ]]>
        </html>
      </message>
    </mail>
  </target>

</project>

自定义内置插件。

新建一个文件greeting.js,并写入如下代码:

module.exports = function(bee){
  bee.register('greeting', function(options){
    console.log('greeting: ' + options.value);
  });
}

greeting.js相同目录新建一个文件build.xml,并写入如下内容:

<?xml version="1.0" encoding="utf-8"?>
<project name="custom plugin" description="bee example">

  <taskdef file="greeting.js"/>

  <property name='argv' value='Write a custom task without publish to npm'/>

  <target name="build">
    <greeting>${argv}</greeting>
  </target>
</project>

childNodes

如上greeting.js所示,xml定义的task定义会变成object形式的js对象:

<taskName prop1='a'>
  <subprop prop2='b'>
    <key1 prop3='c'>value1</key1>
  </subprop>
</taskName>

经过./XMLParser.js解析之后

module.exports = function(bee){
  bee.register('taskName', function(options){
    /**
    {
      prop1: 'a',
            value: '',
      childNodes: [
        {
                    name: 'subprop',
                    value: {
                        prop2: 'b',
                        value: '',
                        childNodes: [
                            {
                                name: 'key1',
                                value: {
                                    prop3: 'c',
                                    value: 'value1'
                                }
                            }
                        ]
                    }
                }
      ]
    }
    **/
        console.log(options);
  });
}

xml2json的内部实现:

xml2json: function(xml) {
    var obj = {
        childNodes: []
    };
    xml.attributes("*").each(function(attr) {
        obj[attr.localName()] = attr.toString();
    });
    if (xml.hasComplexContent()) {
        xml.children().each(function(child) {
            obj.childNodes.push({
                name: child.localName(),
                value: xu.xml2json(child)
            })
        });
    } else {
        if (!obj.value) {
            obj.value = this.text(xml);
        }
    }
    return obj;
}

Bugs & Feedback

Please feel free to report bugs or feature requests. You can send me private message on [github], or send me an email to: [colorhook@gmail.com]

License

bee is free to use under MIT license.

npm loves you