zhoupeng 1 рік тому
батько
коміт
8801c73b39

+ 5 - 0
airPlay/packages/excel-killer/1.txt

@@ -0,0 +1,5 @@
+id	fruit	cost	num
+编号	水果	价格	数量
+1	香蕉	1	5
+2	苹果	1	6
+3	草莓	1	7

+ 142 - 0
airPlay/packages/excel-killer/README.md

@@ -0,0 +1,142 @@
+# excel-killer
+## 插件说明:
+插件特色:
+- 插件自动监测excel文件内容变化
+- 自动容错处理
+- excel转json
+- excel转js
+- excel转lua(后续支持)
+- excel转csv(后续支持)
+
+![](../../doc/excel-killer/scene.png)
+## 插件打开方式
+- 菜单:扩展/excel-killer
+- 快捷键: Ctrl+Shift+J
+## 格式转换说明
+#### excel
+##### 支持的格式:
+- *.xlsx, *.xls
+- [示例excel](../../doc/excel-killer/test.xlsx)
+##### 内容格式如下
+- 第1行: 字段的索引key,这个是不能重复的,想必这个肯定是常识吧!
+- 第2行: 字段的中文注释
+- 尽量不要出现空Sheet,当然插件会自动跳过该Sheet
+- 尽量不要出现空行,空单元格,当然插件也做了这方面的优化,空单元格统一处理为空字符串,空行直接跳过
+##### 水果sheet:fruit
+|id| fruit        | cost      |   num    |
+|:----: | :----:    | -----:  | :----: |
+|编号| 水果        | 价格    |  数量  |
+|1| 香蕉        | 1      |   5    |
+|2| 苹果        | 1      |   6    |
+|3| 草莓        | 1      |   7    |
+##### 人类sheet:man
+|id| name        | age         |
+|:----: | :----:    | -----:    |
+|编号| 名字        | 年龄       |
+|1| 小明       | 10           |
+|2| 小红        | 20         |
+|3| 小蓝        | 30          |
+
+
+#### 转换的js代码为
+```javascript
+module.export = {
+        fruit: {
+            1: {fruit: "香蕉", cost: 1, num: 5},
+            2: {fruit: "苹果", cost: 1, num: 6},
+            3: {fruit: "草莓", cost: 1, num: 7}
+        },
+        man: {
+            1: {name: "小明", age: 10},
+            2: {name: "小红", age: 20},
+            3: {name: "小蓝", age: 30},
+        }
+    }
+```
+#### 转换的json文件为:
+- 未合并的json
+> fruit.json
+```json
+{
+    "1": {"fruit": "香蕉", "cost": 1, "num": 5},
+    "2": {"fruit": "苹果", "cost": 1, "num": 6},
+    "3": {"fruit": "草莓", "cost": 1, "num": 7}
+}
+```
+> man.json
+```json
+{
+    "1": {"name": "小明", "age": 10},
+    "2": {"name": "小红", "age": 20},
+    "3": {"name": "小蓝", "age": 30}
+}
+```
+- 合并后的json
+```json
+{
+    "fruit": {
+        "1": {"fruit": "香蕉", "cost": 1, "num": 5},
+        "2": {"fruit": "苹果", "cost": 1, "num": 6},
+        "3": {"fruit": "草莓", "cost": 1, "num": 7}
+    },
+    "man": {
+        "1": {"name": "小明", "age": 10},
+        "2": {"name": "小红", "age": 20},
+        "3": {"name": "小蓝", "age": 30}
+    }
+}
+```
+
+
+## 使用说明:
+### 配置-json
+#### json存放路径:
+> 插件自动指定路径,不能手动指定,生成的json配置会统一存放在该目录下
+
+#### json格式化:
+> 勾选该选项,生成的json文件将会格式化之后输出,例如:
+```json
+{
+    "test":100
+}
+```
+>未勾选该选项,生成的json将会是一行,例如:
+```json
+{"test":100}
+```
+从上边观察可以看出:
+- 格式化后的json更容易查阅,但是文件体积比较大,适合开发的时候使用
+- 未格式化的json文件体积比较小,但是不容易查阅,适合项目发布的时候使用
+#### 合并所有Json:
+- 未勾选该选项,每个excel的sheet会生成一个对应的json配置,因此,需要保证sheet不能出现重名
+- 勾选该选项,所有的json配置将合并为一个json,因此,需要手动指定json配置的文件名
+
+### 配置-JavaScript
+#### js存放路径:
+> 插件自动指定路径,不能手动指定,生成的js配置会统一存放在该目录下
+#### js配置文件名
+> 生成的js配置的文件的名字
+#### 代码格式化
+> 勾选该选项,生成的js文件将会格式化之后输出,例如:
+```javascript
+module.exports={
+    test:"100"
+}
+```
+> 未勾选该选项,生成的js文件将会是一行,例如:
+```javascript
+module.exports={test:"100"}
+```
+是否使用该选项,和上边的同理,视情况而定.
+### 配置-Excel
+### Excel文件路径:
+> 需要手动指定自己的excel所存放的目录,插件会识别出来目录下的所有excle文件,因此允许目录嵌套的方式存放excel
+### Excel列表
+- 列表中罗列出来了目录下的所有excel的sheet
+- 标题右侧 **sheet[x] excel[y]** 的意思是,目录下sheet一共x个,excel文件一共y个
+- 每个sheet列表条目都有一个选中项,如果勾选,则会加入到生成队列中,反之不会被生成,默认全部选中
+
+## 插件反馈
+- 详细的说明文档:点击插件的[帮助按钮](https://github.com/tidys/CocosCreatorPlugins/tree/master/packages/excel-killer/README.md)直达
+- 如果使用过程中遇到任何问题,欢迎点击[QQ交谈](http://wpa.qq.com/msgrd?v=3&uin=774177933&site=qq&menu=yes)给我留言
+

+ 60 - 0
airPlay/packages/excel-killer/core/CfgUtil.js

@@ -0,0 +1,60 @@
+let fs = require('fire-fs');
+let path = require('fire-path');
+let electron = require('electron');
+module.exports = {
+    cfgData: {
+        excelRootPath: null,// excel根路径
+        jsFileName: null,
+        jsonAllFileName: null,
+        isMergeJson: null,
+        isFormatJsCode: null,
+        isFormatJson: null,
+    },
+    initCfg(cb) {
+        let configFilePath = this._getAppCfgPath();
+        let b = fs.existsSync(configFilePath);
+        if (b) {
+            console.log("cfg path: " + configFilePath);
+            fs.readFile(configFilePath, 'utf-8', function (err, data) {
+                if (!err) {
+                    let saveData = JSON.parse(data.toString());
+                    self.cfgData = saveData;
+                    if (cb) {
+                        cb(saveData);
+                    }
+                }
+            }.bind(self));
+        } else {
+            if (cb) {
+                cb(null);
+            }
+        }
+    },
+    saveCfgByData(data) {
+        this.cfgData.excelRootPath = data.excelRootPath;
+        this.cfgData.jsFileName = data.jsFileName;
+        this.cfgData.jsonAllFileName = data.jsonAllFileName;
+        this.cfgData.isMergeJson = data.isMergeJson;
+        this.cfgData.isFormatJsCode = data.isFormatJsCode;
+        this.cfgData.isFormatJson = data.isFormatJson;
+        this._save();
+    },
+    _save() {
+        let savePath = this._getAppCfgPath();
+        fs.writeFileSync(savePath, JSON.stringify(this.cfgData));
+        console.log("save ok!");
+    },
+    _getAppCfgPath() {
+        let userDataPath = null;
+        if (electron.remote) {
+            userDataPath = electron.remote.app.getPath('userData');
+        } else {
+            userDataPath = electron.app.getPath('userData');
+        }
+        let tar = Editor.libraryPath;
+        tar = tar.replace(/\\/g, '-');
+        tar = tar.replace(/:/g, '-');
+        tar = tar.replace(/\//g, '-');
+        return path.join(userDataPath, "excel-fucker-" + tar + ".json");
+    },
+};

+ 0 - 0
airPlay/packages/excel-killer/core/excel.js


+ 1053 - 0
airPlay/packages/excel-killer/editor.d.ts

@@ -0,0 +1,1053 @@
+/**
+ * Cocos Creator 编辑器模块
+ * @author 陈皮皮(ifaswind)
+ * @version 20210312
+ * @see https://gitee.com/ifaswind/eazax-ccc/blob/master/declarations/editor.d.ts
+ */
+declare module Editor {
+
+    /**
+     * Log the normal message and show on the console. The method will send ipc message editor:console-log to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    function log(...args: any): void;
+
+    /**
+     * Log the normal message and show on the console. The method will send ipc message editor:console-log to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    function info(...args: any): void;
+
+    /**
+     * Log the warnning message and show on the console, it also shows the call stack start from the function call it. The method will send ipc message editor:console-warn to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    function warn(...args: any): void;
+
+    /**
+     * Log the error message and show on the console, it also shows the call stack start from the function call it. The method will sends ipc message editor:console-error to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    function error(...args: any): void;
+
+    /**
+     * Log the success message and show on the console The method will send ipc message editor:console-success to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    function success(...args: any): void;
+
+    /**
+     * Require the module by Editor.url. This is good for module exists in package, since the absolute path of package may be variant in different machine.
+     * @param url 
+     */
+    function require(url: string): any;
+
+    /**
+     * Returns the file path (if it is registered in custom protocol) or url (if it is a known public protocol).
+     * @param url 
+     * @param encode 
+     */
+    function url(url: string, encode?: string): string;
+
+    function T(key: string): string;
+
+}
+
+declare module Editor {
+    readonly let appPath: string;
+    readonly let frameworkPath: string;
+    readonly let importPath: string;
+    readonly let isWin32: boolean;
+    readonly let isDarwin: boolean;
+    readonly let lang: string;
+    readonly let libraryPath: string;
+    readonly let sceneScripts: { [packageName: string]: string };
+}
+
+declare module Editor {
+
+    /**
+     * 渲染进程
+     */
+    module RendererProcess {
+
+        /**
+        * AssetDB singleton class in renderer process, you can access the instance with `Editor.assetdb`.
+        */
+        class AssetDB {
+
+            /**
+             * The remote AssetDB instance of main process, same as `Editor.remote.assetdb`.
+             */
+            readonly remote: Remote;
+
+            /**
+             * The library path.
+             */
+            readonly library: string;
+
+            /**
+             * Reveal given url in native file system.
+             * @param url 
+             */
+            explore(url: string): string;
+
+            /**
+             * Reveal given url's library file in native file system.
+             * @param url 
+             */
+            exploreLib(url: string): string;
+
+            /**
+             * Get native file path by url.
+             * @param url 
+             * @param cb The callback function.
+             */
+            queryPathByUrl(url: string, cb?: (err: any, path: any) => void): void;
+
+            /**
+             * Get uuid by url.
+             * @param url 
+             * @param cb The callback function.
+             */
+            queryUuidByUrl(url: string, cb?: (err: any, uuid: any) => void): void;
+
+            /**
+             * Get native file path by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryPathByUuid(uuid: string, cb?: (err: any, path: any) => void): void;
+
+            /**
+             * Get asset url by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryUrlByUuid(uuid: string, cb?: (err: any, url: any) => void): void;
+
+            /**
+             * Get asset info by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryInfoByUuid(uuid: string, cb?: (err: any, info: any) => void): void;
+
+            /**
+             * Get meta info by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryMetaInfoByUuid(uuid: string, cb?: (err: any, info: any) => void): void;
+
+            /**
+             * Query all assets from asset-db.
+             * @param cb The callback function.
+             */
+            deepQuery(cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Query assets by url pattern and asset-type.
+             * @param pattern The url pattern.
+             * @param assetTypes The asset type(s).
+             * @param cb The callback function.
+             */
+            queryAssets(pattern: string, assetTypes: string | string[], cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Import files outside asset-db to specific url folder. 
+             * @param rawfiles Rawfile path list.
+             * @param destUrl The url of dest folder.
+             * @param showProgress Show progress or not.
+             * @param cb The callbak function.
+             */
+            import(rawfiles: string[], destUrl: string, showProgress?: boolean, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Create asset in specific url by sending string data to it.
+             * @param uuid 
+             * @param metaJson 
+             * @param cb the callback function.
+             */
+            create(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Move asset from src to dest.
+             * @param srcUrl 
+             * @param destUrl 
+             * @param showMessageBox 
+             */
+            move(srcUrl: string, destUrl: string, showMessageBox?: boolean): void;
+
+            /**
+             * Delete assets by url list.
+             * @param urls 
+             */
+            delete(urls: string[]): void;
+
+            /**
+             * Save specific asset by sending string data.
+             * @param url 
+             * @param data 
+             * @param cb the callback function.
+             */
+            saveExists(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Create or save assets by sending string data. If the url is already existed, it will be changed with new data. The behavior is same with method saveExists. Otherwise, a new asset will be created. The behavior is same with method create.
+             * @param url 
+             * @param data 
+             * @param cb the callback function.
+             */
+            createOrSave(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Save specific meta by sending meta's json string.
+             * @param uuid 
+             * @param metaJson 
+             * @param cb the callback function.
+             */
+            saveMeta(uuid: string, metaJson: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Refresh the assets in url, and return the results.
+             * @param url 
+             * @param cb 
+             */
+            refresh(url: string, cb?: (err: any, results: any[]) => void): void;
+
+        }
+
+    }
+
+    /**
+     * 主进程
+     */
+    module MainProcess {
+
+        /**
+         * AssetDB singleton class in main process, you can access the instance with `Editor.assetdb`.
+         */
+        class AssetDB {
+
+            /**
+             * Return uuid by url. If uuid not found, it will return null.
+             * @param url 
+             */
+            urlToUuid(url: string): string;
+
+            /**
+             * Return uuid by file path. If uuid not found, it will return null.
+             * @param fspath 
+             */
+            fspathToUuid(fspath: string): string;
+
+            /**
+             * Return file path by uuid. If file path not found, it will return null.
+             * @param url 
+             */
+            uuidToFspath(url: string): string;
+
+            /**
+             * Return url by uuid. If url not found, it will return null.
+             * @param uuid 
+             */
+            uuidToUrl(uuid: string): string;
+
+            /**
+             * Return url by file path. If file path not found, it will return null.
+             * @param fspath 
+             */
+            fspathToUrl(fspath: string): string;
+
+            /**
+             * Return file path by url. If url not found, it will return null.
+             * @param url 
+             */
+            urlToFspath(url: string): string;
+
+            /**
+             * Check existance by url.
+             * @param url 
+             */
+            exists(url: string): string;
+
+            /**
+             * Check existance by uuid.
+             * @param uuid 
+             */
+            existsByUuid(uuid: string): string;
+
+            /**
+             * Check existance by path.
+             * @param fspath 
+             */
+            existsByPath(fspath: string): string;
+
+            /**
+             * Check whether asset for a given url is a sub asset.
+             * @param url 
+             */
+            isSubAsset(url: string): boolean;
+
+            /**
+             * Check whether asset for a given uuid is a sub asset.
+             * @param uuid 
+             */
+            isSubAssetByUuid(uuid: string): boolean;
+
+            /**
+             * Check whether asset for a given path is a sub asset.
+             * @param fspath 
+             */
+            isSubAssetByPath(fspath: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given url.
+             * @param url
+             */
+            containsSubAssets(url: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given uuid.
+             * @param uuid
+             */
+            containsSubAssetsByUuid(uuid: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given path.
+             * @param fspath 
+             */
+            containsSubAssetsByPath(fspath: string): boolean;
+
+            /**
+             * Return asset info by a given url.
+             * @param url 
+             */
+            assetInfo(url: string): AssetInfo;
+
+            /**
+             * Return asset info by a given uuid.
+             * @param uuid 
+             */
+            assetInfoByUuid(uuid: string): AssetInfo;
+
+            /**
+             * Return asset info by a given file path.
+             * @param fspath 
+             */
+            assetInfoByPath(fspath: string): AssetInfo;
+
+            /**
+             * Return all sub assets info by url if the url contains sub assets.
+             * @param url 
+             */
+            subAssetInfos(url: string): AssetInfo[];
+
+            /**
+             * Return all sub assets info by uuid if the uuid contains sub assets.
+             * @param uuid 
+             */
+            subAssetInfosByUuid(uuid: string): AssetInfo[];
+
+            /**
+             * Return all sub assets info by path if the path contains sub assets.
+             * @param fspath 
+             */
+            subAssetInfosByPath(fspath: string): AssetInfo[];
+
+            /**
+             * Return meta instance by a given url.
+             * @param url 
+             */
+            loadMeta(url: string): MetaBase;
+
+            /**
+             * Return meta instance by a given uuid.
+             * @param uuid 
+             */
+            loadMetaByUuid(uuid: string): MetaBase;
+
+            /**
+             * Return meta instance by a given path.
+             * @param fspath 
+             */
+            loadMetaByPath(fspath: string): MetaBase;
+
+            /**
+             * Return whether a given url is reference to a mount.
+             * @param url 
+             */
+            isMount(url: string): boolean;
+
+            /**
+             * Return whether a given path is reference to a mount.
+             * @param fspath 
+             */
+            isMountByPath(fspath: string): boolean;
+
+            /**
+             * Return whether a given uuid is reference to a mount.
+             * @param uuid 
+             */
+            isMountByUuid(uuid: string): boolean;
+
+            /**
+             * Return mount info by url.
+             * @param url 
+             */
+            mountInfo(url: string): MountInfo;
+
+            /**
+             * Return mount info by uuid.
+             * @param uuid 
+             */
+            mountInfoByUuid(uuid: string): MountInfo;
+
+            /**
+             * Return mount info by path.
+             * @param fspath 
+             */
+            mountInfoByPath(fspath: string): MountInfo;
+
+            /**
+             * Mount a directory to assetdb, and give it a name. If you don't provide a name, it will mount to root.
+             * @param path file system path.
+             * @param mountPath the mount path (relative path).
+             * @param opts options.
+             * @param opts.hide if the mount hide in assets browser.
+             * @param opts.virtual if this is a virtual mount point.
+             * @param opts.icon icon for the mount.
+             * @param cb a callback function.
+             * @example Editor.assetdb.mount('path/to/mount', 'assets', function (err) {
+                            // mounted, do something ...
+                        });
+             */
+            mount(path: string, mountPath: string, opts: { hide: object, vitural: object, icon: object }, cb?: (err: any) => void): void;
+
+            /**
+             * Attach the specified mount path.
+             * @param mountPath the mount path (relative path).
+             * @param cb a callback function.
+             * @example Editor.assetdb.attachMountPath('assets', function (err, results) {
+                            // mount path attached, do something ...
+                            // results are the assets created
+                        });
+             */
+            attachMountPath(mountPath: string, cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Unattach the specified mount path.
+             * @param mountPath the mount path (relative path).
+             * @param cb a callback function.
+             * @example Editor.assetdb.unattachMountPath('assets', function (err, results) {
+                            // mount path unattached, do something ...
+                            // results are the assets deleted
+                        });
+             */
+            unattachMountPath(mountPath: string, cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Unmount by name.
+             * @param mountPath the mount path.
+             * @param cb a callback function.
+             * @example Editor.assetdb.unmount('assets', function (err) {
+                            // unmounted, do something ...
+                        });
+             */
+            unmount(mountPath: string, cb?: (err: any) => void): void;
+
+            /**
+             * Init assetdb, it will scan the mounted directories, and import unimported assets.
+             * @param cb a callback function.
+             * @example Editor.assetdb.init(function (err, results) {
+                            // assets that imported during init
+                            results.forEach(function (result) {
+                                // result.uuid
+                                // result.parentUuid
+                                // result.url
+                                // result.path
+                                // result.type
+                            });
+                        });
+             */
+            init(cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Refresh the assets in url, and return the results.
+             * @param url 
+             * @param cb 
+             */
+            refresh(url: string, cb?: Function): void;
+
+            /**
+             * deepQuery
+             * @param cb 
+             * @example Editor.assetdb.deepQuery(function (err, results) {
+                          results.forEach(function (result) {
+                            // result.name
+                            // result.extname
+                            // result.uuid
+                            // result.type
+                            // result.isSubAsset
+                            // result.children - the array of children result
+                          });
+                        });
+             */
+            deepQuery(cb?: Function): void;
+
+            /**
+             * queryAssets
+             * @param pattern The url pattern.
+             * @param assetTypes The asset type(s).
+             * @param cb The callback function.
+             */
+            queryAssets(pattern: string, assetTypes: string | string[], cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * queryMetas
+             * @param pattern The url pattern.
+             * @param type The asset type.
+             * @param cb The callback function.
+             */
+            queryMetas(pattern: string, type: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * move
+             * @param srcUrl The url pattern.
+             * @param destUrl The asset type.
+             * @param cb The callback function.
+             */
+            move(srcUrl: string, destUrl: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * delete
+             * @param urls 
+             * @param cb 
+             */
+            delete(urls: string[], cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Create asset at url with data.
+             * @param url 
+             * @param data 
+             * @param cb 
+             */
+            create(url: string, data: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Save data to the exists asset at url.
+             * @param url 
+             * @param data 
+             * @param cb 
+             */
+            saveExists(url: string, data: string, cb?: (err: Error, meta: any) => void): void;
+
+            /**
+             * Import raw files to url
+             * @param rawfiles 
+             * @param url 
+             * @param cb 
+             */
+            import(rawfiles: string[], url: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Overwrite the meta by loading it through uuid.
+             * @param uuid 
+             * @param jsonString 
+             * @param cb 
+             */
+            saveMeta(uuid: string, jsonString: string, cb?: (err: Error, meta: any) => void): void;
+
+            /**
+             * Exchange uuid for two assets.
+             * @param urlA 
+             * @param urlB 
+             * @param cb 
+             */
+            exchangeUuid(urlA: string, urlB: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Clear imports.
+             * @param url 
+             * @param cb 
+             */
+            clearImports(url: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Register meta type.
+             * @param extname 
+             * @param folder Whether it's a folder type.
+             * @param metaCtor 
+             */
+            register(extname: string, folder: boolean, metaCtor: object): void;
+
+            /**
+             * Unregister meta type.
+             * @param metaCtor 
+             */
+            unregister(metaCtor: object): void;
+
+            /**
+             * Get the relative path from mount path to the asset by fspath.
+             * @param fspath  
+             */
+            getRelativePath(fspath: string): string;
+
+            /**
+             * Get the backup file path of asset file.
+             * @param filePath 
+             */
+            getAssetBackupPath(filePath: string): string;
+
+        }
+
+    }
+
+    interface MetaBase {
+        ver: string;
+        uuid: string;
+    }
+
+    interface MountInfo {
+        path: string;
+        name: string;
+        type: string;
+    }
+
+    interface Metas {
+        asset: string[];
+        folder: string[];
+        mount: string[];
+        'custom-asset': string[];
+        'native-asset': string[];
+        'animation-clip': string[];
+        'audio-clip': string[];
+        'bitmap-font': string[];
+    }
+
+    interface App {
+        readonly home: string;
+        readonly name: string;
+        readonly path: string;
+        readonly version: string;
+    }
+
+    class Remote {
+        readonly App: App;
+        readonly isClosing: boolean;
+        readonly lang: string;
+        readonly isNode: boolean;
+        readonly isElectron: boolean;
+        readonly isNative: boolean;
+        readonly isPureWeb: boolean;
+        readonly isRendererProcess: boolean;
+        readonly isMainProcess: boolean;
+        readonly isDarwin: boolean;
+        readonly isWin32: boolean;
+        readonly isRetina: boolean;
+        readonly frameworkPath: string;
+        readonly dev: boolean;
+        readonly logfile: string;
+        readonly themePaths: string[];
+        readonly theme: string;
+        readonly showInternalMount: boolean;
+        readonly metas: Metas;
+        readonly metaBackupPath: string;
+        readonly assetBackupPath: string;
+        readonly libraryPath: string;
+        readonly importPath: string;
+        readonly externalMounts: any;
+        readonly mountsWritable: string;
+        readonly assetdb: MainProcess.AssetDB;
+        readonly assetdbInited: boolean;
+        readonly sceneList: string[];
+        readonly versions: {
+            'asset-db': string;
+            CocosCreator: string;
+            cocos2d: string;
+            'editor-framework': string;
+        }
+    }
+
+    /** Remote 实例 */
+    const remote: Remote;
+
+    /** AssetDB 实例 */
+    const assetdb: MainProcess.AssetDB;
+
+}
+
+interface AssetInfo {
+    uuid?: string;
+    path?: string;
+    url?: string;
+    type?: string;
+    isSubAsset?: boolean;
+    assetType?: string;
+    id?: string;
+    name?: string;
+    subAssetTypes?: string;
+}
+
+declare module Editor.Project {
+    readonly let id: string;
+    readonly let name: string;
+    /** Absolute path for current open project. */
+    readonly let path: string;
+}
+
+declare module Editor.Builder {
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    function on(eventName: string, callback: (options: BuildOptions, cb: Function) => void): void;
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    function once(eventName: string, callback: (options: BuildOptions, cb: Function) => void): void;
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    function removeListener(eventName: string, callback: Function): void;
+
+}
+
+declare module Editor.Scene {
+
+    /**
+     * 
+     * @param packageName 
+     * @param method 
+     * @param cb 
+     */
+    function callSceneScript(packageName: string, method: string, cb: (err: Error, msg: any) => void): void;
+
+}
+
+declare module Editor.Panel {
+
+    /**
+     * Open a panel via panelID.
+     * @param panelID The panel ID
+     * @param argv 
+     */
+    function open(panelID: string, argv?: object): void;
+
+    /**
+     * Close a panel via panelID.
+     * @param panelID The panel ID
+     */
+    function close(panelID: string): void;
+
+    /**
+     * Find panel frame via panelID.
+     * @param panelID The panel ID
+     */
+    function find(panelID: string): void;
+
+    /**
+     * Extends a panel.
+     * @param proto 
+     */
+    function extend(proto: object): void;
+
+}
+
+declare module Editor.Selection {
+
+    /**
+     * Select item with its id.
+     * @param type 
+     * @param id 
+     * @param unselectOthers 
+     * @param confirm 
+     */
+    function select(type: string, id: string, unselectOthers?: boolean, confirm?: boolean): void;
+
+    /**
+     * Unselect item with its id.
+     * @param type 
+     * @param id 
+     * @param confirm 
+     */
+    function unselect(type: string, id: string, confirm?: boolean): void;
+
+    /**
+     * Hover item with its id. If id is null, it means hover out.
+     * @param type 
+     * @param id 
+     */
+    function hover(type: string, id: string): string;
+
+    /**
+     * 
+     * @param type 
+     */
+    function clear(type: string): void;
+
+    /**
+     * 
+     * @param type 
+     */
+    function curActivate(type: string): string[];
+
+    /**
+     * 
+     * @param type 
+     */
+    function curGlobalActivate(type: string): string[];
+
+    /**
+     * 
+     * @param type 
+     */
+    function curSelection(type: string): string[];
+
+    /**
+     * 
+     * @param items 
+     * @param mode 'top-level', 'deep' and 'name'
+     * @param func 
+     */
+    function filter(items: string[], mode: string, func: Function): string[];
+
+}
+
+declare module Editor.Ipc {
+
+    /**
+     * Send message with ...args to main process asynchronously. It is possible to add a callback as the last or the 2nd last argument to receive replies from the IPC receiver.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param callback You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
+     * @param timeout You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
+     */
+    function sendToMain(message: string, ...args?: any, callback?: Function, timeout?: number): void;
+
+    /**
+     * Send message with ...args to panel defined in renderer process asynchronously. It is possible to add a callback as the last or the 2nd last argument to receive replies from the IPC receiver.
+     * @param panelID Panel ID.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param callback You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
+     * @param timeout You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
+     */
+    function sendToPanel(panelID: string, message: string, ...args?: any, callback?: Function, timeout?: number): void;
+
+    /**
+     * Send message with ...args to all opened window and to main process asynchronously.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param option You can indicate the last argument as an IPC option by Editor.Ipc.option({...}).
+     */
+    function sendToAll(message: string, ...args?: any, option?: object): void;
+
+    /**
+     * Send message with ...args to main process synchronized and return a result which is responded from main process.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     */
+    function sendToMainSync(message: string, ...args?: any): void;
+
+    /**
+     * Send message with ...args to main process by package name and the short name of the message.
+     * @param pkgName Package name.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     */
+    function sendToPackage(pkgName: string, message: string, ...args?: any): void;
+
+}
+
+declare module Editor.UI {
+
+    module Setting {
+
+        /**
+         * Control the default step for float point input element. Default is 0.1.
+         * @param value 
+         */
+        function stepFloat(value: number): void;
+
+        /**
+         * Control the default step for integer input element. Default is 1.
+         * @param value 
+         */
+        function stepInt(value: number): void;
+
+        /**
+         * Control the step when shift key press down. Default is 10.
+         * @param value 
+         */
+        function shiftStep(value: number): void;
+
+    }
+
+    module DragDrop {
+
+        readonly let dragging: boolean;
+
+        function start(e: any, t: any): void;
+
+        function end(): void;
+
+        function updateDropEffect(e: any, t: any);
+
+        function type(e: any);
+
+        function filterFiles(e: any);
+
+        function items(dataTransfer: DataTransfer): AssetInfo[];
+
+        function getDragIcon(e: any);
+
+        function options(e: any);
+
+        function getLength(e: any): number;
+
+    }
+
+}
+
+declare module Editor.GizmosUtils {
+
+    function addMoveHandles(e, n, t);
+
+    function getCenter(e);
+
+    function getCenterWorldPos(n);
+
+    function getCenterWorldPos3D(e);
+
+    function getRecursiveNodes(e, t);
+
+    function getRecursiveWorldBounds3D(e);
+
+    function getWorldBounds3D(n);
+
+    function snapPixel(e);
+
+    function snapPixelWihVec2(e);
+
+}
+
+declare module Editor.Utils {
+
+    /**
+     * Uuid 工具
+     */
+    module UuidUtils {
+
+        /**
+         * 压缩后的 uuid 可以减小保存时的尺寸,但不能做为文件名(因为无法区分大小写并且包含非法字符)。
+         * 默认将 uuid 的后面 27 位压缩成 18 位,前 5 位保留下来,方便调试。
+         * 如果启用 min 则将 uuid 的后面 30 位压缩成 20 位,前 2 位保留不变。
+         * @param uuid 
+         * @param min 
+         */
+        function compressUuid(uuid: string, min?: boolean): string;
+
+        function compressHex(hexString: string, reservedHeadLength?: number): string;
+
+        function decompressUuid(str: string): string;
+
+        function isUuid(str: string): boolean;
+
+        function uuid(): string;
+
+    }
+
+}
+
+declare interface BuildOptions {
+    actualPlatform: string;
+    android: { packageName: string };
+    'android-instant': {
+        REMOTE_SERVER_ROOT: string;
+        host: string;
+        packageName: string;
+        pathPattern: string;
+        recordPath: string;
+        scheme: string;
+        skipRecord: boolean;
+    }
+    apiLevel: string;
+    appABIs: string[];
+    appBundle: boolean;
+    buildPath: string;
+    buildScriptsOnly: boolean;
+    debug: string;
+    dest: string;
+    embedWebDebugger: boolean;
+    encryptJs: boolean;
+    excludeScenes: string[];
+    excludedModules: string[];
+    'fb-instant-games': object;
+    inlineSpriteFrames: boolean;
+    inlineSpriteFrames_native: boolean;
+    ios: { packageName: string };
+    mac: { packageName: string };
+    md5Cache: boolean;
+    mergeStartScene: boolean;
+    optimizeHotUpdate: boolean;
+    orientation: {
+        landscapeLeft: boolean;
+        landscapeRight: boolean;
+        portrait: boolean;
+        upsideDown: boolean;
+    };
+    packageName: string;
+    platform: string;
+    previewHeight: number;
+    previewWidth: number;
+    scenes: string[];
+    sourceMaps: boolean;
+    startScene: string;
+    template: string;
+    title: string;
+    useDebugKeystore: boolean;
+    vsVersion: string;
+    webOrientation: boolean;
+    win32: object;
+    xxteaKey: string;
+    zipCompressJs: string;
+    project: string;
+    projectName: string;
+    debugBuildWorker: boolean;
+    bundles: bundle[];
+}
+
+interface bundle {
+    /** bundle 的根目录 */
+    root: string;
+    /** bundle 的输出目录 */
+    dest: string;
+    /** 脚本的输出目录 */
+    scriptDest: string;
+    /** bundle 的名称 */
+    name: string;
+    /** bundle 的优先级 */
+    priority: number;
+    /** bundle 中包含的场景 */
+    scenes: string[];
+    /** bundle 的压缩类型 */
+    compressionType: 'subpackage' | 'normal' | 'none' | 'merge_all_json' | 'zip';
+    /** bundle 所构建出来的所有资源 */
+    buildResults: BuildResults;
+    /** bundle 的版本信息,由 config 生成 */
+    version: string;
+    /** bundle 的 config.json 文件 */
+    config: any;
+    /** bundle 是否是远程包 */
+    isRemote: boolean;
+}

+ 2 - 0
airPlay/packages/excel-killer/jsconfig.json

@@ -0,0 +1,2 @@
+{
+}

+ 27 - 0
airPlay/packages/excel-killer/main.js

@@ -0,0 +1,27 @@
+'use strict';
+
+module.exports = {
+  load () {
+    // execute when package loaded
+  },
+
+  unload () {
+    // execute when package unloaded
+  },
+
+  // register your ipc messages here
+  messages: {
+    'open' () {
+      // open entry panel registered in package.json
+      Editor.Panel.open('excel-killer');
+    },
+    'say-hello' () {
+      Editor.log('Hello World!');
+      // send ipc message to panel
+      Editor.Ipc.sendToPanel('excel-killer', 'excel-killer:hello');
+    },
+    'clicked' () {
+      Editor.log('Button clicked!');
+    }
+  },
+};

+ 1439 - 0
airPlay/packages/excel-killer/package-lock.json

@@ -0,0 +1,1439 @@
+{
+  "name": "excel-killer",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "adler-32": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
+      "integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=",
+      "requires": {
+        "exit-on-epipe": "1.0.1",
+        "printj": "1.1.2"
+      }
+    },
+    "anymatch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+      "requires": {
+        "micromatch": "3.1.9",
+        "normalize-path": "2.1.1"
+      }
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
+    },
+    "async-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
+    },
+    "atob": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz",
+      "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10="
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "requires": {
+        "cache-base": "1.0.1",
+        "class-utils": "0.3.6",
+        "component-emitter": "1.2.1",
+        "define-property": "1.0.0",
+        "isobject": "3.0.1",
+        "mixin-deep": "1.3.1",
+        "pascalcase": "0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        }
+      }
+    },
+    "binary-extensions": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
+      "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU="
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz",
+      "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==",
+      "requires": {
+        "arr-flatten": "1.1.0",
+        "array-unique": "0.3.2",
+        "define-property": "1.0.0",
+        "extend-shallow": "2.0.1",
+        "fill-range": "4.0.0",
+        "isobject": "3.0.1",
+        "kind-of": "6.0.2",
+        "repeat-element": "1.1.2",
+        "snapdragon": "0.8.2",
+        "snapdragon-node": "2.1.1",
+        "split-string": "3.1.0",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
+      }
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "requires": {
+        "collection-visit": "1.0.0",
+        "component-emitter": "1.2.1",
+        "get-value": "2.0.6",
+        "has-value": "1.0.0",
+        "isobject": "3.0.1",
+        "set-value": "2.0.0",
+        "to-object-path": "0.3.0",
+        "union-value": "1.0.0",
+        "unset-value": "1.0.0"
+      }
+    },
+    "cfb": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.0.5.tgz",
+      "integrity": "sha512-z1BN+JkopTE4vYu0sx25da2ZFurcN8gUKcBpT2ThCDlNFtWBozId8AHMs4OS7jTPLCJYK30Ud7QgcioyGkkkbg==",
+      "requires": {
+        "commander": "2.15.0",
+        "printj": "1.1.2"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.15.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.0.tgz",
+          "integrity": "sha512-7B1ilBwtYSbetCgTY1NJFg+gVpestg0fdA1MhC1Vs4ssyfSXnCAjFr+QcQM9/RedXC0EaUx1sG8Smgw2VfgKEg=="
+        }
+      }
+    },
+    "chokidar": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.2.tgz",
+      "integrity": "sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw==",
+      "requires": {
+        "anymatch": "2.0.0",
+        "async-each": "1.0.1",
+        "braces": "2.3.1",
+        "glob-parent": "3.1.0",
+        "inherits": "2.0.3",
+        "is-binary-path": "1.0.1",
+        "is-glob": "4.0.0",
+        "normalize-path": "2.1.1",
+        "path-is-absolute": "1.0.1",
+        "readdirp": "2.1.0",
+        "upath": "1.0.4"
+      }
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "requires": {
+        "arr-union": "3.1.0",
+        "define-property": "0.2.5",
+        "isobject": "3.0.1",
+        "static-extend": "0.1.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "requires": {
+            "is-accessor-descriptor": "0.1.6",
+            "is-data-descriptor": "0.1.4",
+            "kind-of": "5.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        }
+      }
+    },
+    "codepage": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.12.2.tgz",
+      "integrity": "sha512-FAN+oPs/ocaPLFvIt4vEOHgWA6UJ6t+fVbbVBoXDpTpC+4JYasomYZEEjR/Miph3qQrVnIShRwwmwu4P35JW1w==",
+      "requires": {
+        "commander": "2.14.1",
+        "exit-on-epipe": "1.0.1"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.14.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
+          "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+        }
+      }
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "requires": {
+        "map-visit": "1.0.0",
+        "object-visit": "1.0.1"
+      }
+    },
+    "commander": {
+      "version": "2.13.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
+      "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
+    },
+    "component-emitter": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "crc-32": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
+      "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
+      "requires": {
+        "exit-on-epipe": "1.0.1",
+        "printj": "1.1.2"
+      }
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "requires": {
+        "is-descriptor": "1.0.2",
+        "isobject": "3.0.1"
+      }
+    },
+    "exit-on-epipe": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+      "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "requires": {
+        "debug": "2.6.9",
+        "define-property": "0.2.5",
+        "extend-shallow": "2.0.1",
+        "posix-character-classes": "0.1.1",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "requires": {
+            "is-accessor-descriptor": "0.1.6",
+            "is-data-descriptor": "0.1.4",
+            "kind-of": "5.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        }
+      }
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "requires": {
+        "assign-symbols": "1.0.0",
+        "is-extendable": "1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "requires": {
+            "is-plain-object": "2.0.4"
+          }
+        }
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "requires": {
+        "array-unique": "0.3.2",
+        "define-property": "1.0.0",
+        "expand-brackets": "2.1.4",
+        "extend-shallow": "2.0.1",
+        "fragment-cache": "0.2.1",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
+      }
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "requires": {
+        "extend-shallow": "2.0.1",
+        "is-number": "3.0.0",
+        "repeat-string": "1.6.1",
+        "to-regex-range": "2.1.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
+      }
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
+    },
+    "frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "requires": {
+        "map-cache": "0.2.2"
+      }
+    },
+    "fs-extra": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
+      "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "jsonfile": "4.0.0",
+        "universalify": "0.1.1"
+      }
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
+    },
+    "glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "requires": {
+        "is-glob": "3.1.0",
+        "path-dirname": "1.0.2"
+      },
+      "dependencies": {
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "requires": {
+            "is-extglob": "2.1.1"
+          }
+        }
+      }
+    },
+    "graceful-fs": {
+      "version": "4.1.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "requires": {
+        "get-value": "2.0.6",
+        "has-values": "1.0.0",
+        "isobject": "3.0.1"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "requires": {
+        "is-number": "3.0.0",
+        "kind-of": "4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "is-accessor-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+      "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+      "requires": {
+        "kind-of": "6.0.2"
+      }
+    },
+    "is-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "requires": {
+        "binary-extensions": "1.11.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-data-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+      "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+      "requires": {
+        "kind-of": "6.0.2"
+      }
+    },
+    "is-descriptor": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+      "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+      "requires": {
+        "is-accessor-descriptor": "1.0.0",
+        "is-data-descriptor": "1.0.0",
+        "kind-of": "6.0.2"
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+    },
+    "is-glob": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+      "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+      "requires": {
+        "is-extglob": "2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "requires": {
+        "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "is-odd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz",
+      "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==",
+      "requires": {
+        "is-number": "4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="
+        }
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "requires": {
+        "isobject": "3.0.1"
+      }
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
+    },
+    "json-beautifully": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/json-beautifully/-/json-beautifully-1.0.3.tgz",
+      "integrity": "sha512-aD3uL3qr5y29B8w6cdmsoXaX3zp53ulWjgVQUgGwLmpAoXaB9jIeHYmpOLA9vRMvrIlfG6MQ7OArYKPS0SwMmw=="
+    },
+    "jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+      "requires": {
+        "graceful-fs": "4.1.11"
+      }
+    },
+    "kind-of": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "requires": {
+        "object-visit": "1.0.1"
+      }
+    },
+    "micromatch": {
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz",
+      "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==",
+      "requires": {
+        "arr-diff": "4.0.0",
+        "array-unique": "0.3.2",
+        "braces": "2.3.1",
+        "define-property": "2.0.2",
+        "extend-shallow": "3.0.2",
+        "extglob": "2.0.4",
+        "fragment-cache": "0.2.1",
+        "kind-of": "6.0.2",
+        "nanomatch": "1.2.9",
+        "object.pick": "1.3.0",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      }
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "1.1.11"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+      "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+      "requires": {
+        "for-in": "1.0.2",
+        "is-extendable": "1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "requires": {
+            "is-plain-object": "2.0.4"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "nanomatch": {
+      "version": "1.2.9",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz",
+      "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==",
+      "requires": {
+        "arr-diff": "4.0.0",
+        "array-unique": "0.3.2",
+        "define-property": "2.0.2",
+        "extend-shallow": "3.0.2",
+        "fragment-cache": "0.2.1",
+        "is-odd": "2.0.0",
+        "is-windows": "1.0.2",
+        "kind-of": "6.0.2",
+        "object.pick": "1.3.0",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
+      }
+    },
+    "node-xlsx": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/node-xlsx/-/node-xlsx-0.11.2.tgz",
+      "integrity": "sha512-EVKysbKISk0mWzYLq1kED/V/SEEjlMrdyyBN8xu9gilEeYvHX0G1NrvQU+CyYHxUeMh+stuPNhjwUdBuyyYIZw==",
+      "requires": {
+        "xlsx": "0.11.19"
+      }
+    },
+    "normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "requires": {
+        "remove-trailing-separator": "1.1.0"
+      }
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "requires": {
+        "copy-descriptor": "0.1.1",
+        "define-property": "0.2.5",
+        "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "requires": {
+            "kind-of": "3.2.2"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "requires": {
+            "kind-of": "3.2.2"
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "requires": {
+            "is-accessor-descriptor": "0.1.6",
+            "is-data-descriptor": "0.1.4",
+            "kind-of": "5.1.0"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "5.1.0",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+            }
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "requires": {
+        "isobject": "3.0.1"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "requires": {
+        "isobject": "3.0.1"
+      }
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
+    },
+    "printj": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
+      "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
+    },
+    "process-nextick-args": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+    },
+    "readable-stream": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
+      "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+      "requires": {
+        "core-util-is": "1.0.2",
+        "inherits": "2.0.3",
+        "isarray": "1.0.0",
+        "process-nextick-args": "2.0.0",
+        "safe-buffer": "5.1.1",
+        "string_decoder": "1.0.3",
+        "util-deprecate": "1.0.2"
+      }
+    },
+    "readdirp": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
+      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+      "requires": {
+        "graceful-fs": "4.1.11",
+        "minimatch": "3.0.4",
+        "readable-stream": "2.3.5",
+        "set-immediate-shim": "1.0.1"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "requires": {
+        "extend-shallow": "3.0.2",
+        "safe-regex": "1.1.0"
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+    },
+    "repeat-element": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
+      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo="
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "requires": {
+        "ret": "0.1.15"
+      }
+    },
+    "set-immediate-shim": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+    },
+    "set-value": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+      "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+      "requires": {
+        "extend-shallow": "2.0.1",
+        "is-extendable": "0.1.1",
+        "is-plain-object": "2.0.4",
+        "split-string": "3.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        }
+      }
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "requires": {
+        "base": "0.11.2",
+        "debug": "2.6.9",
+        "define-property": "0.2.5",
+        "extend-shallow": "2.0.1",
+        "map-cache": "0.2.2",
+        "source-map": "0.5.7",
+        "source-map-resolve": "0.5.1",
+        "use": "3.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "requires": {
+            "is-accessor-descriptor": "0.1.6",
+            "is-data-descriptor": "0.1.4",
+            "kind-of": "5.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "requires": {
+        "define-property": "1.0.0",
+        "isobject": "3.0.1",
+        "snapdragon-util": "3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "1.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "requires": {
+        "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-resolve": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz",
+      "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==",
+      "requires": {
+        "atob": "2.0.3",
+        "decode-uri-component": "0.2.0",
+        "resolve-url": "0.2.1",
+        "source-map-url": "0.4.0",
+        "urix": "0.1.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "requires": {
+        "extend-shallow": "3.0.2"
+      }
+    },
+    "ssf": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.2.tgz",
+      "integrity": "sha512-rDhAPm9WyIsY8eZEKyE8Qsotb3j/wBdvMWBUsOhJdfhKGLfQidRjiBUV0y/MkyCLiXQ38FG6LWW/VYUtqlIDZQ==",
+      "requires": {
+        "frac": "1.1.2"
+      }
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "requires": {
+        "define-property": "0.2.5",
+        "object-copy": "0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "0.1.6"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "requires": {
+            "kind-of": "3.2.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "requires": {
+                "is-buffer": "1.1.6"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "requires": {
+            "is-accessor-descriptor": "0.1.6",
+            "is-data-descriptor": "0.1.4",
+            "kind-of": "5.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        }
+      }
+    },
+    "string_decoder": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "requires": {
+        "kind-of": "3.2.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "1.1.6"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "requires": {
+        "define-property": "2.0.2",
+        "extend-shallow": "3.0.2",
+        "regex-not": "1.0.2",
+        "safe-regex": "1.1.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "requires": {
+        "is-number": "3.0.0",
+        "repeat-string": "1.6.1"
+      }
+    },
+    "uglify-js": {
+      "version": "3.3.14",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.14.tgz",
+      "integrity": "sha512-OY8VPQU25q09gQRbC+Ekk3xgEVBmYFEfVcgS47ksjTiNht2LmLlUkWutyi38ZsDSToJHwbe76kDGwmD226Z2Fg==",
+      "requires": {
+        "commander": "2.14.1",
+        "source-map": "0.6.1"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.14.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
+          "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+        }
+      }
+    },
+    "union-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+      "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+      "requires": {
+        "arr-union": "3.1.0",
+        "get-value": "2.0.6",
+        "is-extendable": "0.1.1",
+        "set-value": "0.4.3"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "0.1.1"
+          }
+        },
+        "set-value": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+          "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+          "requires": {
+            "extend-shallow": "2.0.1",
+            "is-extendable": "0.1.1",
+            "is-plain-object": "2.0.4",
+            "to-object-path": "0.3.0"
+          }
+        }
+      }
+    },
+    "universalify": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
+      "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "requires": {
+        "has-value": "0.3.1",
+        "isobject": "3.0.1"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "requires": {
+            "get-value": "2.0.6",
+            "has-values": "0.1.4",
+            "isobject": "2.1.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
+        }
+      }
+    },
+    "upath": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.4.tgz",
+      "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw=="
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
+    },
+    "use": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz",
+      "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==",
+      "requires": {
+        "kind-of": "6.0.2"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "xlsx": {
+      "version": "0.11.19",
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.11.19.tgz",
+      "integrity": "sha512-UTfD64o5Ka/E6QHL12fzcq5wnt9MCtuwgoUdYSTDxjjDkhNmZwSfPlJH/+Yh8vE6nU/0ax3MXNrc9AP4haAmIg==",
+      "requires": {
+        "adler-32": "1.2.0",
+        "cfb": "1.0.5",
+        "codepage": "1.12.2",
+        "commander": "2.13.0",
+        "crc-32": "1.2.0",
+        "exit-on-epipe": "1.0.1",
+        "ssf": "0.10.2"
+      }
+    }
+  }
+}

