module.exports = function()

in build/tasks/build.js [6:247]


module.exports = function( grunt ) {

    "use strict";

    var fs = require( "fs" );
    var requirejs = require("requirejs");
    var srcFolder = __dirname + "/../../xf/";
    var rdefineEnd = /\}\);[^}\w]*$/;
    var config = {
        baseUrl: "xf/src",
        name: "xf.framework",
        out: "js/xf.js",
        // We have multiple minify steps
        optimize: "none",
        // Include dependencies loaded with require
        findNestedDependencies: true,
        // Avoid breaking semicolons inserted by r.js
        skipSemiColonInsertion: true,
        wrap: {
            start: '/*! X-Framework @DATE */\n;(function (window, $, BB) {',
            end: '}).call(this, window, $, Backbone); \n\n'
        },
        rawText: {},
        paths: {
            jquery: 'empty:',
            underscore: 'empty:',
            backbone: 'empty:'
        },
        onBuildWrite: convert
    };

    /**
     * Strip all definitions generated by requirejs
     * Convert "var" modules to var declarations
     * "var module" means the module only contains a return statement that should be converted to a var declaration
     * This is indicated by including the file in any "var" folder
     * @param {String} name
     * @param {String} path
     * @param {String} contents The contents to be written (including their AMD wrappers)
     */
    function convert( name, path, contents ) {
        // Convert var modules.
        if ( /.\/var\//.test( path ) ) {
            contents = contents
                    .replace( /define\([\w\W]*?return/, "var " + (/var\/([\w-]+)/.exec(name)[1]) + " =" )
                    .replace( rdefineEnd, "" );

        } else {

            // Ignore exports, XF is exported when it is declared in xf.core.js.
            contents = contents
                .replace( /\s*return\s+[^\}]+(\}\);[^\w\}]*)$/, "$1" )
                // Multiple exports
                .replace( /\s*exports\.\w+\s*=\s*\w+;/g, "" );

            // Remove define wrappers, closure ends, and empty declarations.
            contents = contents
                    .replace( /define\([^{]*?{/, "" )
                    .replace( rdefineEnd, "" );

            // Remove anything wrapped with
            // /* ExcludeStart */ /* ExcludeEnd */
            // or a single line directly after a // BuildExclude comment
            contents = contents
                    .replace( /\/\*\s*ExcludeStart\s*\*\/[\w\W]*?\/\*\s*ExcludeEnd\s*\*\//ig, "" )
                    .replace( /\/\/\s*BuildExclude\n\r?[\w\W]*?\n\r?/ig, "" );

            // Remove empty definitions
            contents = contents
                    .replace( /define\(\[[^\]]+\]\)[\W\n]+$/, "" );
        }
        return contents;
    }

    grunt.registerMultiTask(
            "build",
            "Concatenate source, remove sub AMD definitions, (include/exclude modules with +/- flags), embed date/version",
            function() {
                var flag;
                var index;
                var done = this.async();
                var flags = this.flags;
                var optIn = flags[ "*" ];
                var name = this.data.dest;
                var minimum = ['core'];
                var removeWith = [];
                var excluded = [];
                var included = [];
                var version = grunt.config("pkg.version");
                /**
                 * Recursively calls the excluder to remove on all modules in the list.
                 * @param {Array} list
                 * @param {String} [prepend] Prepend this to the module name. Indicates we're walking a directory.
                 */
                var excludeList = function (list, prepend) {
                    if (list) {
                        prepend = prepend ? prepend + "/" : "";
                        list.forEach(function (module) {
                            // Exclude var modules as well
                            if (module === "var") {
                                excludeList(fs.readdirSync(srcFolder + prepend + module), prepend + module);
                                return;
                            }
                            if (prepend) {
                                // Skip if this is not a js file and we're walking files in a dir.
                                if (!(module = /([\w-\/]+)\.js$/.exec(module))) {
                                    return;
                                }
                                // Prepend folder name if passed.
                                // Remove .js extension.
                                module = prepend + module[1];
                            }

                            // Avoid infinite recursion.
                            if (excluded.indexOf(module) === -1) {
                                excluder("-" + module);
                            }
                        });
                    }
                };
                /**
                 * Adds the specified module to the excluded or included list, depending on the flag
                 * @param {String} flag A module path relative to the src directory starting with + or - to indicate whether it should included or excluded
                 */
                var excluder = function (flag) {
                    var m = /^(\+|\-|)([\w\/\.-]+)$/.exec(flag);
                    var exclude = m[ 1 ] === "-";
                    var module = m[ 2 ];
                    if (exclude) {
                        // Can't exclude certain modules
                        if (minimum.indexOf(module) === -1) {
                            // Add to excluded
                            if (excluded.indexOf(module) === -1) {
                                excluded.push(module);
                                // Exclude all files in the folder of the same name
                                // These are the removable dependencies
                                // It's fine if the directory is not there
                                try {
                                    excludeList(fs.readdirSync(srcFolder + module), module);
                                } catch (e) {
                                    grunt.verbose.writeln(e);
                                }
                            }
                            // Check removeWith list
                            excludeList(removeWith[ module ]);
                        } else {
                            grunt.log.error("Module \"" + module + "\" is a mimimum requirement.");
                        }
                    } else {
                        grunt.log.writeln(flag);
                        included.push(module);
                    }
                };

                // append commit id to version
                if ( process.env.COMMIT ) {
                    version += " " + process.env.COMMIT;
                }

                // figure out which files to exclude based on these rules in this order:
                //  dependency explicit exclude
                //  > explicit exclude
                //  > explicit include
                //  > dependency implicit exclude
                //  > implicit exclude
                // examples:
                //  *                  none (implicit exclude)
                //  *:*                all (implicit include)
                //  *:*:-css           all except css and dependents (explicit > implicit)
                //  *:*:-css:+effects  same (excludes effects because explicit include is trumped by explicit exclude of dependency)
                //  *:+effects         none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
                delete flags[ "*" ];
                for ( flag in flags ) {
                    excluder( flag );
                }

                // Exclude second inclusion of xf.* modules, required by xf.ui components.
                // TODO(jauhen): Move xf/ui folder to proper location to avoid this hack.
                ['-../src/xf.core', '-../src/xf.utils', '-../src/xf.device', '-../src/dom/dom'].forEach(function (module) {
                   excluder(module);
                });

                grunt.verbose.writeflags( excluded, "Excluded" );
                grunt.verbose.writeflags( included, "Included" );

                // Append excluded modules to version.
                if ( excluded.length ) {
                    version += " -" + excluded.join( ",-" );
                    // set pkg.version to version with excludes, so minified file picks it up
                    grunt.config.set( "pkg.version", version );
                    grunt.verbose.writeln( "Version changed to " + version );
                    // Have to use shallow or core will get excluded since it is a dependency
                    config.excludeShallow = excluded;
                }
                config.include = included;

                /**
                 * Handle Final output from the optimizer.
                 * @param {String} compiled
                 */
                config.out = function( compiled ) {
                    compiled = compiled
                        // Embed Date
                            .replace( /@DATE/g, grunt.template.today("dd-mm-yyyy") );

                    // Write concatenated source to file
                    grunt.file.write( name, compiled );
                };

                // Turn off opt-in if necessary
                if ( !optIn ) {
                    // Overwrite the default inclusions with the explicit ones provided
                    config.rawText.xf = "define([" + (included.length ? included.join(",") : "") + "]);";
                }

                // Trace dependencies and concatenate files
                requirejs.optimize( config, function( response ) {
                    grunt.verbose.writeln( response );
                    grunt.log.ok( "File '" + name + "' created." );
                    done();
                }, function( err ) {
                    done( err );
                });
            });

    // Special "alias" task to make custom build creation less grawlix-y
    // Translation example
    //
    //   grunt custom:+ajax,-dimensions,-effects,-offset
    //
    // Becomes:
    //
    //   grunt build:*:*:+ajax:-dimensions:-effects:-offset
    grunt.registerTask( "custom", function() {
        var args = [].slice.call( arguments ),
                modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "";

        grunt.log.writeln( "Creating custom build...\n" );

        grunt.task.run([ "build:*:*:" + modules, "uglify", "dist" ]);
    });
};