mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
47835965bd
This commit removes Sizzle from jQuery, inlining its code & removing obsolete workarounds where applicable. The selector-native module has been removed. Further work on the selector module may decrease the size enough that it will no longer be necessary. If it turns out it's still useful, we'll reinstate it but the code will look different anyway as we'll want to share as much code as possible with the existing selector module. The Sizzle AUTHORS.txt file has been merged with the jQuery one - people are sorted by their first contributions to either of the two repositories. The commit reduces the gzipped jQuery size by 1460 bytes compared to master. Closes gh-4395
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
|
|
*/
|
|
|
|
module.exports = function( grunt ) {
|
|
|
|
"use strict";
|
|
|
|
var fs = require( "fs" ),
|
|
requirejs = require( "requirejs" ),
|
|
Insight = require( "insight" ),
|
|
pkg = require( "../../package.json" ),
|
|
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\(\[[^\]]*\]\)[\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 included or excluded
|
|
*/
|
|
excluder = function( flag ) {
|
|
var additional,
|
|
m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
|
|
exclude = m[ 1 ] === "-",
|
|
module = m[ 2 ];
|
|
|
|
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 {
|
|
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 );
|
|
}
|
|
|
|
// Replace exports/global with a noop noConflict
|
|
if ( ( index = excluded.indexOf( "exports/global" ) ) > -1 ) {
|
|
config.rawText[ "exports/global" ] = "define(['../core']," +
|
|
"function( jQuery ) {\njQuery.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([" +
|
|
( 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 = this.args,
|
|
modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "",
|
|
done = this.async(),
|
|
insight = new Insight( {
|
|
trackingCode: "UA-1076265-4",
|
|
pkg: pkg
|
|
} );
|
|
|
|
function exec( trackingAllowed ) {
|
|
var tracks = args.length ? args[ 0 ].split( "," ) : [];
|
|
var defaultPath = [ "build", "custom" ];
|
|
|
|
tracks = tracks.map( function( track ) {
|
|
return track.replace( /\//g, "+" );
|
|
} );
|
|
|
|
if ( trackingAllowed ) {
|
|
|
|
// Track individuals
|
|
tracks.forEach( function( module ) {
|
|
var path = defaultPath.concat( [ "individual" ], module );
|
|
|
|
insight.track.apply( insight, path );
|
|
} );
|
|
|
|
// Track full command
|
|
insight.track.apply( insight, defaultPath.concat( [ "full" ], tracks ) );
|
|
}
|
|
|
|
grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "uglify", "dist" ] );
|
|
done();
|
|
}
|
|
|
|
grunt.log.writeln( "Creating custom build...\n" );
|
|
|
|
// Ask for permission the first time
|
|
if ( insight.optOut === undefined ) {
|
|
insight.askPermission( null, function( _error, result ) {
|
|
exec( result );
|
|
} );
|
|
} else {
|
|
exec( !insight.optOut );
|
|
}
|
|
} );
|
|
};
|