aboutsummaryrefslogtreecommitdiffstats
path: root/build/tasks/build.js
diff options
context:
space:
mode:
Diffstat (limited to 'build/tasks/build.js')
-rw-r--r--build/tasks/build.js381
1 files changed, 174 insertions, 207 deletions
diff --git a/build/tasks/build.js b/build/tasks/build.js
index 63dfc1ee8..188e1e452 100644
--- a/build/tasks/build.js
+++ b/build/tasks/build.js
@@ -1,132 +1,66 @@
/**
- * Special concat/build task to handle various jQuery build requirements
- * Concats AMD modules, removes their definitions,
+ * Special build task to handle various jQuery build requirements.
+ * Compiles JS modules into one bundle, sets the custom AMD name,
* and includes/excludes specified modules
*/
"use strict";
module.exports = function( grunt ) {
- 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;
- }
+ const fs = require( "fs" );
+ const path = require( "path" );
+ const rollup = require( "rollup" );
+ const rollupHypothetical = require( "rollup-plugin-hypothetical" );
+ const Insight = require( "insight" );
+ const pkg = require( "../../package.json" );
+ const srcFolder = path.resolve( `${ __dirname }/../../src` );
+ const 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.
+ const wrapper = read( "wrapper.js" )
+ .split( /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ );
+
+ const inputFileName = "jquery.js";
+ const inputRollupOptions = {
+ input: `${ srcFolder }/${ inputFileName }`
+ };
+ const outputRollupOptions = {
+
+ // The ESM format is not actually used as we strip it during
+ // the build; it's just that it doesn't generate any extra
+ // wrappers so there's nothing for us to remove.
+ format: "esm",
+
+ intro: wrapper[ 0 ]
+ .replace( /\n*$/, "" ),
+ outro: wrapper[ 1 ]
+ .replace( /^\n*/, "" )
+ };
+ const rollupHypotheticalOptions = {
+ allowFallthrough: true,
+ files: {}
+ };
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" ),
+ async function() {
+ const done = this.async();
+
+ try {
+ let flag, index;
+ const flags = this.flags;
+ const optIn = flags[ "*" ];
+ let name = grunt.option( "filename" );
+ const minimum = this.data.minimum;
+ const removeWith = this.data.removeWith;
+ const excluded = [];
+ const included = [];
+ let version = grunt.config( "pkg.version" );
/**
* Recursively calls the excluder to remove on all modules in the list
@@ -134,15 +68,16 @@ module.exports = function( grunt ) {
* @param {String} [prepend] Prepend this to the module name.
* Indicates we're walking a directory
*/
- excludeList = function( list, prepend ) {
+ const excludeList = ( list, prepend ) => {
if ( list ) {
- prepend = prepend ? prepend + "/" : "";
+ prepend = prepend ? `${ prepend }/` : "";
list.forEach( function( module ) {
// Exclude var modules as well
if ( module === "var" ) {
excludeList(
- fs.readdirSync( srcFolder + prepend + module ), prepend + module
+ fs.readdirSync( `${ srcFolder }/${ prepend }${ module }` ),
+ prepend + module
);
return;
}
@@ -164,7 +99,7 @@ module.exports = function( grunt ) {
}
} );
}
- },
+ };
/**
* Adds the specified module to the excluded or included list, depending on the flag
@@ -172,11 +107,11 @@ module.exports = function( grunt ) {
* 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 ];
+ const excluder = flag => {
+ let additional;
+ const m = /^(\+|\-|)([\w\/-]+)$/.exec( flag );
+ const exclude = m[ 1 ] === "-";
+ const module = m[ 2 ];
if ( exclude ) {
@@ -192,7 +127,10 @@ module.exports = function( grunt ) {
// These are the removable dependencies
// It's fine if the directory is not there
try {
- excludeList( fs.readdirSync( srcFolder + module ), module );
+ excludeList(
+ fs.readdirSync( `${ srcFolder }/${ module }` ),
+ module
+ );
} catch ( e ) {
grunt.verbose.writeln( e );
}
@@ -204,7 +142,7 @@ module.exports = function( grunt ) {
if ( additional ) {
excludeList( additional.remove || additional );
if ( additional.include ) {
- included = included.concat( additional.include );
+ included.push( ...additional.include );
grunt.log.writeln( "+" + additional.include );
}
}
@@ -217,93 +155,122 @@ module.exports = function( grunt ) {
}
};
- // 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;
+ // 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;
- }
+ // 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 );
- }
+ // 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 );
- }
+ // Remove the jQuery export from the entry file, we'll use our own
+ // custom wrapper.
+ rollupHypotheticalOptions.files[ inputRollupOptions.input ] = read( inputFileName )
+ .replace( /\n*export default jQuery;\n*/, "\n" );
+
+ // Replace exports/global with a noop noConflict
+ if ( ( index = excluded.indexOf( "exports/global" ) ) > -1 ) {
+ rollupHypotheticalOptions.files[ `${ srcFolder }/exports/global.js` ] =
+ "import jQuery from \"../core.js\";\n\n" +
+ "jQuery.noConflict = function() {};";
+ excluded.splice( index, 1 );
+ }
- grunt.verbose.writeflags( excluded, "Excluded" );
- grunt.verbose.writeflags( included, "Included" );
+ // Set a desired AMD name.
+ let amdName = grunt.option( "amd" );
+ if ( amdName != null ) {
+ if ( amdName ) {
+ grunt.log.writeln( "Naming jQuery with AMD name: " + amdName );
+ } else {
+ grunt.log.writeln( "AMD name now anonymous" );
+ }
- // append excluded modules to version
- if ( excluded.length ) {
- version += " -" + excluded.join( ",-" );
+ // Remove the comma for anonymous defines
+ rollupHypotheticalOptions.files[ `${ srcFolder }/exports/amd.js` ] =
+ read( "exports/amd.js" )
+ .replace( /(\s*)"jquery"(\,\s*)/, amdName ? "$1\"" + amdName + "\"$2" : "" );
+ }
- // 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 );
+ grunt.verbose.writeflags( excluded, "Excluded" );
+ grunt.verbose.writeflags( included, "Included" );
- // Have to use shallow or core will get excluded since it is a dependency
- config.excludeShallow = excluded;
- }
- config.include = 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 );
+
+ // Replace excluded modules with empty sources.
+ for ( const module of excluded ) {
+ rollupHypotheticalOptions.files[ `${ srcFolder }/${ module }.js` ] = "";
+ }
+ }
+
+ // Turn off opt-in if necessary
+ if ( !optIn ) {
- /**
- * Handle Final output from the optimizer
- * @param {String} compiled
- */
- config.out = function( compiled ) {
- compiled = compiled
+ // Remove the default inclusions, they will be overwritten with the explicitly
+ // included ones.
+ rollupHypotheticalOptions.files[ inputRollupOptions.input ] = "";
+
+ }
+
+ // Import the explicitly included modules.
+ if ( included.length ) {
+ rollupHypotheticalOptions.files[ inputRollupOptions.input ] += included
+ .map( module => `import "./${module}.js";` )
+ .join( "\n" );
+ }
+
+ const bundle = await rollup.rollup( {
+ ...inputRollupOptions,
+ plugins: [ rollupHypothetical( rollupHypotheticalOptions ) ]
+ } );
+
+ const { output: [ { code } ] } = await bundle.generate( outputRollupOptions );
+
+ const compiledContents = code
// 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( "," ) : "" ) +
- "]);";
- }
+ .replace(
+ /@DATE/g,
+ ( new Date() ).toISOString()
+ .replace( /:\d+\.\d+Z$/, "Z" )
+ );
- // Trace dependencies and concatenate files
- requirejs.optimize( config, function( response ) {
- grunt.verbose.writeln( response );
+ grunt.file.write( name, compiledContents );
grunt.log.ok( "File '" + name + "' created." );
done();
- }, function( err ) {
+ } catch ( err ) {
done( err );
- } );
+ }
} );
// Special "alias" task to make custom build creation less grawlix-y
@@ -315,17 +282,17 @@ module.exports = function( grunt ) {
//
// 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
- } );
+ const args = this.args;
+ const modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "";
+ const done = this.async();
+ const insight = new Insight( {
+ trackingCode: "UA-1076265-4",
+ pkg: pkg
+ } );
function exec( trackingAllowed ) {
- var tracks = args.length ? args[ 0 ].split( "," ) : [];
- var defaultPath = [ "build", "custom" ];
+ let tracks = args.length ? args[ 0 ].split( "," ) : [];
+ const defaultPath = [ "build", "custom" ];
tracks = tracks.map( function( track ) {
return track.replace( /\//g, "+" );
@@ -335,7 +302,7 @@ module.exports = function( grunt ) {
// Track individuals
tracks.forEach( function( module ) {
- var path = defaultPath.concat( [ "individual" ], module );
+ const path = defaultPath.concat( [ "individual" ], module );
insight.track.apply( insight, path );
} );