Build: Auto-convert sources to AMD

jQuery source has been migrated in gh-4541 from AMD to ES modules. To maintain
support for consumers of our AMD modules, this commits adds a task transpiling
the ES modules sources in `src/` to AMD in `amd/`.

A "Load with AMD" checkbox was also restored to the QUnit setup. Note that,
contrary to jQuery 3.x, AMD files need to be generated via `grunt amd` or
`grunt` as sources are not authored in ECMAScript modules. To achieve a similar
no-compile experience during jQuery 4.x testing, use the new "Load as modules"
checkbox which works in all supported browsers except for IE & Edge (the
legacy, EdgeHTML-based one).

Ref gh-4541
Closes gh-4554
This commit is contained in:
Michał Gołębiowski-Owczarek 2019-12-09 20:00:44 +01:00 committed by GitHub
parent d5c505e35d
commit f37c2e51f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 91 additions and 16 deletions

View File

@ -1,4 +1,4 @@
external amd
node_modules node_modules
*.min.js *.min.js
dist/** dist/**

2
.gitignore vendored
View File

@ -16,6 +16,8 @@ npm-debug.log*
/dist/* /dist/*
!/dist/.eslintrc.json !/dist/.eslintrc.json
/amd
/node_modules /node_modules
/test/data/core/jquery-iterability-transpiled.js /test/data/core/jquery-iterability-transpiled.js

View File

@ -178,6 +178,7 @@ module.exports = function( grunt ) {
{ pattern: "dist/jquery.*", included: false, served: true }, { pattern: "dist/jquery.*", included: false, served: true },
{ pattern: "src/**", type: "module", included: false, served: true }, { pattern: "src/**", type: "module", included: false, served: true },
{ pattern: "amd/**", included: false, served: true },
{ pattern: "node_modules/**", included: false, served: true }, { pattern: "node_modules/**", included: false, served: true },
{ {
pattern: "test/**/*.@(js|css|jpg|html|xml|svg)", pattern: "test/**/*.@(js|css|jpg|html|xml|svg)",
@ -319,6 +320,7 @@ module.exports = function( grunt ) {
grunt.registerTask( "default", [ grunt.registerTask( "default", [
"eslint:dev", "eslint:dev",
"build:*:*", "build:*:*",
"amd",
"uglify", "uglify",
"remove_map_comment", "remove_map_comment",
"dist:*", "dist:*",

View File

@ -22,18 +22,23 @@ module.exports = function( Release ) {
npmTags = Release.npmTags; npmTags = Release.npmTags;
function setSrcVersion( filepath ) {
var contents = fs.readFileSync( filepath, "utf8" );
contents = contents.replace( /@VERSION/g, Release.newVersion );
fs.writeFileSync( filepath, contents, "utf8" );
}
Release.define( { Release.define( {
npmPublish: true, npmPublish: true,
issueTracker: "github", issueTracker: "github",
/** /**
* Set the version in the src folder for distributing AMD * Set the version in the src folder for distributing ES modules
* and in the amd folder for AMD.
*/ */
_setSrcVersion: function() { _setSrcVersion: function() {
var corePath = __dirname + "/../src/core.js", setSrcVersion( `${ __dirname }/../src/core.js` );
contents = fs.readFileSync( corePath, "utf8" ); setSrcVersion( `${ __dirname }/../amd/core.js` );
contents = contents.replace( /@VERSION/g, Release.newVersion );
fs.writeFileSync( corePath, contents, "utf8" );
}, },
/** /**

View File

@ -13,6 +13,7 @@ module.exports = function( Release, files, complete ) {
// These files are included with the distribution // These files are included with the distribution
extras = [ extras = [
"amd",
"src", "src",
"LICENSE.txt", "LICENSE.txt",
"AUTHORS.txt", "AUTHORS.txt",

43
build/tasks/amd.js Normal file
View File

@ -0,0 +1,43 @@
/**
* Compiles sources from ES Modules in `src/` to AMD in `amd/`.
*/
"use strict";
module.exports = function( grunt ) {
const path = require( "path" );
const rimraf = require( "rimraf" );
const rollup = require( "rollup" );
const srcFolder = path.resolve( __dirname, "..", "..", "src" );
const amdFolder = path.resolve( srcFolder, "..", "amd" );
const inputFileName = "jquery.js";
const inputRollupOptions = {
input: path.resolve( srcFolder, inputFileName ),
preserveModules: true
};
const outputRollupOptions = {
format: "amd",
dir: "amd"
};
grunt.registerTask(
"amd",
"Convert ES modules from `src/` to AMD modules in `amd/`",
async function() {
const done = this.async();
try {
grunt.verbose.writeln( "Removing the 'amd' directory..." );
rimraf( amdFolder, async function() {
const bundle = await rollup.rollup( inputRollupOptions );
await bundle.write( outputRollupOptions );
grunt.log.ok( "Sources from 'src' converted to AMD in 'amd'." );
done();
} );
} catch ( err ) {
done( err );
}
} );
};

View File

@ -60,7 +60,8 @@
"qunit": "2.9.2", "qunit": "2.9.2",
"raw-body": "2.3.3", "raw-body": "2.3.3",
"requirejs": "2.3.6", "requirejs": "2.3.6",
"rollup": "1.25.2", "rimraf": "3.0.0",
"rollup": "1.27.6",
"sinon": "7.3.1", "sinon": "7.3.1",
"strip-json-comments": "2.0.1", "strip-json-comments": "2.0.1",
"testswarm": "1.1.0", "testswarm": "1.1.0",

View File

@ -299,14 +299,16 @@ QUnit.testUnlessIE = QUnit.isIE ? QUnit.skip : QUnit.test;
this.loadTests = function() { this.loadTests = function() {
// Directly load tests that need synchronous evaluation // QUnit.config is populated from QUnit.urlParams but only at the beginning
if ( !QUnit.urlParams.esmodules || document.readyState === "loading" ) { // of the test run. We need to read both.
var amd = QUnit.config.amd || QUnit.urlParams.amd;
// Directly load tests that need evaluation before DOMContentLoaded.
if ( !amd || document.readyState === "loading" ) {
document.write( "<script src='" + parentUrl + "test/unit/ready.js'><\x2Fscript>" ); document.write( "<script src='" + parentUrl + "test/unit/ready.js'><\x2Fscript>" );
} else { } else {
QUnit.module( "ready", function() { QUnit.module( "ready", function() {
QUnit.test( "jQuery ready", function( assert ) { QUnit.skip( "jQuery ready tests skipped in async mode", function() {} );
assert.ok( false, "Test should be initialized before DOM ready" );
} );
} ); } );
} }

View File

@ -19,7 +19,7 @@
<!-- See testinit for the list of tests --> <!-- See testinit for the list of tests -->
<script src="data/testinit.js"></script> <script src="data/testinit.js"></script>
<!-- A script that includes jQuery min, dev, or ES modules --> <!-- A script that includes jQuery min, dev, ES modules or AMD -->
<!-- Adds "basic" URL option, even to iframes --> <!-- Adds "basic" URL option, even to iframes -->
<!-- iframes will not load AMD as loading needs to be synchronous for some tests --> <!-- iframes will not load AMD as loading needs to be synchronous for some tests -->
<!-- Also executes the function above to load tests --> <!-- Also executes the function above to load tests -->
@ -29,7 +29,7 @@
// Load tests if they have not been loaded // Load tests if they have not been loaded
// This is in a different script tag to ensure that // This is in a different script tag to ensure that
// jQuery is on the page when the testrunner executes // jQuery is on the page when the testrunner executes
if ( !QUnit.urlParams.esmodules ) { if ( !QUnit.urlParams.esmodules && !QUnit.urlParams.amd ) {
loadTests(); loadTests();
} }
</script> </script>

23
test/jquery.js vendored
View File

@ -28,7 +28,12 @@
QUnit.config.urlConfig.push( { QUnit.config.urlConfig.push( {
id: "esmodules", id: "esmodules",
label: "Load as modules", label: "Load as modules",
tooltip: "Load a relevant jQuery module file (and its dependencies)" tooltip: "Load the jQuery module file (and its dependencies)"
} );
QUnit.config.urlConfig.push( {
id: "amd",
label: "Load with AMD",
tooltip: "Load the AMD jQuery file (and its dependencies)"
} ); } );
} }
@ -39,7 +44,7 @@
} ); } );
} }
// Honor AMD loading on the main window (detected by seeing QUnit on it). // Honor ES modules loading on the main window (detected by seeing QUnit on it).
// This doesn't apply to iframes because they synchronously expect jQuery to be there. // This doesn't apply to iframes because they synchronously expect jQuery to be there.
if ( urlParams.esmodules && window.QUnit ) { if ( urlParams.esmodules && window.QUnit ) {
@ -57,6 +62,20 @@
eval( dynamicImportSource ); eval( dynamicImportSource );
// Apply similar treatment for AMD modules
} else if ( urlParams.amd && window.QUnit ) {
require.config( {
baseUrl: parentUrl
} );
src = "amd/jquery";
// Include tests if specified
if ( typeof loadTests !== "undefined" ) {
require( [ src ], loadTests );
} else {
require( [ src ] );
}
// Otherwise, load synchronously // Otherwise, load synchronously
} else { } else {
document.write( "<script id='jquery-js' nonce='jquery+hardcoded+nonce' src='" + parentUrl + src + "'><\x2Fscript>" ); document.write( "<script id='jquery-js' nonce='jquery+hardcoded+nonce' src='" + parentUrl + src + "'><\x2Fscript>" );