Example #1
0
            }, function (error, output) {
            
                var content = output.css;

                var file = MD5.get(content);
                file = cssDir + file + '.css';

                var href = path.relative(meta.dir, file);
                href = Path.format(href);

                //删除 concat() 产生的文件
                if (options.delete) {
                    File.delete(build.file);
                }

                File.write(file, content);


                $.Object.extend(build, {
                    'file': file,
                    'href': href,
                    'content': content,
                });

                //更新 html
                meta.html = $.String.format(meta.sample, {
                    'href': href,
                });

                fn && fn();

            });
Example #2
0
File: Data.js Project: micty/Weber
    /**
    * 递归扫描并填充预留的数据。
    */
    function format(config) {

        return $.Object.map(config, function (key, value) {

            if (typeof value == 'string') {
                return fill(value);
            }

            if (value instanceof Array) {

                return $.Array.keep(value, function (item, index) {

                    if (typeof item == 'string') {
                        return fill(item);
                    }

                    if (typeof item == 'object') {
                        return format(item); //递归
                    }

                    return item;

                }, true);
            }

            return value;

        }, true); //深层次来扫描

    }
Example #3
0
        reset: function () {
            var meta = mapper.get(this);
            var less$item = meta.less$item;

            meta.list.forEach(function (less) {
                var item = less$item[less];

                FileRefs.delete(less);
                FileRefs.delete(item.file);
            });


            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 less 文件列表。
                'less$item': {},    //less 文件所对应的信息

                //记录 concat, minify 的输出结果
                'build': {
                    file: '',       //完整物理路径
                    href: '',       //用于 link 标签中的 href 属性
                    content: '',    //合并和压缩后的内容
                },
            });
        },
Example #4
0
        reset: function () {

            var meta = mapper.get(this);

            //删除之前的文件引用计数
            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);         
            });


            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 js 文件列表及其它信息。
                'file$stat': {},    //文件所对应的最大行数和最大列数等统计信息。 
                'file$md5': {},     //
                'build': {
                    file: '',       //完整物理路径
                    content: '',    //合并和压缩后的内容
                },
            });

        },
Example #5
0
 set: function (name, config) {
     if (typeof name == 'object') { //批量设置: set({...})
         $.Object.each(name, function (name, config) {
             use(name);
             cfg.set(name, config);
         });
     }
     else { //单个设置 set(name, config)
         use(name);
         cfg.set(name, config);
     }
 },
Example #6
0
        reset: function () {

            var meta = mapper.get(this);

            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块的 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 html 文件列表及其它信息。
            });

        },
Example #7
0
                'success': function (data) {
                    //增加些字段。
                    data = $.Object.extend(data, {
                        'url': meta.url,
                        'host': meta.host,
                    });

                    if (meta.json.write) {
                        File.writeJSON(meta.json.file, data);
                    }

                    emitter.fire('get', [data]);
                },
Example #8
0
            stats.slice(1).forEach(function (item) {

                $.Object.each(item, function (file, obj) {

                    var stat = file$stat[file];
                    if (stat) {
                        stat['count'] += obj['count'];
                    }
                    else {
                        file$stat[file] = obj;
                    }
                });
            });
Example #9
0
                'success': function (data) {

                    $.Object.extend(data, {
                        'userId': meta.userId,
                        'url': meta.url,
                        'no': meta.no,
                        'total': meta.total,
                    });

                    if (meta.json.write) {
                        File.writeJSON(meta.json.file, data);
                    }

                    emitter.fire('get', [data]);
                },
Example #10
0
        reset: function () {
            var meta = mapper.get(this);
            var less$item = meta.less$item;

            meta.list.forEach(function (less) {
                var item = less$item[less];
                FileRefs.delete(less);
            });


            $.Object.extend(meta, {
                'patterns': [],     //模式列表。
                'list': [],         //真实 less 文件列表。
                'less$item': {},    //less 文件所对应的信息
            });
        },
Example #11
0
            list.forEach(function (item) {

                var ext = item.ext;
                var opts = options[ext];
                if (!opts) {
                    return;
                }


                var file = item.file;
                if (!file) { //外部地址
                    if (opts.outer) { //指定了替换外部地址为压缩版
                        item.build.ext = opts.ext;
                    }
                    return;
                }

               
                var result = UglifyJS.minify(file);
                var content = result.code;

                if (opts.delete) { //删除源 js文件
                    FileRefs.delete(file);
                }

                var dest = item.name + opts.ext;
                dest = Path.join(meta.dir, dest);

                if (opts.write) {
                    if (File.exists(dest)) {
                        if (opts.overwrite) {
                            File.write(dest, content);
                        }
                    }
                    else {
                        File.write(dest, content);
                    }
                }

                $.Object.extend(item.build, {
                    'file': dest,
                    'ext': opts.ext,
                    'content': content,
                });

            });
Example #12
0
        reset: function () {
            var meta = mapper.get(this);


            meta.list.forEach(function (item) {
                item.links.destroy();               //移除之前的子节点实例
                FileRefs.delete(item.file);         //删除之前的文件引用计数
            });


            $.Object.extend(meta, {
                'master': '',   //当前分母版页的内容。
                'lines': [],    //html 换行拆分的列表
                'list': [],     //html 片段文件列表及其它信息。
            });

        },
Example #13
0
        reset: function (keep) {
            var meta = mapper.get(this);
            var less$item = meta.less$item;

            meta.list.forEach(function (obj) {
                var less = obj.file;
                var item = less$item[less];

                FileRefs.delete(less);
                FileRefs.delete(item.file);
            });

            $.Object.extend(meta, {
                'master': '',
                'list': [],
                'lines': [],        //html 换行拆分的列表
                'less$item': keep ? less$item : {},    //less 文件所对应的信息
            });
        },
