Build: Generate the slim build on grunt & run compare_size on it

Summary of the changes:
* expand `node_smoke_tests` to test the full & slim builds
* run `compare_size` on all built minified files; don't run it anymore on
  unminified files where they don't provide lots of value

The main goal of this change is to make it easier to compare sizes of both the
full & slim builds between the `3.x-stable` & `main` branches.

Closes gh-5291
Ref gh-5255

(partially cherry-picked from commit 8be4c0e4f8)
This commit is contained in:
Michał Gołębiowski-Owczarek 2023-07-10 20:45:30 +02:00 committed by GitHub
parent a288838c6f
commit 763ade6dda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 199 additions and 108 deletions

View File

@ -4,6 +4,8 @@ node_modules
dist/**
!dist/jquery.js
!dist/jquery.min.js
!dist/jquery.slim.js
!dist/jquery.slim.min.js
test/data/jquery-1.9.1.js
test/data/badcall.js
test/data/badjson.js

2
.gitignore vendored
View File

@ -12,7 +12,7 @@ package-lock.json
npm-debug.log*
# Ignore everything in dist folder except for eslint config
# Ignore everything in `dist` folder except for the ESLint config
/dist/*
!/dist/.eslintrc.json

View File

@ -6,10 +6,7 @@
/.mailmap
/build
/external
/speed
/test
/Gruntfile.js
/external/qunit
/external/requirejs
/external/sinon

View File

@ -13,19 +13,19 @@ module.exports = function( grunt ) {
}
const fs = require( "fs" );
const { spawn } = require( "child_process" );
const gzip = require( "gzip-js" );
const nodeV14OrNewer = !/^v1[0-3]\./.test( process.version );
const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
const nodeV17OrNewer = !/^v1[0-6]\./.test( process.version );
const customBrowsers = process.env.BROWSERS && process.env.BROWSERS.split( "," );
// Support: Node.js <14
// Skip running tasks that dropped support for Node.js 10 or 12
// in this Node version.
// Support: Node.js <16
// Skip running tasks that dropped support for old Node.js in these Node versions.
function runIfNewNode( task ) {
return nodeV14OrNewer ? task : "print_old_node_message:" + task;
return nodeV16OrNewer ? task : "print_old_node_message:" + task;
}
if ( nodeV14OrNewer ) {
if ( nodeV16OrNewer ) {
const playwright = require( "playwright-webkit" );
process.env.WEBKIT_HEADLESS_BIN = playwright.webkit.executablePath();
}
@ -34,11 +34,21 @@ module.exports = function( grunt ) {
grunt.option( "filename", "jquery.js" );
}
const builtJsFiles = [
"dist/jquery.js",
"dist/jquery.min.js",
"dist/jquery.slim.js",
"dist/jquery.slim.min.js"
];
const builtJsMinFiles = builtJsFiles
.filter( filepath => filepath.endsWith( ".min.js" ) );
grunt.initConfig( {
pkg: grunt.file.readJSON( "package.json" ),
dst: readOptionalJSON( "dist/.destination.json" ),
"compare_size": {
files: [ "dist/jquery.js", "dist/jquery.min.js" ],
compare_size: {
files: builtJsMinFiles,
options: {
compress: {
gz: function( contents ) {
@ -124,7 +134,7 @@ module.exports = function( grunt ) {
// We have to explicitly declare "src" property otherwise "newer"
// task wouldn't work properly :/
dist: {
src: [ "dist/jquery.js", "dist/jquery.min.js" ]
src: builtJsFiles
},
dev: {
src: [ "src/**/*.js", "Gruntfile.js", "test/**/*.js", "build/**/*.js" ]
@ -308,7 +318,7 @@ module.exports = function( grunt ) {
uglify: {
all: {
files: {
"dist/<%= grunt.option('filename').replace('.js', '.min.js') %>":
"dist/<%= grunt.option('filename').replace(/\\.js$/, '.min.js') %>":
"dist/<%= grunt.option('filename') %>"
},
options: {
@ -343,7 +353,7 @@ module.exports = function( grunt ) {
// Load grunt tasks from NPM packages
require( "load-grunt-tasks" )( grunt, {
pattern: nodeV14OrNewer ? [ "grunt-*" ] : [ "grunt-*", "!grunt-eslint" ]
pattern: nodeV16OrNewer ? [ "grunt-*" ] : [ "grunt-*", "!grunt-eslint" ]
} );
// Integrate jQuery specific tasks
@ -354,6 +364,20 @@ module.exports = function( grunt ) {
grunt.log.writeln( "Old Node.js detected, running the task \"" + task + "\" skipped..." );
} );
grunt.registerTask( "build-all-variants",
"Build both the full & slim builds",
function() {
const done = this.async();
spawn( "npm run build-all-variants", {
stdio: "inherit",
shell: true
} )
.on( "close", code => {
done( code === 0 );
} );
} );
grunt.registerTask( "print_jsdom_message", () => {
grunt.log.writeln( "Node.js 17 or newer detected, skipping jsdom tests..." );
} );
@ -377,7 +401,7 @@ module.exports = function( grunt ) {
runIfNewNode( "newer:eslint:dist" )
] );
grunt.registerTask( "test:fast", runIfNewNode( "node_smoke_tests" ) );
grunt.registerTask( "test:fast", [ "node_smoke_tests" ] );
grunt.registerTask( "test:slow", [
runIfNewNode( "promises_aplus_tests" ),
@ -410,10 +434,7 @@ module.exports = function( grunt ) {
grunt.registerTask( "default", [
runIfNewNode( "eslint:dev" ),
"build:*:*",
"uglify",
"remove_map_comment",
"dist:*",
"build-all-variants",
"test:prepare",
runIfNewNode( "eslint:dist" ),
"test:fast",

View File

@ -117,7 +117,7 @@ The build process shows a message for each dependent module it excludes or inclu
##### AMD name
As an option, you can set the module name for jQuery's AMD definition. By default, it is set to "jquery", which plays nicely with plugins and third-party libraries, but there may be cases where you'd like to change this. Simply set the `"amd"` option:
As an option, you can set the module name for jQuery's AMD definition. By default, it is set to "jquery", which plays nicely with plugins and third-party libraries, but there may be cases where you'd like to change this. Simply pass it to the `--amd` parameter:
```bash
grunt custom --amd="custom-name"
@ -129,6 +129,16 @@ Or, to define anonymously, set the name to an empty string.
grunt custom --amd=""
```
##### File name
The default name for the built jQuery file is `jquery.js`; it is placed under the `dist/` directory. It's possible to change the file name using the `--filename` parameter:
```bash
grunt custom:slim --filename="jquery.slim.js"
```
This would create a slim version of jQuery and place it under `dist/jquery.slim.js`. In fact, this is exactly the command we use to generate the slim jQuery during the release process.
#### Custom Build Examples
To create a custom build, first check out the version:

View File

@ -42,12 +42,8 @@ module.exports = function( Release ) {
* @param {Function} callback
*/
generateArtifacts: function( callback ) {
Release.exec( "npx grunt", "Grunt command failed" );
Release.exec(
"npx grunt custom:slim --filename=jquery.slim.js && " +
"npx grunt remove_map_comment --filename=jquery.slim.js",
"Grunt custom failed"
);
Release.exec( "npx grunt" );
cdn.makeReleaseCopies( Release );
Release._setSrcVersion();
callback( filesToCommit );
@ -69,10 +65,9 @@ module.exports = function( Release ) {
* Publish to distribution repo and npm
* @param {Function} callback
*/
dist: function( callback ) {
cdn.makeArchives( Release, function() {
dist( Release, distFiles, callback );
} );
dist: async callback => {
await cdn.makeArchives( Release );
dist( Release, distFiles, callback );
}
} );
};

View File

@ -358,6 +358,11 @@ module.exports = function( grunt ) {
"";
grunt.log.writeln( "Creating custom build...\n" );
grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "uglify", "dist" ] );
grunt.task.run( [
"build:*:*" + ( modules ? ":" + modules : "" ),
"uglify",
"remove_map_comment",
"dist"
] );
} );
};

View File

@ -1,17 +1,17 @@
"use strict";
module.exports = function( grunt ) {
var fs = require( "fs" ),
filename = grunt.option( "filename" ),
distpaths = [
"dist/" + filename,
"dist/" + filename.replace( ".js", ".min.map" ),
"dist/" + filename.replace( ".js", ".min.js" )
];
const fs = require( "fs" );
const filename = grunt.option( "filename" );
const distPaths = [
`dist/${ filename }`,
`dist/${ filename.replace( ".js", ".min.js" ) }`,
`dist/${ filename.replace( ".js", ".min.map" ) }`
];
// Process files for distribution
grunt.registerTask( "dist", function() {
var stored, flags, paths, nonascii;
let stored, flags, paths, nonascii;
// Check for stored destination paths
// ( set in dist/.destination.json )
@ -28,9 +28,9 @@ module.exports = function( grunt ) {
// Ensure the dist files are pure ASCII
nonascii = false;
distpaths.forEach( function( filename ) {
var i, c,
text = fs.readFileSync( filename, "utf8" );
distPaths.forEach( function( filename ) {
let i, c;
const text = fs.readFileSync( filename, "utf8" );
// Ensure files use only \n for line endings, not \r\n
if ( /\x0d\x0a/.test( text ) ) {
@ -54,7 +54,7 @@ module.exports = function( grunt ) {
// Optionally copy dist files to other locations
paths.forEach( function( path ) {
var created;
let created;
if ( !/\/$/.test( path ) ) {
path += "/";

View File

@ -3,29 +3,40 @@
module.exports = ( grunt ) => {
const fs = require( "fs" );
const spawnTest = require( "./lib/spawn_test.js" );
const testsDir = "./test/node_smoke_tests/";
const nodeSmokeTests = [];
const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
// Fire up all tests defined in test/node_smoke_tests/*.js in spawned sub-processes.
// All the files under test/node_smoke_tests/*.js are supposed to exit with 0 code
// on success or another one on failure. Spawning in sub-processes is
// important so that the tests & the main process don't interfere with
// each other, e.g. so that they don't share the require cache.
grunt.registerTask( "node_smoke_tests", function( jQueryModuleSpecifier = "./dist/jquery.js" ) {
if ( !nodeV16OrNewer ) {
grunt.log.writeln( "Old Node.js detected, running the task " +
`"node_smoke_tests:${ jQueryModuleSpecifier }" skipped...` );
return;
}
fs.readdirSync( testsDir )
.filter( ( testFilePath ) =>
fs.statSync( testsDir + testFilePath ).isFile() &&
/\.js$/.test( testFilePath )
)
.forEach( ( testFilePath ) => {
const taskName = `node_${ testFilePath.replace( /\.js$/, "" ) }`;
const testsDir = "./test/node_smoke_tests";
const nodeSmokeTests = [];
grunt.registerTask( taskName, function() {
spawnTest( this.async(), `node "test/node_smoke_tests/${ testFilePath }"` );
// Fire up all tests defined in test/node_smoke_tests/*.js in spawned sub-processes.
// All the files under test/node_smoke_tests/*.js are supposed to exit with 0 code
// on success or another one on failure. Spawning in sub-processes is
// important so that the tests & the main process don't interfere with
// each other, e.g. so that they don't share the `require` cache.
fs.readdirSync( testsDir )
.filter( ( testFilePath ) =>
fs.statSync( `${ testsDir }/${ testFilePath }` ).isFile() &&
/\.[cm]?js$/.test( testFilePath )
)
.forEach( ( testFilePath ) => {
const taskName = `node_${ testFilePath.replace( /\.[cm]?js$/, "" ) }:${ jQueryModuleSpecifier }`;
grunt.registerTask( taskName, function() {
spawnTest( this.async(), `node "${ testsDir }/${
testFilePath }" ${ jQueryModuleSpecifier }` );
} );
nodeSmokeTests.push( taskName );
} );
nodeSmokeTests.push( taskName );
} );
grunt.registerTask( "node_smoke_tests", nodeSmokeTests );
grunt.task.run( nodeSmokeTests );
} );
};

16
dist/.eslintrc.json vendored
View File

@ -8,12 +8,26 @@
"overrides": [
{
"files": "jquery.js",
"files": "jquery{,.slim}.min.js",
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "script"
}
},
{
"files": "jquery{,.slim}.js",
"extends": "../.eslintrc-browser.json",
"rules": {
// That is okay for the built version
"no-multiple-empty-lines": "off",
// When custom compilation is used, the version string
// can get large. Accept that in the built version.
"max-len": "off",
"one-var": "off"
}
}

View File

@ -68,15 +68,19 @@
"uglify-js": "3.4.7"
},
"scripts": {
"build": "npm install && grunt",
"build": "npm install && npm run build-all-variants",
"build-all-variants": "grunt custom:slim --filename=jquery.slim.js && grunt custom",
"start": "grunt watch",
"test:browserless": "grunt && grunt test:slow",
"test:browserless": "grunt && npm run test:node_smoke_tests && grunt test:slow",
"test:browser": "grunt && grunt karma:main",
"test:amd": "grunt && grunt karma:amd",
"test:no-deprecated": "grunt test:prepare && grunt custom:-deprecated && grunt karma:main",
"test:selector-native": "grunt test:prepare && grunt custom:-selector && grunt karma:main",
"test:slim": "grunt test:prepare && grunt custom:slim && grunt karma:main",
"test": "npm run test:slim && npm run test:no-deprecated && npm run test:selector-native && grunt && grunt test:slow && grunt karma:main && grunt karma:amd",
"test:node_smoke_tests:full": "grunt node_smoke_tests:./dist/jquery.js",
"test:node_smoke_tests:slim": "grunt node_smoke_tests:./dist/jquery.slim.js",
"test:node_smoke_tests": "npm run test:node_smoke_tests:full && npm run test:node_smoke_tests:slim",
"test": "npm run test:browserless && npm run test:slim && npm run test:no-deprecated && npm run test:selector-native && grunt && grunt test:slow && grunt karma:main && grunt karma:amd",
"jenkins": "npm run test:browserless"
},
"commitplease": {

View File

@ -4,10 +4,10 @@
"extends": "../../.eslintrc-node.json",
"parserOptions": {
"ecmaVersion": 6
"ecmaVersion": 2015,
"sourceType": "script"
},
"env": {
"es6": true
"es2020": true
}
}

View File

@ -1,8 +1,12 @@
"use strict";
const assert = require( "assert" );
const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" );
const jQueryFactory = require( "../../dist/jquery.js" );
const assert = require( "node:assert" );
const { ensureGlobalNotCreated } = require( "./lib/ensure_global_not_created" );
const { getJQueryModuleSpecifier } = require( "./lib/jquery-module-specifier" );
const jQueryModuleSpecifier = getJQueryModuleSpecifier();
const jQueryFactory = require( jQueryModuleSpecifier );
assert.throws( () => {
jQueryFactory( {} );

View File

@ -2,11 +2,15 @@
const { JSDOM } = require( "jsdom" );
const { ensureJQuery } = require( "./lib/ensure_jquery" );
const { ensureGlobalNotCreated } = require( "./lib/ensure_global_not_created" );
const { getJQueryModuleSpecifier } = require( "./lib/jquery-module-specifier" );
const { window } = new JSDOM( "" );
const ensureJQuery = require( "./lib/ensure_jquery" );
const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" );
const jQuery = require( "../../dist/jquery.js" )( window );
const jQueryModuleSpecifier = getJQueryModuleSpecifier();
const jQueryFactory = require( jQueryModuleSpecifier );
const jQuery = jQueryFactory( window );
ensureJQuery( jQuery );
ensureGlobalNotCreated( module.exports );

View File

@ -1,15 +0,0 @@
"use strict";
const { JSDOM } = require( "jsdom" );
const { window } = new JSDOM( "" );
// Pretend the window is a global.
global.window = window;
const ensureJQuery = require( "./lib/ensure_jquery" );
const ensureGlobalNotCreated = require( "./lib/ensure_global_not_created" );
const jQuery = require( "../../dist/jquery.js" );
ensureJQuery( jQuery );
ensureGlobalNotCreated( module.exports, window );

View File

@ -1,8 +1,14 @@
"use strict";
const process = require( "node:process" );
if ( typeof Symbol === "undefined" ) {
console.log( "Symbols not supported, skipping the test..." );
process.exit();
}
require( "./lib/ensure_iterability_es6" )();
const { ensureIterability } = require( "./lib/ensure_iterability_es6" );
const { getJQueryModuleSpecifier } = require( "./lib/jquery-module-specifier" );
const jQueryModuleSpecifier = getJQueryModuleSpecifier();
ensureIterability( jQueryModuleSpecifier );

View File

@ -1,6 +1,6 @@
"use strict";
const assert = require( "assert" );
const assert = require( "node:assert" );
// Ensure the jQuery property on global/window/module.exports/etc. was not
// created in a CommonJS environment.
@ -12,4 +12,4 @@ const ensureGlobalNotCreated = ( ...args ) => {
} );
};
module.exports = ensureGlobalNotCreated;
module.exports = { ensureGlobalNotCreated };

View File

@ -1,25 +1,25 @@
"use strict";
const assert = require( "assert" );
const assert = require( "node:assert" );
const { JSDOM } = require( "jsdom" );
const ensureIterability = () => {
const { JSDOM } = require( "jsdom" );
const { ensureJQuery } = require( "./ensure_jquery" );
const ensureIterability = ( jQueryModuleSpecifier ) => {
const { window } = new JSDOM( "" );
let i;
const ensureJQuery = require( "./ensure_jquery" );
const jQuery = require( "../../../dist/jquery.js" )( window );
const jQueryFactory = require( jQueryModuleSpecifier );
const jQuery = jQueryFactory( window );
const elem = jQuery( "<div></div><span></span><a></a>" );
let result = "";
ensureJQuery( jQuery );
for ( i of elem ) {
result += i.nodeName;
let result = "";
for ( const node of elem ) {
result += node.nodeName;
}
assert.strictEqual( result, "DIVSPANA", "for-of works on jQuery objects" );
};
module.exports = ensureIterability;
module.exports = { ensureIterability };

View File

@ -1,6 +1,6 @@
"use strict";
const assert = require( "assert" );
const assert = require( "node:assert" );
// Check if the object we got is the jQuery object by invoking a basic API.
const ensureJQuery = ( jQuery ) => {
@ -8,4 +8,4 @@ const ensureJQuery = ( jQuery ) => {
"jQuery.expando was not detected, the jQuery bootstrap process has failed" );
};
module.exports = ensureJQuery;
module.exports = { ensureJQuery };

View File

@ -0,0 +1,14 @@
"use strict";
const path = require( "node:path" );
const ROOT_DIR = path.resolve( __dirname, "..", "..", ".." );
// If `jQueryModuleSpecifier` is a real relative path, make it absolute
// to make sure it resolves to the same file inside utils from
// a subdirectory. Otherwise, leave it as-is as we may be testing `exports`
// so we need input as-is.
const getJQueryModuleSpecifier = () =>
path.resolve( ROOT_DIR, process.argv[ 2 ] );
module.exports = { getJQueryModuleSpecifier };

View File

@ -0,0 +1,19 @@
"use strict";
const { JSDOM } = require( "jsdom" );
const { ensureJQuery } = require( "./lib/ensure_jquery" );
const { ensureGlobalNotCreated } = require( "./lib/ensure_global_not_created" );
const { getJQueryModuleSpecifier } = require( "./lib/jquery-module-specifier" );
const jQueryModuleSpecifier = getJQueryModuleSpecifier();
const { window } = new JSDOM( "" );
// Set the window global.
global.window = window;
const jQuery = require( jQueryModuleSpecifier );
ensureJQuery( jQuery );
ensureGlobalNotCreated( module.exports, window );