+ 29 - 0
airPlay/packages/excel-killer/package.json

@@ -0,0 +1,29 @@
+{
+  "name": "excel-killer",
+  "version": "0.0.1",
+  "description": "The package template for getting started.",
+  "author": "Cocos Creator",
+  "main": "main.js",
+  "main-menu": {
+    "i18n:MAIN_MENU.package.title/excel-killer": {
+      "accelerator": "CmdOrCtrl+Shift+j",
+      "message": "excel-killer:open"
+    }
+  },
+  "panel": {
+    "main": "panel/index.js",
+    "type": "dockable",
+    "title": "excel-killer",
+    "width": 500,
+    "height": 700,
+    "min-height":700,
+    "min-width":500
+  },
+  "dependencies": {
+    "chokidar": "^2.0.2",
+    "fs-extra": "^5.0.0",
+    "json-beautifully": "^1.0.3",
+    "node-xlsx": "^0.11.2",
+    "uglify-js": "^3.3.14"
+  }
+}

+ 0 - 0
airPlay/packages/excel-killer/panel/index.css


+ 101 - 0
airPlay/packages/excel-killer/panel/index.html

@@ -0,0 +1,101 @@
+<div class="layout vertical" style="height: 100%">
+    <div class="layout vertical start-justified">
+        <hr style="margin: 1px 0 1px 0">
+        <div class="layout horizontal center justified">
+            <h2 class="end-justified" style="margin: 1px 0 1px 0;color: #2feb21;">配置-Json</h2>
+            <div class="end-justified">
+                <ui-button v-on:confirm="onBtnClickTellMe"
+                           style="background: url('http://wpa.qq.com/pa?p=2:774177933:51');width: 79px;height: 25px;">
+                </ui-button>
+                <ui-button v-on:confirm="onBtnClickHelpDoc">
+                    帮助文档
+                </ui-button>
+            </div>
+        </div>
+        <ui-prop name="Json存放路径:">
+            <ui-input class="flex-1" v-value="jsonSavePath" disabled></ui-input>
+            <ui-button class="green" v-on:confirm="onBtnClickOpenJsonSavePath">打开</ui-button>
+        </ui-prop>
+
+        <ui-prop name="合并所有Json"
+                 tooltip="  [√]勾选,所有的json将合并为一个文件
+                            [×]未勾选,每个sheet对应一个json文件">
+            <ui-checkbox v-bind:checked="isMergeJson" v-on:change="onBtnClickMergeJson"></ui-checkbox>
+            <ui-input class="flex-1" v-on:blur="onJsonAllCfgFileChanged" v-value="jsonAllCfgFileName"
+                      v-show="isMergeJson" placeholder="请输入json配置文件名"></ui-input>
+            <ui-button v-show="isJsonAllCfgFileExist && isMergeJson" v-on:confirm="onBtnClickJsonAllCfgFile">打开
+            </ui-button>
+        </ui-prop>
+        <ui-prop name="Json格式化" tooltip=" [√]勾选,json将格式化后保存
+                                            [×]未勾选,json将保存为单行文件">
+            <ui-checkbox v-bind:checked="isFormatJson" v-on:change="onBtnClickFormatJson"></ui-checkbox>
+
+        </ui-prop>
+
+        <hr style="margin: 1px 0 1px 0">
+        <h2 style="margin: 1px 0 1px 0;color: #2feb21;">配置-JavaScript</h2>
+        <ui-prop name="Js存放路径:">
+            <ui-input class="flex-1" v-value="jsSavePath" disabled></ui-input>
+            <ui-button class="green" v-on:confirm="onBtnClickOpenJsSavePath">打开</ui-button>
+        </ui-prop>
+        <ui-prop name="JS配置文件名">
+            <ui-input class="flex-1" v-on:blur="onJsFileNameChanged" v-value="jsFileName"></ui-input>
+            <ui-button v-on:confirm="onBtnClickOpenJsFile" v-show="isJsFileExist">打开</ui-button>
+        </ui-prop>
+        <ui-prop name="代码格式化" tooltip=" [√]勾选,js将格式化后保存文件
+                                            [×]未勾选,js将保存为单行文件">
+            <ui-checkbox v-bind:checked="isFormatJsCode" v-on:change="onBtnClickFormatJsCode"></ui-checkbox>
+        </ui-prop>
+
+        <hr style="margin: 1px 0 1px 0">
+        <h2 style="margin: 1px 0 1px 0;color: #2feb21;">配置-Excel</h2>
+        <ui-prop name="Excel文件路径:" tooltip="插件会循环遍历出目录下所有的excel文件">
+            <div class="flex-1 layout horizontal center">
+                <ui-input placeholder="请选择Excel目录" disabled class="flex-1" v-value="excelRootPath"></ui-input>
+                <ui-button v-show="this.excelRootPath!==null && this.excelRootPath.length>0"
+                           v-on:confirm="onBtnClickOpenExcelRootPath">打开目录
+                </ui-button>
+                <ui-button class="blue" v-on:confirm="onBtnClickSelectExcelRootPath">选择目录</ui-button>
+            </div>
+        </ui-prop>
+
+        <div class="layout vertical"
+             style="width:100%;height: 200px;background-color: #888888;
+         margin-left: 4px;margin-right: 4px;">
+            <div class="layout horizontal center flex-1" style="height: 20px;">
+                <h4 style="margin:4px 0 4px 0" class="flex-2">Excel列表:</h4>
+                <h4 style="margin:4px 10px 4px 0" class="self-end">sheet[{{excelArray.length}}]
+                    excel[{{excelFileArr.length}}]</h4>
+            </div>
+            <!--表头-->
+            <div class="layout horizontal center"
+                 style="width: 100%;height: 20px;background-color: #fd942b;border-radius: 2px;">
+                <div style="width: 20%">
+                    <ui-checkbox v-on:change="onBtnClickSelectSheet" checked></ui-checkbox>
+                    序号
+                </div>
+                <div style="width: 40%">
+                    Excel文件
+                </div>
+                <div style="width: 40%">
+                    工作表名称
+                </div>
+            </div>
+            <!--内容-->
+            <div style="overflow: auto;overflow-x:hidden;height: 160px;">
+
+                <excel-item track-by="$index" v-for="(index,item) in excelArray"
+                            v-bind:data="item"
+                            v-bind:index="index">
+                </excel-item>
+            </div>
+
+        </div>
+        <div class="layout horizontal center justified ">
+            <h2 class="start-justified" style="margin: 1px 0 1px 0;color: #2feb21;"> 输出日志 </h2>
+            <ui-button v-on:confirm="onBtnClickGen" class="end-justified red">生成</ui-button>
+        </div>
+    </div>
+    <textarea class="flex-1 " id="logTextArea" v-model="logView"
+              style="width: 100%; height: 100%; background: #252525;	color: #fd942b;	border-color: #fd942b;"></textarea>
+</div>