Example #14
0
        reset: function () {

            var meta = mapper.get(this);


            meta.list.forEach(function (item) {
                FileRefs.delete(item.file); //删除之前的文件引用计数
                FileRefs.delete(item.build.file); //删除之前的文件引用计数
            });


            $.Object.extend(meta, {
                'master': '',
                'list': [],
                'lines': [],
                'file$md5': {},
            });

        },
Example #15
0
        reset: function () {
            var meta = mapper.get(this);
            var old = meta.old;

            //先备份。 old 中的一旦有值,将再也不会变为 null。
            old.HtmlPackage = meta.HtmlPackage;
            old.JsPackage = meta.JsPackage;
            old.LessPackage = meta.LessPackage;

            //再清空。
            $.Object.extend(meta, {
                'HtmlPackage': null,
                'JsPackage': null,
                'LessPackage': null,

                'css': '',
                'html': '',
                'js': '',
            });
        },
Example #16
0
        reset: function () {

            var meta = mapper.get(this);

            //删除之前的文件引用计数
            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);         
            });


            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 js 文件列表及其它信息。
                'content': '',      //编译后的 js 内容。
            });

        },
Example #17
0
define('JsPackage', function (require, module, exports) {

    var $ = require('$');
    var File = require('File');
    var FileRefs = require('FileRefs');
    var Path = require('Path');
    var Patterns = require('Patterns');
    var Watcher = require('Watcher');
    var Defaults = require('Defaults');
    var MD5 = require('MD5');

    var Emitter = $.require('Emitter');
    


    var mapper = new Map();




    function JsPackage(dir, config) {


        config = Defaults.clone(module.id, config);

        var meta = {

            'dir': dir,
            'patterns': [],     //模式列表。
            'list': [],         //真实 js 文件列表及其它信息。
            'content': '',      //编译后的 js 内容。
            'emitter': new Emitter(this),
            'watcher': null,                //监控器,首次用到时再创建。



        };

        mapper.set(this, meta);

    }



    JsPackage.prototype = {
        constructor: JsPackage,

        /**
        * 重置为初始状态,即创建时的状态。
        */
        reset: function () {

            var meta = mapper.get(this);

            //删除之前的文件引用计数
            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);         
            });


            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 js 文件列表及其它信息。
                'content': '',      //编译后的 js 内容。
            });

        },

        /**
        * 根据当前模式获取对应真实的 js 文件列表。
        */
        get: function (patterns) {
            var meta = mapper.get(this);
            var dir = meta.dir;

            patterns = meta.patterns = Patterns.combine(dir, patterns);

            var list = Patterns.getFiles(patterns);

            list = list.map(function (file, index) {

                file = Path.format(file);
                FileRefs.add(file);

                return file;

            });

            meta.list = list;

        },


        /**
        * 合并对应的 js 文件列表。
        */
        concat: function (options) {

            var meta = mapper.get(this);
            var list = meta.list;
            if (list.length == 0) {
                meta.content = '';
                return;
            }

            //加上文件头部和尾部,形成闭包
            var header = options.header;
            if (header) {
                header = Path.format(header);
                FileRefs.add(header);
                list = [header].concat(list);
            }

            var footer = options.footer;
            if (footer) {
                footer = Path.format(footer);
                FileRefs.add(footer);
                list = list.concat(footer);
            }

            var JS = require('JS');
            var content = meta.content = JS.concat(list, options);
            var md5 = MD5.get(content);

            return md5;

        },

        /**
        * 压缩合并后的 js 文件。
        */
        minify: function (dest) {

            var meta = mapper.get(this);
            var content = meta.content;
            var JS = require('JS');

            //直接从内容压缩,不读取文件
            content = meta.content = JS.minify(content);

            if (typeof dest == 'object') {
                var name = dest.name;
                if (typeof name == 'number') {
                    name = MD5.get(content, name);
                    name += '.js';
                }

                dest = dest.dir + name;
                File.write(dest, content); //写入合并后的 js 文件
            }

            return dest;
        },



        /**
        * 监控当前模式下 js 文件的变化。
        */
        watch: function () {
            var meta = mapper.get(this);
            var patterns = meta.patterns;
            if (patterns.length == 0) { //列表为空,不需要监控
                return;
            }

            var watcher = meta.watcher;

            if (!watcher) { //首次创建
               
                watcher = meta.watcher = new Watcher();

                var self = this;
                var emitter = meta.emitter;

                function add(files) {

                    //增加到列表
                    var list = files.map(function (file, index) {
                        file = Path.format(file);
                        FileRefs.add(file);
                        return file;
                    });

                    meta.list = meta.list.concat(list);
                }



                watcher.on({
                    'added': function (files) {
                        add(files);
                        emitter.fire('change');
                    },

                    'deleted': function (files) {
                        //从列表中删除
                        var obj = {};
                        files.forEach(function (file) {
                            FileRefs.delete(file, true);
                            obj[file] = true;
                        });

                        meta.list = meta.list.filter(function (file) {
                            return !obj[file];
                        });

                        emitter.fire('change');
                    },

                    //重命名的,会先后触发 deleted 和 renamed
                    'renamed': function (files) {
                        add(files);
                        emitter.fire('change');
                    },

                    'changed': function (files) {
                        emitter.fire('change');
                    },

                });
                
            }


            watcher.set(patterns);

        },


        /**
        * 取消监控。
        */
        unwatch: function () {
            var meta = mapper.get(this);
            var watcher = meta.watcher;
            if (watcher) {
                watcher.close();
            }
        },


        /**
        * 删除模式列表中所对应的 js 物理文件。
        */
        delete: function () {
            var meta = mapper.get(this);

            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);
            });
        },

        /**
        * 绑定事件。
        */
        on: function (name, fn) {
            var meta = mapper.get(this);
            var emitter = meta.emitter;

            var args = [].slice.call(arguments, 0);
            emitter.on.apply(emitter, args);
        },

       

    };


    return $.Object.extend(JsPackage, {

       
    });



});
Example #18
0
define('JsList', function (require, module, exports) {

    var $ = require('$');
    var path = require('path');

    var File = require('File');
    var FileRefs = require('FileRefs');
    var Path = require('Path');
    var Patterns = require('Patterns');
    var MD5 = require('MD5');
    var Watcher = require('Watcher');
    var Defaults = require('Defaults');
    var Log = require('Log');
    var Attribute = require('Attribute');
    var Lines = require('Lines');
    var Url = require('Url');

    var Emitter = $.require('Emitter');
    


    var mapper = new Map();




    function JsList(dir, config) {


        config = Defaults.clone(module.id, config);

        var rid = $.String.random(4); //随机 id

        var meta = {

            'dir': dir,         //母版页所在的目录。
            'master': '',       //母版页的内容,在 parse() 中用到。
            'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
            'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
            'patterns': [],     //模式列表。
            'list': [],         //真实 js 文件列表及其它信息。
            'file$stat': {},    //记录文件内容中的最大行数和最大列数信息。 
            'file$md5': {}, 


            'scriptType': $.String.random(64),      //用于 script 的 type 值。 在页面压缩 js 时防止重复压缩。
            'emitter': new Emitter(this),
            'watcher': null,                        //监控器,首次用到时再创建。

            'extraPatterns': config.extraPatterns,  //额外附加的模式。
            'regexp': config.regexp,
            'md5': config.md5,
            'sample': config.sample,
            'tags': config.tags,
            'concat': config.concat,
            'minify': config.minify,
            'inline': config.inline,
            'max': config.max,              //允许的最大行数和列数。
            'htdocsDir': config.htdocsDir,

            //记录 concat, minify 的输出结果
            'build': {
                file: '',       //完整物理路径
                content: '',    //合并和压缩后的内容
            },

        };

        mapper.set(this, meta);

    }



    JsList.prototype = {
        constructor: JsList,

        /**
        * 重置为初始状态,即创建时的状态。
        */
        reset: function () {

            var meta = mapper.get(this);

            //删除之前的文件引用计数
            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);         
            });


            $.Object.extend(meta, {
                'master': '',       //母版页的内容,在 parse() 中用到。
                'html': '',         //模式所生成的 html 块,即缓存 toHtml() 方法中的返回结果。
                'outer': '',        //包括开始标记和结束标记在内的原始的整一块 html。
                'patterns': [],     //模式列表。
                'list': [],         //真实 js 文件列表及其它信息。
                'file$stat': {},    //文件所对应的最大行数和最大列数等统计信息。 
                'file$md5': {},     //
                'build': {
                    file: '',       //完整物理路径
                    content: '',    //合并和压缩后的内容
                },
            });

        },


        /**
        * 从当前或指定的母版页 html 内容中提出 js 文件列表信息。
        * @param {string} master 要提取的母版页 html 内容字符串。
        */
        parse: function (master) {
            var meta = mapper.get(this);
            master = meta.master = master || meta.master;
           

            var tags = meta.tags;
            var dir = meta.dir;
        
            var html = $.String.between(master, tags.begin, tags.end);
            if (!html) {
                return;
            }

            var patterns = $.String.between(html, '<script>', '</script>');

            if (!patterns) {

                var list = html.match(meta.regexp);
                if (!list) {
                    return;
                }
                
                var lines = Lines.get(html);
                var startIndex = 0;

                patterns = $.Array.map(list, function (item, index) {

                    var src = Attribute.get(item, 'src');
                    if (!src) {
                        console.log('JsList 块里的 script 标签必须含有 src 属性:'.bgRed, item);
                        throw new Error();
                    }

                    var index = Lines.getIndex(lines, item, startIndex);
                    var line = lines[index];    //整一行的 html。

                    //所在的行给注释掉了,忽略
                    if (Lines.commented(line, item)) {
                        return null;
                    }

                    startIndex = index + 1; //下次搜索的起始行号
                    
                    if (Url.checkFull(src)) { //是绝对(外部)地址
                        console.log('JsList 块里的 script 标签 src 属性不能引用外部地址:'.bgRed, item);
                        throw new Error();
                    }

                    src = Path.format(src);
                    return src;
                });

                patterns = JSON.stringify(patterns, null, 4);
            }


            if (!patterns) {
                return;
            }

            //母版页中可能会用到的上下文。
            var context = {
                'dir': dir,
                'master': master,
                'tags': meta.tags,
                'htdocsDir': meta.htdocsDir,
            };

            var fn = new Function('require', 'context',
                //包装多一层匿名立即执行函数
                'return (function () { ' +
                    'var a = ' + patterns + '; \r\n' +
                    'return a;' +
                '})();'
            );

            //执行母版页的 js 代码,并注入变量。
            patterns = fn(require, context);

            if (!Array.isArray(patterns)) {
                throw new Error('引入文件的模式必须返回一个数组!');
            }

            patterns = patterns.concat(meta.extraPatterns); //跟配置中的模式合并
            patterns = Patterns.fill(dir, patterns);
            patterns = Patterns.combine(dir, patterns);

            console.log('匹配到'.bgGreen, patterns.length.toString().cyan, '个 js 模式:');
            Log.logArray(patterns);

            meta.patterns = patterns;
            meta.outer = tags.begin + html + tags.end;

        },

        /**
        * 根据当前模式获取对应真实的 js 文件列表和其它信息。
        */
        get: function () {
            var meta = mapper.get(this);
           
            //删除之前的文件引用计数
            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);
            });

            var patterns = meta.patterns;
            var list = Patterns.getFiles(patterns);

            list = $.Array.keep(list, function (file, index) {

                file = Path.format(file);

                var href = path.relative(meta.dir, file);
                href = Path.format(href);

                FileRefs.add(file);

                return {
                    'file': file,
                    'href': href,
                };

            });

            meta.list = list;

        },


        /**
        * 获取 js 文件列表所对应的 md5 值和引用计数信息。
        */
        md5: function () {
            var meta = mapper.get(this);
            var file$md5 = meta.file$md5;
            var list = meta.list;
   
            var file$stat = {};

            list.forEach(function (item) {

                var file = item.file;
                var stat = file$stat[file];
                if (stat) {
                    stat['count']++;
                    return;
                }


                var md5 = file$md5[file];
                if (!md5) {
                    md5 = file$md5[file] = MD5.read(file);
                }

                file$stat[file] = {
                    'count': 1,
                    'md5': md5,
                };

            });

            return file$stat;
        },

        /**
        * 把当前的动态 js 引用模式块转成真实的静态 js 引用所对应的 html。
        */
        toHtml: function () {
            var meta = mapper.get(this);
            var sample = meta.sample;
            var list = meta.list;
            if (list.length == 0) {
                meta.html = '';
                return;
            }

            var tags = meta.tags;
            var file$stat = meta.file$stat;
            var file$md5 = meta.file$md5;

            var max = meta.max;

            //需要排除的文件列表,即不作检查的文件列表。
            var excludes = max.excludes;
            if (excludes) {
                excludes = Patterns.combine(meta.htdocsDir, excludes);
            }


            //todo: 检查重复的文件
            list = $.Array.keep(list, function (item, index) {
                var href = item.href;
                var file = item.file;

                var stat = file$stat[file];
                if (!stat) {
                    var content = File.read(file);
                    stat = file$stat[file] = Lines.stat(content);
                }

                //在排除列表中的文件,不作检查。
                //具体为: 如果未指定排除列表,或者不在排除列表中。
                if (!excludes || !Patterns.matchedIn(excludes, file)) {

                    if (stat.y > max.y) {
                        console.log('超出所允许的最大行数'.bgRed, JSON.stringify({
                            '所在文件': file,
                            '当前原始行数': stat.y0,
                            '当前有效行数': stat.y,
                            '允许最大行数': max.y,
                            '超过行数': stat.y - max.y,
                        }, null, 4).yellow);
                        throw new Error();
                    }

                    if (stat.x > max.x) {
                        console.log('代码行超出所允许的最大长度'.bgRed, JSON.stringify({
                            '所在文件': file,
                            '所在行号': stat.no,
                            '当前行长度': stat.x,
                            '允许最大长度': max.x,
                            '超过长度': stat.x - max.x,
                        }, null, 4).yellow);
                        throw new Error();
                    }
                }



                var len = meta.md5;
                if (len > 0) {

                    var md5 = file$md5[file];

                    if (!md5) { //动态去获取 md5 值。
                        md5 = file$md5[file] = MD5.read(file);
                    }

                    md5 = md5.slice(0, len);

                    href = href + '?' + md5;
                }

                return $.String.format(sample, {
                    'href': href,
                });
            });

            meta.html =
                tags.begin + '\r\n    ' +
                list.join('\r\n    ') + '\r\n    ' +
                tags.end + '\r\n';

        },

        /**
        * 把整一块动态 js 引用模式替换成真实的静态 js 引用。
        * @param {string} [master] 要替换的母版 html。 如果不指定,则使用原来的。
        *   注意,如果使用新的模板,则该模板中的模式不能变。
        */
        mix: function (master) {
            var meta = mapper.get(this);
            var outer = meta.outer;

            master = master || meta.master;

            //实现安全替换
            var beginIndex = master.indexOf(outer);
            var endIndex = beginIndex + outer.length;

            master =
                master.slice(0, beginIndex) +
                meta.html + 
                master.slice(endIndex);

            return master;

        },

        /**
        * 监控当前模式下 js 文件的变化。
        */
        watch: function () {
            var meta = mapper.get(this);
            var patterns = meta.patterns;
            if (patterns.length == 0) { //列表为空,不需要监控
                return;
            }


            var watcher = meta.watcher;

            if (!watcher) { //首次创建
               
                watcher = meta.watcher = new Watcher();

                var self = this;
                var file$stat = meta.file$stat;
                var file$md5 = meta.file$md5;
                var emitter = meta.emitter;

                watcher.on({
                    'added': function (files) {
                        self.get();
                        self.toHtml();
                        emitter.fire('change');
                    },

                    'deleted': function (files) {

                        //删除对应的记录
                        files.forEach(function (file) {
                            delete file$stat[file];
                            delete file$md5[file];
                            FileRefs.delete(file, true);
                        });

                        self.get();
                        self.toHtml();
                        emitter.fire('change');
                    },

                    //重命名的,会先后触发:deleted 和 renamed
                    'renamed': function (files) {
                        self.get();
                        self.toHtml();
                        emitter.fire('change');
                    },

                    'changed': function (files) {

                        //让对应的记录作废
                        files.forEach(function (file) {
                            file$stat[file] = null;
                            file$md5[file] = null;
                        });

                        self.toHtml();
                        emitter.fire('change');
                    },

                });
                
            }


            watcher.set(patterns);

        },

        /**
        * 合并对应的 js 文件列表。
        */
        concat: function (options) {

            var meta = mapper.get(this);
            var list = meta.list;
            if (list.length == 0) {
                meta.html = '';
                return;
            }


            if (options === true) { //直接指定了为 true,则使用默认配置。
                options = meta.concat;
            }
           

            list = $.Array.keep(list, function (item) {
                return item.file;
            });

            //加上文件头部和尾部,形成闭包
            var header = options.header;
            if (header) {
                header = Path.format(header);
                FileRefs.add(header);
                list = [header].concat(list);
            }

            var footer = options.footer;
            if (footer) {
                footer = Path.format(footer);
                FileRefs.add(footer);
                list = list.concat(footer);
            }

            var JS = require('JS');
            var content = JS.concat(list, {
                'addPath': options.addPath,
                'delete': options.delete,
            });


            var name = options.name || 32;
            var isMd5Name = typeof name == 'number';  //为数字时,则表示使用 md5 作为名称。
            var md5 = MD5.get(content);

            if (isMd5Name) {
                name = md5.slice(0, name) + '.js';
            }

            var file = meta.dir + name;

            if (options.write) { //写入合并后的 js 文件
                File.write(file, content);
            }


            $.Object.extend(meta.build, {
                'file': file,
                'content': content,
            });


            //更新 html

            var href = name;

            //当不是以 md5 作为名称时,即当成使用固定的名称,如 index.all.debug.js,
            //为了确保能刷新缓存,这里还是强行加进了 md5 值作为 query 部分。
            var len = meta.md5;
            if (len > 0 && !isMd5Name) { 
                href = href + '?' + md5.slice(0, len);
            }
           
            meta.html = $.String.format(meta.sample, {
                'href': href,
            });

        },


        /**
        * 压缩合并后的 js 文件。
        */
        minify: function (options) {
           
            var meta = mapper.get(this);
            if (meta.list.length == 0) {
                meta.html = '';
                return;
            }


            if (options === true) { //直接指定了为 true,则使用默认配置。
                options = meta.minify;
            }


            var build = meta.build;
            var content = build.content;

            if (options.delete) { //删除 concat() 产生的文件
                File.delete(build.file);
            }

            var JS = require('JS');
            content = JS.minify(content);    //直接从内容压缩,不读取文件


            var name = options.name || 32;
            var isMd5Name = typeof name == 'number';  //为数字时,则表示使用 md5 作为名称。
            var md5 = MD5.get(content);

            if (isMd5Name) {
                name = md5.slice(0, name) + '.js';
            }

            var file = meta.dir + name;

            if (options.write) {
                File.write(file, content);
            }


            $.Object.extend(build, {
                'file': file,
                'content': content,
            });

            //更新 html
            var href = name;

            //当不是以 md5 作为名称时,即当成使用固定的名称,如 index.all.debug.js,
            //为了确保能刷新缓存,这里还是强行加进了 md5 值作为 query 部分。
            var len = meta.md5;
            if (len > 0 && !isMd5Name) {
                href = href + '?' + md5.slice(0, len);
            }

            meta.html = $.String.format(meta.sample, {
                'href': href,
            });


        },

        /**
        * 把 js 文件的内容内联到 html 中。
        */
        inline: function (options) {

            var meta = mapper.get(this);
            if (meta.list.length == 0) {
                meta.html = '';
                return;
            }

            if (options === true) {//直接指定了为 true,则使用默认配置。
                options = meta.inline;
            }

            var build = meta.build;
            var content = build.content;

            //删除 concat() 或 minify() 产生的文件
            if (options.delete) {
                File.delete(build.file);
            }
            
            //添加一个随机的 type 值,变成不可执行的 js 代码,
            //可以防止在压缩页面时重复压缩本 js 代码。
            var sample = '<script type="{type}">{content}</script>'
            meta.html = $.String.format(sample, {
                'type': meta.scriptType,
                'content': content,
            });

        },

        /**
        * 移除临时添加进去的 script type,恢复成可执行的 script 代码。
        */
        removeType: function (master) {
            var meta = mapper.get(this);

            var tag = $.String.format('<script type="{type}">', {
                'type': meta.scriptType,
            });

            master = master.split(tag).join('<script>'); //replaceAll
            return master;
        },

        /**
        * 删除模式列表中所对应的 js 物理文件。
        */
        delete: function () {
            var meta = mapper.get(this);

            meta.list.forEach(function (item) {
                FileRefs.delete(item.file);
            });
        },

        /**
        * 绑定事件。
        */
        on: function (name, fn) {
            var meta = mapper.get(this);
            var emitter = meta.emitter;

            var args = [].slice.call(arguments, 0);
            emitter.on.apply(emitter, args);

            return this;
        },

    };


    return $.Object.extend(JsList, {

        //子类,用于提供实例方法:
        //检查 JsList 块里是否包含指定的 script 标签。
        Checker: (function () {

            var tags = null;

            function Checker(master) {
                tags = tags || Defaults.get(module.id).tags;
                this.html = $.String.between(master, tags.begin, tags.end);
            }

            Checker.prototype = {
                constructor: Checker,

                /**
                * 检查 JsList 块里是否包含指定的 script 标签。
                * 该方法主要是给 JsScripts 模块使用。
                * @param {string} 要检查的 html 文本内容。
                * @param {string} script 要检查的 script 标签内容。
                * @return {boolean} 返回一个布尔值,该值指示指定的 script 标签是否出现在 JsList 块里。
                */
                has: function (script) {
                    return this.html.indexOf(script) >= 0;
                },
            };

            return Checker;

        })(),

    });



});
Example #19
0
define('Package', function (require, module, exports) {

    var $ = require('$');
    var path = require('path');

    var File = require('File');
    var FileRefs = require('FileRefs');
    var Path = require('Path');
    var Watcher = require('Watcher');
    var Defaults = require('Defaults');
    var Lines = require('Lines');
    var Url = require('Url');
    var Patterns = require('Patterns');
    var Log = require('Log');

    var Emitter = $.require('Emitter');
    var HtmlPackage = require('HtmlPackage');
    var JsPackage = require('JsPackage');
    var LessPackage = require('LessPackage');

    var mapper = new Map();
    var name$file = {};         //记录包的名称与文件名的对应关系,防止出现重名的包。



    function Package(file, config) {

        config = Defaults.clone(module.id, config);

        var htdocsDir = config.htdocsDir;
        file = Path.join(htdocsDir, file);

        var dir = Path.dirname(file); //分包 package.json 文件所在的目录

        var meta = {
            'dir': dir, 
            'file': file,

            'htdocsDir': htdocsDir,
            'packageDir': config.packageDir,
            'cssDir': config.cssDir,
            'compile': config.compile,
            'minify': config.minify,
            'md5': config.md5,


            'emitter': new Emitter(this),
            'watcher': null, //监控器,首次用到时再创建

            'old': {},      //用来存放旧的 HtmlPackage、JsPackage 和 LessPackage。

            'HtmlPackage': null,
            'JsPackage': null,
            'LessPackage': null,

            'css': '',
            'html': '',
            'js': '',
        };

        mapper.set(this, meta);
    }



    Package.prototype = {
        constructor: Package,

        /**
        * 重置上一次可能存在的结果。
        */
        reset: function () {
            var meta = mapper.get(this);
            var old = meta.old;

            //先备份。 old 中的一旦有值,将再也不会变为 null。
            old.HtmlPackage = meta.HtmlPackage;
            old.JsPackage = meta.JsPackage;
            old.LessPackage = meta.LessPackage;

            //再清空。
            $.Object.extend(meta, {
                'HtmlPackage': null,
                'JsPackage': null,
                'LessPackage': null,

                'css': '',
                'html': '',
                'js': '',
            });
        },

        /**
        * 
        */
        parse: function () {
            var meta = mapper.get(this);
            var file = meta.file;
            var dir = meta.dir;
            var htdocsDir = meta.htdocsDir;
 
            var json = File.readJSON(file);
            var name = json.name;

            //如果未指定 name,则以包文件所在的目录的第一个 js 文件名作为 name。
            if (!name) {
                var files = Patterns.getFiles(dir, '*.js');
                name = files[0];
                if (!name) {
                    console.log('包文件'.bgRed, file.yellow, '中未指定 name 字段,且未在其的所在目录找到任何 js 文件。'.bgRed);
                    throw new Error();
                }
                name = Path.relative(dir, name);
                name = name.slice(0, -3); //去掉 `.js` 后缀。
            }
            else if (name == '*') {
                name = Path.relative(htdocsDir, dir);
                name = name.split('/').join('.');
            }

            var oldFile = name$file[name];
            if (oldFile && oldFile != file) {
                console.log('存在同名'.bgRed, name.green, '的包文件:'.bgRed);
                Log.logArray([oldFile, file], 'yellow');
                throw new Error();
            }

            name$file[name] = file;
            meta.name = name;

            var old = meta.old;
            var packageDir = htdocsDir + meta.packageDir;

            if (json.html) {
                meta.HtmlPackage = old.HtmlPackage || new HtmlPackage(dir);
                meta.html = {
                    'src': json.html,
                    'dir': packageDir,
                    'dest': packageDir + name + '.html',
                    'md5': '',
                };
            }

            if (json.js) {
                meta.JsPackage = old.JsPackage || new JsPackage(dir);
                meta.js = {
                    'src': json.js,
                    'dir': packageDir,
                    'dest': packageDir + name + '.js',
                    'md5': '',
                };
            }

            if (json.css) {
                var cssDir = htdocsDir + meta.cssDir;
                meta.LessPackage = old.LessPackage || new LessPackage(dir);
                meta.css = {
                    'src': json.css,
                    'dir': cssDir,
                    'dest': cssDir + name + '.css',
                    'md5': '',
                };
            }

        },
        
        /**
        * 编译当前包文件。
        */
        compile: function (options, done) {

            //重载 compile(done)
            if (typeof options == 'function') {
                done = options;
                options = null;
            }

            var meta = mapper.get(this);
            var HtmlPackage = meta.HtmlPackage;
            var JsPackage = meta.JsPackage;
            var LessPackage = meta.LessPackage;


            options = options || meta.compile;

            if (HtmlPackage) {
                var file = options.html.write ? meta.html.dest : '';

                HtmlPackage.reset();
                HtmlPackage.get(meta.html.src);

                meta.html.md5 = HtmlPackage.compile(file);

                if (options.html.delete) {
                    HtmlPackage.delete();
                }
            }
           

            if (JsPackage) {
                var js = options.js;
                js.dest = js.write ? meta.js.dest : '';

                JsPackage.reset();
                JsPackage.get(meta.js.src);
                meta.js.md5 = JsPackage.concat(js);
            }
            

            if (LessPackage) {
                var less = options.less;
                var opt = { delete: less.delete };

                LessPackage.reset();
                LessPackage.get(meta.css.src);

                LessPackage.compile(opt, function () {

                    var css = less.write ? meta.css.dest : '';
                    meta.css.md5 = LessPackage.concat(css);

                    done && done();
                });
            }
            else {
                done && done();
            }
           
        },

        /**
        * 压缩。
        */
        minify: function (options, done) {
            //重载 minify(done)
            if (typeof options == 'function') {
                done = options;
                options = null;
            }

            var meta = mapper.get(this);
            var dest = meta.dest;
            var HtmlPackage = meta.HtmlPackage;
            var JsPackage = meta.JsPackage;
            var LessPackage = meta.LessPackage;


            options = options || meta.minify;

            if (HtmlPackage) {
                var opt = options.html;
                if (opt) {
                    if (opt === true) { //当指定为 true 时,则使用默认的压缩选项。
                        opt = meta.minify.html;
                    }

                    var html = meta.html;
                    html.dest = HtmlPackage.minify(opt, {
                        'dir': html.dir,
                        'name': 32,         //md5 的长度。
                    });

                    html.md5 = '';
                }
            }

            if (JsPackage) {
                var opt = options.js;
                if (opt && opt.write) {
                    var js = meta.js;
                    js.dest = JsPackage.minify({
                        'dir': js.dir,
                        'name': 32,
                    });

                    js.md5 = '';
                }
            }
            
            if (LessPackage) {
                var opt = options.less;
                if (opt && opt.write) {
                    var css = meta.css;
                    var dest = {
                        'dir': css.dir,
                        'name': 32,         //md5 的长度。
                    };

                    LessPackage.minify(dest, function (dest, content) {
                        css.dest = dest;
                        css.md5 = '';

                        done && done();
                    });
                }
                else {
                    done && done();
                }
            }
            else {
                done && done();
            }
           
           
        },

        /**
        * 监控当前包文件及各个资源引用模块。
        */
        watch: function () {
            var meta = mapper.get(this);
            var HtmlPackage = meta.HtmlPackage;
            var JsPackage = meta.JsPackage;
            var LessPackage = meta.LessPackage;
            var emitter = meta.emitter;
            var old = meta.old;

            if (HtmlPackage) {
                HtmlPackage.watch();
                if (!old.HtmlPackage) {
                    HtmlPackage.on('change', function () {
                        var html = meta.html;
                        html.md5 = HtmlPackage.compile(html.dest);
                        emitter.fire('change');
                    });
                }
            }
            else if(old.HtmlPackage) {
                old.HtmlPackage.unwatch();
            }


            if (JsPackage) {
                JsPackage.watch();
                if (!old.JsPackage) {
                    JsPackage.on('change', function () {
                        var js = meta.js;
                        js.md5 = JsPackage.concat({ 'dest': js.dest, });
                        emitter.fire('change');
                    });
                }
            }
            else if (old.JsPackage) {
                old.JsPackage.unwatch();
            }

            if (LessPackage) {
                LessPackage.watch();
                if (!old.LessPackage) {
                    LessPackage.on('change', function () {
                        var css = meta.css;
                        css.md5 = LessPackage.concat(css.dest);
                        emitter.fire('change');
                    });
                }
            }
            else if (old.LessPackage) {
                old.LessPackage.unwatch();
            }

       
            var watcher = meta.watcher;
            if (!watcher) {

                var self = this;

                watcher = meta.watcher = new Watcher();
                watcher.set(meta.file);      //这里只需要添加一次

                watcher.on('changed', function () {
                    self.reset();
                    self.parse();       //json 文件发生变化,重新解析。
                    self.compile();     //根节点发生变化,需要重新编译。
                    self.watch();

                    emitter.fire('change');

                });
            }


        },

        /**
        * 构建。
        */
        build: function (options, done) {

            var pkg = this;
            pkg.parse();

            pkg.compile(options.compile, function () {

                pkg.minify(options.minify, function () {
           
                    done && done();

                });
            });

        },

        /**
        * 绑定事件。
        */
        on: function (name, fn) {
            var meta = mapper.get(this);
            var emitter = meta.emitter;

            var args = [].slice.call(arguments, 0);
            emitter.on.apply(emitter, args);

            return this;
        },

        clean: function () {
            var meta = mapper.get(this);
            FileRefs.delete(meta.file);
        },

        /**
        * 获取输出目标包的信息。
        * 该方法由静态方法 write 调用。
        */
        get: function () {
            var meta = mapper.get(this);
            var name = meta.name;
            var htdocsDir = meta.htdocsDir;

            var data = {};

            ['js', 'html', 'css'].forEach(function (type) {

                var item = meta[type];
                if (!item) {
                    return;
                }

                var href = Path.relative(htdocsDir, item.dest);
                var md5 = item.md5.slice(0, meta.md5);

                if (md5) {
                    href = href + '?' + md5;
                }

                data[type] = href;

            });

            var obj = {};
            obj[name] = data;

            return obj;
        },


    };


    //静态方法。
    return $.Object.extend(Package, {

        /**
        * 写入到指定的总包。
        */
        write: function (dest, pkgs, minify) {

            var json = File.readJSON(dest) || {};

            pkgs.forEach(function (pkg) {

                var obj = pkg.get();

                $.Object.extend(json, obj);
            });
      
            File.writeJSON(dest, json, minify);
        },
    });



});
Example #20
0
        'ids': function (html) {

            var ids = html.match(/\s+id\s*=\s*["'][\s\S]*?["']/ig);
            if (!ids) { //没有匹配到 id。
                return;
            }


            var id$stat = {};

            ids.forEach(function (item) {
                var a = item.split(/\s+id\s*=\s*/i);
                var id = a[1].slice(1, -1);

                //包含 `{` 和 `}`,可能是模板中的 id,忽略掉。
                if (id.indexOf('{') >= 0 && id.indexOf('}') > 0) {
                    return;
                }

                var stat = id$stat[id];
                if (stat) {
                    stat.count++;
                    stat.items.push(item);

                    return;
                }

                id$stat[id] = {
                    'items': [item],
                    'count': 1,
                };

            });


            id$stat = $.Object.grep(id$stat, function (id, stat) {
                return stat.count > 1;
            });


            if (Object.keys(id$stat).length == 0) {
                return;
            }


            console.log('使用重复的 id: '.bgRed);
            console.log('');

            var lines = Lines.get(html);


            $.Object.each(id$stat, function (id, stat) {

                console.log(id.red + ':', stat.count.toString().cyan, '次');

                //得到一个二维数组
                var htmls = $.Array.keep(stat.items, function (item) {

                    return $.Array.grep(lines, function (line) {
                        return line.indexOf(item) >= 0;
                    });
                });

                //降成一维
                htmls = $.Array.reduceDimension(htmls);

                //去重
                var obj = {};
                htmls.forEach(function (item) {
                    obj[item] = true;
                });

                htmls = Object.keys(obj).map(function (item) {
                    item = item.split(id).join(id.yellow);
                    item = item.trim(); //为了方便显示,去掉首尾空格。

                    return item;
                });

                console.log(htmls.join('\r\n'));
                console.log('');

            });


            return true;

        },
Example #21
0
        'files': function (stats) {

            var file$stat = stats[0]; //总的 file$stat。

            //合并成一个 file$stat
            stats.slice(1).forEach(function (item) {

                $.Object.each(item, function (file, obj) {

                    var stat = file$stat[file];
                    if (stat) {
                        stat['count'] += obj['count'];
                    }
                    else {
                        file$stat[file] = obj;
                    }
                });
            });


            var md5$files = {};

            $.Object.each(file$stat, function (file, stat) {
                var md5 = stat.md5;
                var files = md5$files[md5];

                if (!files) {
                    files = md5$files[md5] = [];
                }

                files.push(file);

            });




            var file$count = {};    //重复引用同一个文件,根据文件路径识别。
            var md5$list = {};      //内容完全相同的文件,根据文件内容识别。

            $.Object.each(file$stat, function (file, stat) {
                var count = stat['count'];
                if (count > 1) {
                    file$count[file] = count;
                }
            });


            $.Object.each(md5$files, function (md5, files) {
                if (files.length > 1) {
                    md5$list[md5] = files;
                }
            });


            var invalid = false;

            if (Object.keys(file$count).length > 0) {
                invalid = true;
                console.log('重复引用同一个文件: '.bgRed);

                $.Object.each(file$count, function (file, count) {
                    console.log('    ' + file.red + ':', count.toString().cyan, '次');
                });
            }

            if (Object.keys(md5$list).length > 0) {
                invalid = true;
                console.log('内容完全相同的文件: '.bgRed);

                $.Object.each(md5$list, function (md5, list) {
                    console.log(md5.yellow, list.length.toString().cyan, '个:');
                    console.log('    ' + list.join('\r\n    ').red);
                });
            }

            return invalid;
        },
Example #22
0
            pkgs.forEach(function (pkg) {

                var obj = pkg.get();

                $.Object.extend(json, obj);
            });
Example #23
0
        concat: function (options) {

            var meta = mapper.get(this);
            var list = meta.list;
            if (list.length == 0) {
                meta.html = '';
                return;
            }


            if (options === true) { //直接指定了为 true,则使用默认配置。
                options = meta.concat;
            }
           

            list = $.Array.keep(list, function (item) {
                return item.file;
            });

            //加上文件头部和尾部,形成闭包
            var header = options.header;
            if (header) {
                header = Path.format(header);
                FileRefs.add(header);
                list = [header].concat(list);
            }

            var footer = options.footer;
            if (footer) {
                footer = Path.format(footer);
                FileRefs.add(footer);
                list = list.concat(footer);
            }

            var JS = require('JS');
            var content = JS.concat(list, {
                'addPath': options.addPath,
                'delete': options.delete,
            });


            var name = options.name || 32;
            var isMd5Name = typeof name == 'number';  //为数字时,则表示使用 md5 作为名称。
            var md5 = MD5.get(content);

            if (isMd5Name) {
                name = md5.slice(0, name) + '.js';
            }

            var file = meta.dir + name;

            if (options.write) { //写入合并后的 js 文件
                File.write(file, content);
            }


            $.Object.extend(meta.build, {
                'file': file,
                'content': content,
            });


            //更新 html

            var href = name;

            //当不是以 md5 作为名称时,即当成使用固定的名称,如 index.all.debug.js,
            //为了确保能刷新缓存,这里还是强行加进了 md5 值作为 query 部分。
            var len = meta.md5;
            if (len > 0 && !isMd5Name) { 
                href = href + '?' + md5.slice(0, len);
            }
           
            meta.html = $.String.format(meta.sample, {
                'href': href,
            });

        },
Example #24
0
        concat: function (options) {

            var meta = mapper.get(this);
            var list = meta.list;
            if (list.length == 0) { //没有 less 文件
                meta.html = '';
                return;
            }



            if (options === true) { //直接指定了为 true,则使用默认配置。
                options = meta.concat;
            }

            var build = meta.build;
            var cssDir = meta.cssDir;
            var less$item = meta.less$item;
           

            var contents = [];

            list.forEach(function (less) {
                var item = less$item[less];
                contents.push(item.content);

                if (options.delete) { //删除源分 css 文件
                    FileRefs.delete(item.file);
                }
                
            });

            var content = contents.join('');



            var name = options.name || 32;
            var isMd5Name = typeof name == 'number';  //为数字时,则表示使用 md5 作为名称。
            var md5 = MD5.get(content);

            if (isMd5Name) {
                name = md5.slice(0, name) + '.css';
            }

            var file = cssDir + name;

            var href = path.relative(meta.dir, file);
            href = Path.format(href);

            if (options.write) { //写入合并后的 css 文件
                File.write(file, content);
            }


            $.Object.extend(build, {
                'file': file,
                'href': href,
                'content': content,
            });

            //更新 html

            //当不是以 md5 作为名称时,即当成使用固定的名称,如 index.all.debug.css,
            //为了确保能刷新缓存,这里还是强行加进了 md5 值作为 query 部分。
            var len = meta.md5;
            if (len > 0 && !isMd5Name) {
                href = href + '?' + md5.slice(0, len);
            }

            meta.html = $.String.format(meta.sample, {
                'href': href,
            });

        },
Example #25
0
        minify: function (options) {
           
            var meta = mapper.get(this);
            if (meta.list.length == 0) {
                meta.html = '';
                return;
            }


            if (options === true) { //直接指定了为 true,则使用默认配置。
                options = meta.minify;
            }


            var build = meta.build;
            var content = build.content;

            if (options.delete) { //删除 concat() 产生的文件
                File.delete(build.file);
            }

            var JS = require('JS');
            content = JS.minify(content);    //直接从内容压缩,不读取文件


            var name = options.name || 32;
            var isMd5Name = typeof name == 'number';  //为数字时,则表示使用 md5 作为名称。
            var md5 = MD5.get(content);

            if (isMd5Name) {
                name = md5.slice(0, name) + '.js';
            }

            var file = meta.dir + name;

            if (options.write) {
                File.write(file, content);
            }


            $.Object.extend(build, {
                'file': file,
                'content': content,
            });

            //更新 html
            var href = name;

            //当不是以 md5 作为名称时,即当成使用固定的名称,如 index.all.debug.js,
            //为了确保能刷新缓存,这里还是强行加进了 md5 值作为 query 部分。
            var len = meta.md5;
            if (len > 0 && !isMd5Name) {
                href = href + '?' + md5.slice(0, len);
            }

            meta.html = $.String.format(meta.sample, {
                'href': href,
            });


        },