mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
37b04d5aba
Close gh-5181
364 lines
10 KiB
JavaScript
364 lines
10 KiB
JavaScript
/**
|
|
* Special concat/build task to handle various jQuery build requirements
|
|
* Concats AMD modules, removes their definitions,
|
|
* and includes/excludes specified modules
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
module.exports = function( grunt ) {
|
|
var fs = require( "fs" ),
|
|
requirejs = require( "requirejs" ),
|
|
slimBuildFlags = require( "./lib/slim-build-flags" ),
|
|
srcFolder = __dirname + "/../../src/",
|
|
rdefineEnd = /\}\s*?\);[^}\w]*$/,
|
|
read = function( fileName ) {
|
|
return grunt.file.read( srcFolder + fileName );
|
|
},
|
|
|
|
// Catch `// @CODE` and subsequent comment lines event if they don't start
|
|
// in the first column.
|
|
wrapper = read( "wrapper.js" ).split( /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ ),
|
|
|
|
config = {
|
|
baseUrl: "src",
|
|
name: "jquery",
|
|
|
|
// Allow strict mode
|
|
useStrict: true,
|
|
|
|
// We have multiple minify steps
|
|
optimize: "none",
|
|
|
|
// Include dependencies loaded with require
|
|
findNestedDependencies: true,
|
|
|
|
// Avoid inserting define() placeholder
|
|
skipModuleInsertion: true,
|
|
|
|
// Avoid breaking semicolons inserted by r.js
|
|
skipSemiColonInsertion: true,
|
|
wrap: {
|
|
start: wrapper[ 0 ].replace( /\/\*\s*eslint(?: |-).*\s*\*\/\n/, "" ),
|
|
end: wrapper[ 1 ]
|
|
},
|
|
rawText: {},
|
|
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 ) {
|
|
var amdName;
|
|
|
|
// Convert var modules
|
|
if ( /.\/var\//.test( path.replace( process.cwd(), "" ) ) ) {
|
|
contents = contents
|
|
.replace(
|
|
/define\(\s*(["'])[\w\W]*?\1[\w\W]*?return/,
|
|
"var " +
|
|
( /var\/([\w-]+)/.exec( name )[ 1 ] ) +
|
|
" ="
|
|
)
|
|
.replace( rdefineEnd, "" );
|
|
|
|
} else {
|
|
|
|
contents = contents
|
|
.replace( /\s*return\s+[^\}]+(\}\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\([^{]*?{\s*(?:("|')use strict\1(?:;|))?/, "" )
|
|
.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\(\s*\[[^\]]*\]\s*\)[\W\n]+$/, "" );
|
|
}
|
|
|
|
// AMD Name
|
|
if ( ( amdName = grunt.option( "amd" ) ) != null && /^exports\/amd$/.test( name ) ) {
|
|
if ( amdName ) {
|
|
grunt.log.writeln( "Naming jQuery with AMD name: " + amdName );
|
|
} else {
|
|
grunt.log.writeln( "AMD name now anonymous" );
|
|
}
|
|
|
|
// Remove the comma for anonymous defines
|
|
contents = contents
|
|
.replace( /(\s*)"jquery"(\,\s*)/, amdName ? "$1\"" + amdName + "\"$2" : "" );
|
|
|
|
}
|
|
return contents;
|
|
}
|
|
|
|
grunt.registerMultiTask(
|
|
"build",
|
|
"Concatenate source, remove sub AMD definitions, " +
|
|
"(include/exclude modules with +/- flags), embed date/version",
|
|
function() {
|
|
var flag, index,
|
|
done = this.async(),
|
|
flags = this.flags,
|
|
optIn = flags[ "*" ],
|
|
name = grunt.option( "filename" ),
|
|
minimum = this.data.minimum,
|
|
removeWith = this.data.removeWith,
|
|
excluded = [],
|
|
included = [],
|
|
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
|
|
*/
|
|
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 be included or excluded
|
|
*/
|
|
excluder = function( flag ) {
|
|
var additional,
|
|
m = /^(\+|-|)([\w\/-]+)$/.exec( flag ),
|
|
exclude = m[ 1 ] === "-",
|
|
module = m[ 2 ];
|
|
|
|
// Recognize the legacy `sizzle` alias
|
|
if ( module === "sizzle" ) {
|
|
module = "selector";
|
|
}
|
|
|
|
if ( exclude ) {
|
|
|
|
// Can't exclude certain modules
|
|
if ( minimum.indexOf( module ) === -1 ) {
|
|
|
|
// Add to excluded
|
|
if ( excluded.indexOf( module ) === -1 ) {
|
|
grunt.log.writeln( flag );
|
|
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 {
|
|
|
|
// `selector` is a special case as we don't just remove
|
|
// the module, but we replace it with `selector-native`
|
|
// which re-uses parts of the `src/selector` folder.
|
|
if ( module !== "selector" ) {
|
|
excludeList(
|
|
fs.readdirSync( `${ srcFolder }/${ module }` ),
|
|
module
|
|
);
|
|
}
|
|
} catch ( e ) {
|
|
grunt.verbose.writeln( e );
|
|
}
|
|
}
|
|
|
|
additional = removeWith[ module ];
|
|
|
|
// Check removeWith list
|
|
if ( additional ) {
|
|
excludeList( additional.remove || additional );
|
|
if ( additional.include ) {
|
|
included = included.concat( additional.include );
|
|
grunt.log.writeln( "+" + additional.include );
|
|
}
|
|
}
|
|
} else {
|
|
grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
|
|
}
|
|
} else {
|
|
grunt.log.writeln( flag );
|
|
included.push( module );
|
|
}
|
|
};
|
|
|
|
// Filename can be passed to the command line using
|
|
// command line options
|
|
// e.g. grunt build --filename=jquery-custom.js
|
|
name = name ? ( "dist/" + name ) : this.data.dest;
|
|
|
|
// 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 );
|
|
}
|
|
|
|
// Handle full selector module exclusion.
|
|
// Replace with selector-native.
|
|
if ( excluded.indexOf( "selector" ) > -1 ) {
|
|
config.rawText.selector = "define([ \"./selector-native\" ]);";
|
|
}
|
|
|
|
// Replace exports/global with a noop noConflict
|
|
if ( ( index = excluded.indexOf( "exports/global" ) ) > -1 ) {
|
|
config.rawText[ "exports/global" ] = "define( [\n\t\"../core\"\n], " +
|
|
"function( jQuery ) {\n\tjQuery.noConflict = function() {};\n} );";
|
|
excluded.splice( index, 1 );
|
|
}
|
|
|
|
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 Version
|
|
.replace( /@VERSION/g, version )
|
|
|
|
// Embed Date
|
|
// yyyy-mm-ddThh:mmZ
|
|
.replace( /@DATE/g, ( new Date() ).toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );
|
|
|
|
// 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.jquery = "define( [\n" +
|
|
( included.length ?
|
|
included.map( module => "\t\"./" + module + "\"" ).join( ",\n" ) :
|
|
"" ) +
|
|
"\n] );";
|
|
}
|
|
|
|
// 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
|
|
//
|
|
// There's also a special "slim" alias that resolves to the jQuery Slim build
|
|
// configuration:
|
|
//
|
|
// grunt custom:slim
|
|
grunt.registerTask( "custom", function() {
|
|
var args = this.args,
|
|
modules = args.length ?
|
|
args[ 0 ]
|
|
.split( "," )
|
|
|
|
// Replace "slim" with respective exclusions meant for
|
|
// the official slim build
|
|
.reduce( ( acc, elem ) => acc.concat(
|
|
elem === "slim" ?
|
|
slimBuildFlags :
|
|
[ elem ]
|
|
), [] )
|
|
|
|
.join( ":" ) :
|
|
"";
|
|
|
|
grunt.log.writeln( "Creating custom build...\n" );
|
|
grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "uglify", "dist" ] );
|
|
} );
|
|
};
|