+ 532 - 0
airPlay/packages/excel-killer/panel/index.js

@@ -0,0 +1,532 @@
+let packageName = "excel-killer";
+let fs = require('fire-fs');
+let path = require('fire-path');
+let CfgUtil = Editor.require('packages://' + packageName + '/core/CfgUtil.js');
+let excelItem = Editor.require('packages://' + packageName + '/panel/item/excelItem.js');
+let nodeXlsx = Editor.require('packages://' + packageName + '/node_modules/node-xlsx');
+let Electron = require('electron');
+let uglifyJs = Editor.require('packages://' + packageName + '/node_modules/uglify-js');
+let fsExtra = Editor.require('packages://' + packageName + '/node_modules/fs-extra');
+let jsonBeautifully = Editor.require('packages://' + packageName + '/node_modules/json-beautifully');
+var chokidar = Editor.require('packages://' + packageName + '/node_modules/chokidar');
+
+
+Editor.Panel.extend({
+    style: fs.readFileSync(Editor.url('packages://' + packageName + '/panel/index.css', 'utf8')) + "",
+    template: fs.readFileSync(Editor.url('packages://' + packageName + '/panel/index.html', 'utf8')) + "",
+
+
+    $: {
+        logTextArea: '#logTextArea',
+    },
+
+    ready() {
+        let logCtrl = this.$logTextArea;
+        let logListScrollToBottom = function () {
+            setTimeout(function () {
+                logCtrl.scrollTop = logCtrl.scrollHeight;
+            }, 10);
+        };
+
+
+        excelItem.init();
+        window.plugin = new window.Vue({
+            el: this.shadowRoot,
+            created() {
+                this._initPluginCfg();
+            },
+            init() {
+            },
+            data: {
+                logView: "",
+                excelRootPath: null,
+
+                isMergeJson: false,
+                isFormatJson: false,// 是否格式化Json
+                jsonSavePath: null,//json文件存放目录
+                isJsonAllCfgFileExist: false,// 是否单一配置文件存在
+                jsonAllCfgFileName: null,// json配置文件名
+
+                jsSavePath: null,// 插件资源目录
+                jsFileName: null,//js配置文件名
+                isJsFileExist: false,
+                isFormatJsCode: false,
+                excelArray: [],
+                excelFileArr: [],
+            },
+            methods: {
+                _addLog(str) {
+                    let time = new Date();
+                    // this.logView = "[" + time.toLocaleString() + "]: " + str + "\n" + this.logView;
+                    this.logView += "[" + time.toLocaleString() + "]: " + str + "\n";
+                    logListScrollToBottom();
+                },
+                onBtnClickTellMe() {
+                    // let data = nodeXlsx.parse(path.join(this.excelRootPath, 'test2.xlsx'));
+                    // console.log(data);
+                    // return;
+                    let url = "http://wpa.qq.com/msgrd?v=3&uin=774177933&site=qq&menu=yes";
+                    Electron.shell.openExternal(url);
+                },
+                _saveConfig() {
+                    let data = {
+                        excelRootPath: this.excelRootPath,
+                        jsFileName: this.jsFileName,
+                        jsonAllFileName: this.jsonAllCfgFileName,
+                        isMergeJson: this.isMergeJson,
+                        isFormatJsCode: this.isFormatJsCode,
+                        isFormatJson: this.isFormatJson,
+                    };
+                    CfgUtil.saveCfgByData(data);
+                },
+                _watchDir(event, filePath) {
+                    console.log(event, filePath);
+                    let ext = path.extname(filePath);
+                    if (ext === ".xlsx" || ext === ".xls") {
+                        this._onAnalyzeExcelDirPath(this.excelRootPath);
+                    }
+                },
+                onBtnClickHelpDoc(){
+                    let url = "https://github.com/tidys/CocosCreatorPlugins/tree/master/packages/excel-killer/README.md";
+                    Electron.shell.openExternal(url);
+                },
+                _initPluginCfg() {
+                    console.log("initCfg");
+                    CfgUtil.initCfg(function (data) {
+                        if (data) {
+                            this.excelRootPath = data.excelRootPath || "";
+                            if (fs.existsSync(this.excelRootPath)) {
+                                // this._onAnalyzeExcelDirPath(this.excelRootPath);
+                                chokidar.watch(this.excelRootPath).on('all', this._watchDir.bind(this));
+                            } else {
+
+                            }
+                            this.jsFileName = data.jsFileName || "GameJsCfg";
+                            this.jsonAllCfgFileName = data.jsonAllFileName || "GameJsonCfg";
+                            this.isMergeJson = data.isMergeJson;
+                            this.isFormatJsCode = data.isFormatJsCode;
+                            this.isFormatJson = data.isFormatJson;
+                            this.checkJsFileExist();
+                            this.checkJsonAllCfgFileExist();
+                        }
+                    }.bind(this));
+                    this._initCfgSavePath();// 默认json路径
+                },
+                _initCfgSavePath() {
+                    let projectPath = Editor.Project.path;
+                    let pluginResPath = path.join(projectPath, "plugin-resource");
+                    if (!fs.existsSync(pluginResPath)) {
+                        fs.mkdirSync(pluginResPath);
+                    }
+                    let jsonSavePath = path.join(pluginResPath, "json");
+                    if (!fs.existsSync(jsonSavePath)) {
+                        fs.mkdirSync(jsonSavePath);
+                    }
+                    this.jsonSavePath = jsonSavePath;
+
+                    let jsSavePath = path.join(pluginResPath, "js");
+                    if (!fs.existsSync(jsSavePath)) {
+                        fs.mkdirSync(jsSavePath);
+                    }
+                    this.jsSavePath = jsSavePath;
+
+                    let excelSavePath = path.join(pluginResPath, "excel");
+                    if (!fs.existsSync(excelSavePath)) {
+                        fs.mkdirSync(excelSavePath);
+                    }
+                    this.excelRootPath = excelSavePath;
+                },
+                onBtnClickFormatJson() {
+                    this.isFormatJson = !this.isFormatJson;
+                    this._saveConfig();
+                },
+                // 是否合并json
+                onBtnClickMergeJson() {
+                    this.isMergeJson = !this.isMergeJson;
+                    this._saveConfig();
+
+                },
+                // 打开合并的json
+                onBtnClickJsonAllCfgFile() {
+                    let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
+                    if (fs.existsSync(saveFileFullPath)) {
+                        Electron.shell.openItem(saveFileFullPath);
+                        Electron.shell.beep();
+                    } else {
+                        // this._addLog("目录不存在:" + this.resourceRootDir);
+                        this._addLog("目录不存在:" + saveFileFullPath);
+                        return;
+                    }
+                },
+                checkJsonAllCfgFileExist() {
+                    let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
+                    if (fs.existsSync(saveFileFullPath)) {
+                        this.isJsonAllCfgFileExist = true;
+                    } else {
+                        this.isJsonAllCfgFileExist = false;
+                    }
+                },
+                onBtnClickFormatJsCode() {
+                    this.isFormatJsCode = !this.isFormatJsCode;
+                    this._saveConfig();
+                },
+                onBtnClickOpenExcelRootPath() {
+                    if (fs.existsSync(this.excelRootPath)) {
+                        Electron.shell.showItemInFolder(this.excelRootPath);
+                        Electron.shell.beep();
+                    } else {
+                        this._addLog("目录不存在:" + this.excelRootPath);
+                    }
+                },
+                onBtnClickSelectExcelRootPath() {
+                    let res = Editor.Dialog.openFile({
+                        title: "选择Excel的根目录",
+                        defaultPath: Editor.Project.path,
+                        properties: ['openDirectory'],
+                    });
+                    if (res !== -1) {
+                        let dir = res[0];
+                        if (dir !== this.excelRootPath) {
+                            this.excelRootPath = dir;
+                            chokidar.watch(this.excelRootPath).on('all', this._watchDir.bind(this));
+                            // this._onAnalyzeExcelDirPath(dir);
+                            this._saveConfig();
+                        }
+                    }
+                },
+                // 修改js配置文件
+                onJsFileNameChanged() {
+                    this._saveConfig();
+                },
+                // 修改json配置文件
+                onJsonAllCfgFileChanged() {
+                    this._saveConfig();
+                },
+                // 查找出目录下的所有excel文件
+                _onAnalyzeExcelDirPath(dir) {
+                    // let dir = path.normalize("D:\\proj\\CocosCreatorPlugins\\doc\\excel-killer");
+                    if (dir) {
+                        // 查找json文件
+                        let allFileArr = [];
+                        let excelFileArr = [];
+                        // 获取目录下所有的文件
+                        readDirSync(dir);
+                        // 过滤出来.xlsx的文件
+                        for (let k in allFileArr) {
+                            let file = allFileArr[k];
+                            let extName = path.extname(file);
+                            if (extName === ".xlsx" || extName === ".xls") {
+                                excelFileArr.push(file);
+                            } else {
+                                this._addLog("不支持的文件类型: " + file);
+                            }
+                        }
+
+                        this.excelFileArr = excelFileArr;
+                        // 组装显示的数据
+                        let excelSheetArray = [];
+                        let sheetDuplicationChecker = {};//表单重名检测
+                        for (let k in excelFileArr) {
+                            let itemFullPath = excelFileArr[k];
+                            // this._addLog("excel : " + itemFullPath);
+
+                            let excelData = nodeXlsx.parse(itemFullPath);
+                            //todo 检测重名的sheet
+                            for (let j in excelData) {
+                                let itemData = {
+                                    isUse: true,
+                                    fullPath: itemFullPath,
+                                    name: "name",
+                                    sheet: excelData[j].name
+                                };
+                                itemData.name = itemFullPath.substr(dir.length + 1, itemFullPath.length - dir.length);
+
+                                if (excelData[j].data.length === 0) {
+                                    this._addLog("[Error] 空Sheet: " + itemData.name + " - " + itemData.sheet);
+                                    continue;
+                                }
+
+                                if (sheetDuplicationChecker[itemData.sheet]) {
+                                    //  重名sheet问题
+                                    this._addLog("[Error ] 出现了重名sheet: " + itemData.sheet);
+                                    this._addLog("[Sheet1] " + sheetDuplicationChecker[itemData.sheet].fullPath);
+                                    this._addLog("[Sheet2] " + itemFullPath);
+                                    this._addLog("请仔细检查Excel-Sheet!");
+                                } else {
+                                    sheetDuplicationChecker[itemData.sheet] = itemData;
+                                    excelSheetArray.push(itemData);
+                                }
+                            }
+                        }
+                        this.excelArray = excelSheetArray;
+
+
+                        function readDirSync(dirPath) {
+                            let dirInfo = fs.readdirSync(dirPath);
+                            for (let i = 0; i < dirInfo.length; i++) {
+                                let item = dirInfo[i];
+                                let itemFullPath = path.join(dirPath, item);
+                                let info = fs.statSync(itemFullPath);
+                                if (info.isDirectory()) {
+                                    // this._addLog('dir: ' + itemFullPath);
+                                    readDirSync(itemFullPath);
+                                } else if (info.isFile()) {
+                                    let headStr = item.substr(0, 2);
+                                    if (headStr === "~$") {
+                                        window.plugin._addLog("检索到excel产生的临时文件:" + itemFullPath);
+                                    } else {
+                                        allFileArr.push(itemFullPath);
+                                    }
+                                    // this._addLog('file: ' + itemFullPath);
+                                }
+                            }
+                        }
+                    }
+                },
+                onBtnClickSelectSheet(event) {
+                    let b = event.currentTarget.value;
+                    for (let k in this.excelArray) {
+                        this.excelArray[k].isUse = b;
+                    }
+                },
+                onBtnClickOpenJsonSavePath() {
+                    if (fs.existsSync(this.jsonSavePath)) {
+                        Electron.shell.showItemInFolder(this.jsonSavePath);
+                        Electron.shell.beep();
+                    } else {
+                        this._addLog("目录不存在:" + this.jsonSavePath);
+                    }
+                },
+                onBtnClickOpenJsSavePath() {
+                    if (fs.existsSync(this.jsSavePath)) {
+                        Electron.shell.showItemInFolder(this.jsSavePath);
+                        Electron.shell.beep();
+                    } else {
+                        this._addLog("目录不存在:" + this.jsSavePath);
+                    }
+                },
+                _getJavaScriptSaveData(excelData, itemSheet) {
+                    let title = excelData[0];
+                    let desc = excelData[1];
+                    let sheetFormatData = {};
+                    for (let i = 2; i < excelData.length; i++) {
+                        let lineData = excelData[i];
+                        if (lineData.length === 0) {
+                            // 空行直接跳过
+                            continue;
+                        } else {
+                            // if (lineData.length < title.length) {
+                            //     this._addLog("[Error] 发现第" + i + "行缺少字段,跳过改行数据配置.");
+                            //     continue;
+                            // } else if (lineData.length > title.length) {
+                            //     this._addLog("[Error] 发现第" + i + "行多余字段,跳过改行数据配置.");
+                            //     continue;
+                            // }
+                        }
+                        let saveLineData = {};
+                        for (let j = 1; j < title.length; j++) {
+                            let key = title[j];
+                            let value = lineData[j];
+                            if (value === undefined) {
+                                value = "";
+                                // this._addLog("[Error] 发现空单元格:" + itemSheet.name + "*" + itemSheet.sheet + " => (" + key + "," + (i + 1) + ")");
+                            }
+                            saveLineData[key] = value;
+                        }
+                        sheetFormatData[lineData[0].toString()] = saveLineData;
+                    }
+                    return sheetFormatData;
+                },
+                _getJsonSaveData(excelData, itemSheet) {
+                    let title = excelData[0];
+                    let desc = excelData[1];
+                    let ret = null;
+                    let useFormat1 = false;
+                    if (useFormat1) {
+                        let saveData1 = [];// 格式1:对应的为数组
+                        for (let i = 2; i < excelData.length; i++) {
+                            let lineData = excelData[i];
+                            // if (lineData.length < title.length) {
+                            //     continue;
+                            // } else if (lineData.length > title.length) {
+                            //     continue;
+                            // }
+
+                            let saveLineData = {};
+                            for (let j = 0; j < title.length; j++) {
+                                let key = title[j];
+                                let value = lineData[j];
+                                if (value === undefined) {
+                                    value = "";
+                                }
+                                // this._addLog("" + value);
+                                saveLineData[key] = value;
+                            }
+                            saveData1.push(saveLineData);
+                        }
+                        ret = saveData1;
+                    } else {
+                        let saveData2 = {};// 格式2:id作为索引
+                        for (let i = 2; i < excelData.length; i++) {
+                            let lineData = excelData[i];
+                            // if (lineData.length < title.length) {
+                            //     continue;
+                            // } else if (lineData.length > title.length) {
+                            //     continue;
+                            // }
+                            let saveLineData = {};
+                            for (let j = 1; j < title.length; j++) {
+                                let key = title[j];
+                                let value = lineData[j];
+                                if (value === undefined) {
+                                    value = "";
+                                }
+                                // this._addLog("" + value);
+                                saveLineData[key] = value;
+                            }
+                            saveData2[lineData[0].toString()] = saveLineData;
+                        }
+                        ret = saveData2;
+                    }
+                    return ret;
+                },
+                // 打开生成的js配置文件
+                onBtnClickOpenJsFile() {
+                    let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
+                    if (fs.existsSync(saveFileFullPath)) {
+                        Electron.shell.openItem(saveFileFullPath);
+                        Electron.shell.beep();
+                    } else {
+                        this._addLog("目录不存在:" + this.saveFileFullPath);
+                    }
+                },
+                // 检测js配置文件是否存在
+                checkJsFileExist() {
+                    let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
+                    if (fs.existsSync(saveFileFullPath)) {
+                        this.isJsFileExist = true;
+                    } else {
+                        this.isJsFileExist = false;
+                    }
+                },
+                // 生成配置
+                onBtnClickGen() {
+                    console.log("onBtnClickGen");
+                    // 参数校验
+                    if (this.excelArray.length <= 0) {
+                        this._addLog("未发现要生成的配置!");
+                        return;
+                    }
+
+                    if (this.isMergeJson) {
+                        if (this.jsonAllCfgFileName.length <= 0) {
+                            this._addLog("请输入要保存的json文件名!");
+                            return;
+                        }
+                    }
+                    if (this.jsFileName.length <= 0) {
+                        this._addLog("请输入要保存的js文件名!");
+                        return;
+                    }
+
+
+                    this.logView = "";
+                    // 删除老的配置
+                    fsExtra.emptyDirSync(this.jsonSavePath);
+                    fsExtra.emptyDirSync(this.jsSavePath);
+                    let jsSaveData = {};// 保存的js数据
+                    let jsonAllSaveData = {};// 保存的json数据
+                    for (let k in this.excelArray) {
+                        let itemSheet = this.excelArray[k];
+                        if (itemSheet.isUse) {
+                            let excelData = nodeXlsx.parse(itemSheet.fullPath);
+                            let sheetData = null;
+                            for (let j in excelData) {
+                                if (excelData[j].name === itemSheet.sheet) {
+                                    sheetData = excelData[j].data;
+                                }
+                            }
+                            if (sheetData) {
+                                if (sheetData.length > 2) {
+                                    // 保存为json
+                                    let jsonSaveData = this._getJsonSaveData(sheetData, itemSheet);
+                                    if (this.isMergeJson) {
+                                        jsonAllSaveData[itemSheet.sheet] = jsonSaveData;
+                                    } else {
+                                        let saveStr = JSON.stringify(jsonSaveData);
+                                        if (this.isFormatJson) {// 格式化json
+                                            saveStr = jsonBeautifully(saveStr);
+                                        }
+                                        let saveFileFullPath = path.join(this.jsonSavePath, itemSheet.sheet + ".json");
+                                        fs.writeFileSync(saveFileFullPath, saveStr);
+                                        this._addLog("[Json]:" + saveFileFullPath);
+                                    }
+                                    // 保存为js
+                                    let sheetJsData = this._getJavaScriptSaveData(sheetData, itemSheet);
+                                    // 检测重复问题
+                                    if (jsSaveData[itemSheet.sheet] === undefined) {
+                                        jsSaveData[itemSheet.sheet] = sheetJsData;
+                                    } else {
+                                        this._addLog("发现重名sheet:" + itemSheet.name + "(" + itemSheet.sheet + ")");
+                                    }
+                                } else {
+                                    this._addLog("行数低于2行,无效sheet:" + itemSheet.sheet);
+                                }
+                            } else {
+                                this._addLog("未发现数据");
+                            }
+
+                        } else {
+                            console.log("忽略配置: " + itemSheet.fullPath + ' - ' + itemSheet.sheet);
+                        }
+                    }
+                    // =====================>>>>  保存json文件   <<<=================================
+                    if (this.isMergeJson) {
+                        let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
+                        let str = JSON.stringify(jsonAllSaveData);
+                        if (this.isFormatJson) {
+                            str = jsonBeautifully(str);
+                        }
+                        fs.writeFileSync(saveFileFullPath, str);
+                        this._addLog("[Json]:" + saveFileFullPath);
+                    }
+                    // =====================>>>>  保存js文件   <<<=================================
+                    // TODO 保证key的顺序一致性
+                    let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
+                    let saveStr = "module.exports = " + JSON.stringify(jsSaveData) + ";";
+                    if (this.isFormatJsCode) {// 保存为格式化代码
+                        let ast = uglifyJs.parse(saveStr);
+                        let ret = uglifyJs.minify(ast, {
+                            output: {
+                                beautify: true,//如果希望得到格式化的输出,传入true
+                                indent_start: 0,//(仅当beautify为true时有效) - 初始缩进空格
+                                indent_level: 4,//(仅当beautify为true时有效) - 缩进级别,空格数量
+                            }
+                        });
+                        if (ret.error) {
+                            this._addLog('error: ' + ret.error.message);
+                        } else if (ret.code) {
+                            fs.writeFileSync(saveFileFullPath, ret.code);
+                            this._addLog("[JavaScript]" + saveFileFullPath);
+                        } else {
+                            this._addLog(JSON.stringify(ret));
+                        }
+                    } else {// 保存为单行代码
+                        fs.writeFileSync(saveFileFullPath, saveStr);
+                        this._addLog("[JavaScript]" + saveFileFullPath);
+                    }
+                    this._addLog("全部转换完成!");
+                    this.checkJsFileExist();
+                    this.checkJsonAllCfgFileExist();
+                },
+
+            },
+
+        });
+    },
+
+    messages: {
+        'excel-killer:hello'(event) {
+        }
+    }
+});

