/** * @fileOverview 负责合并amd modules为一个单文件。 */ 'use strict'; var requirejs = require('requirejs'), fs = require('fs'), path = require('path'); // convert relative path to absolute path. function convert( name, _path, contents ) { var rDefine = /(define\s*\(\s*('|").*?\2\s*,\s*\[)([\s\S]*?)\]/ig, rDeps = /('|")(.*?)\1/g, root = _path.substr( 0, _path.length - name.length - 3 ), dir = path.dirname( _path ), m, m2, deps, dep, _path2; contents = contents.replace( rDefine, function( m, m1, m2, m3 ) { return m1 + m3.replace( rDeps, function( m, m1, m2 ) { m2 = path.join( dir, m2 ); m2 = path.relative( root, m2 ); m2 = m2.replace(/\\/g, '/'); return m1 + m2 + m1; }) + ']'; }); return contents; } module.exports = function( grunt ) { grunt.registerMultiTask( 'build', '合并amd modules为一个单文件', function() { var done = this.async(), options = this.options({ banner: '', footer: '', process: null, builtin: { dollar: false, promise: false } }), pkg = grunt.config.get('pkg'), dest = this.data.dest, config, flag, custom; config = { baseUrl: 'src', name: '', out: '', // We have multiple minify steps optimize: 'none', // Include dependencies loaded with require findNestedDependencies: true, // Avoid breaking semicolons inserted by r.js skipSemiColonInsertion: true, wrap: { startFile: 'build/intro.js', endFile: 'build/outro.js' }, rawText: {}, onBuildWrite: function( name, _path ) { var compiled = convert.apply( null, arguments ); if ( options.process ) { compiled = options.process( compiled, _path ); } // 调整缩进 compiled = compiled.replace( /(^|\r\n|\r|\n)/g, '$1 '); compiled = compiled.replace(/@version@/g, function() { return pkg.version; }); return compiled; }, paths: [], include: [] }; options = grunt.util._.extend( options, this.data ); config.name = 'webuploader'; if ( options.builtin.dollar ) { config.rawText.dollar = 'define([\n' + ' \'./dollar-builtin\'\n' + '], function( $ ) {\n' + ' return $;\n' + '});'; } if ( options.builtin.promise ) { config.rawText.promise = 'define([\n' + ' \'./promise-builtin\'\n' + '], function( $ ) {\n' + ' return $;\n' + '});'; } if ( this.data.preset === 'custom' ) { custom = []; this.files.forEach(function( file ) { var files = file.src, cwd = file.cwd || ''; files.filter(function( filepath ) { filepath = path.join( cwd, filepath ); // Warn on and remove invalid source files (if nonull was set). if (!grunt.file.exists(filepath)) { grunt.log.warn('Source file "' + filepath + '" not found.'); return false; } else { return true; } }).forEach(function( filepath ) { custom.push( '\'' + filepath.replace( /\.\w+$/, '' ) + '\'' ); }); }); custom.unshift('\'./base\''); custom = 'define([\n ' + custom.join(',\n ') + '\n], function( Base ) {\n return Base;\n});'; config.rawText.webuploader = custom; } else if (this.data.preset) { config.rawText.webuploader = 'define([\n ' + ['\'./preset/' + this.data.preset +'\''].join(',\n ') + '\n], function( preset ) {\n return preset;\n});'; } else { config.name = this.data.name; } // 处理最终输出 config.out = function( compiled ) { var arr = [], banner = grunt.template.process(options.banner), footer = grunt.template.process(options.footer), sep = '\n\n'; banner && arr.push( banner ); if ( options.builtin.dollar ) { compiled = compiled.replace('define([ \'jquery\' ], exports );', 'define([], exports);'); } arr.push(compiled); footer && arr.push( footer ); // Write concatenated source to file grunt.file.write( dest, arr.join( sep ) ); process.nextTick(function() { // requirejs有bug, callback不一定会执行,目前调试的结果是 // prim的promise实现有问题。 if ( flag ) return; grunt.log.ok( "File '" + dest + "' created." ); done(); flag = true; }); }; if (options.fis) { config.wrap = { startFile: 'build/fis/intro.js', endFile: 'build/fis/outro.js' } } requirejs.optimize( config, function( response ) { // requirejs有bug, callback不一定会执行,目前调试的结果是 // prim的promise实现有问题。 if ( flag ) return; grunt.verbose.writeln( response ); grunt.log.ok( "File '" + name + "' created." ); done(); flag = true; }, function( err ) { done( err ); }); }); };