+ 20 - 0
airPlay/packages/excel-killer/panel/item/excelItem.html

@@ -0,0 +1,20 @@
+<div class="layout horizontal center"
+     style="width: 100%;height: 20px;
+     background-color: #252525;"
+     onmouseover="this.style.backgroundColor='#505050'"
+     onmouseout="this.style.backgroundColor='#252525'"
+>
+    <div class="layout horizontal center " style="width: 20%">
+        <ui-checkbox v-bind:checked="data.isUse"
+                     v-on:change="onBtnClickUse"
+        ></ui-checkbox>
+        <div class="flex-1"> {{index+1}}</div>
+    </div>
+    <div style="width: 40%">
+        {{data.name}}
+    </div>
+    <div style="width: 40%">
+        {{data.sheet}}
+    </div>
+</div>
+<hr style="margin: 1px 0 1px 0">

+ 23 - 0
airPlay/packages/excel-killer/panel/item/excelItem.js

@@ -0,0 +1,23 @@
+let fs = require('fire-fs');
+let packageName = "excel-killer";
+
+module.exports = {
+    init() {
+        console.log("excel-item 注册组件!");
+        Vue.component('excel-item', {
+            props: ['data', 'index'],
+            template: fs.readFileSync(Editor.url('packages://' + packageName + '/panel/item/excelItem.html', 'utf8')) + "",
+            created() {
+
+            },
+            methods: {
+                onBtnClickUse() {
+                    this.data.isUse=!this.data.isUse;
+                    console.log("on use: " + this.data.isUse);
+
+                }
+            },
+            computed: {},
+        });
+    }
+};