Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Build: Add native ESM distribution
Browse files Browse the repository at this point in the history
Krinkle committed Aug 27, 2024
1 parent 6799e6a commit 3c697ed
Showing 9 changed files with 285 additions and 118 deletions.
12 changes: 12 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -27,6 +27,18 @@ module.exports = function (grunt) {
'src-css': {
src: 'src/core/qunit.css',
dest: 'qunit/qunit.css'
},
'src-export-cjs-wrappers': {
src: 'src/core/qunit-wrapper-bundler-require.js',
expand: true,
flatten: true,
dest: 'qunit/'
},
'src-export-esm-wrappers': {
src: 'src/core/qunit-wrapper-nodejs-module.js',
expand: true,
flatten: true,
dest: 'qunit/esm/'
}
},
search: {
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -37,6 +37,20 @@
"LICENSE.txt"
],
"main": "qunit/qunit.js",
"exports": {
".": {
"node": {
"import": "./qunit/esm/qunit-wrapper-nodejs-module.js",
"default": "./qunit/qunit.js"
},
"module": {
"import": "./qunit/esm/qunit.module.js",
"default": "./qunit/qunit-wrapper-bundler-require.js"
},
"import": "./qunit/esm/qunit.module.js",
"default": "./qunit/qunit.js"
}
},
"engines": {
"node": ">=18"
},
@@ -82,8 +96,8 @@
"tap-min": "^3.0.0"
},
"scripts": {
"build": "rollup -c && grunt copy:src-css",
"build-coverage": "rollup -c --environment BUILD_TARGET:coverage && grunt copy:src-css",
"build": "rollup -c && grunt copy",
"build-coverage": "rollup -c --environment BUILD_TARGET:coverage && grunt copy",
"build-dev": "node build/dev.js",
"benchmark": "npm install --silent --no-audit --prefix test/benchmark/ && node test/benchmark/micro.js",
"lint": "eslint --cache .",
108 changes: 69 additions & 39 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -8,44 +8,74 @@ const replace = require('@rollup/plugin-replace');
const { replacements } = require('./build/dist-replace.js');
const isCoverage = process.env.BUILD_TARGET === 'coverage';

module.exports = {
input: 'src/core/qunit.js',
output: {
file: 'qunit/qunit.js',
sourcemap: isCoverage,
format: 'iife',
exports: 'none',
const banner = `/*!
* QUnit @VERSION
* https://qunitjs.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*/`;

// eslint-disable-next-line no-multi-str
banner: '/*!\n\
* QUnit @VERSION\n\
* https://qunitjs.com/\n\
*\n\
* Copyright OpenJS Foundation and other contributors\n\
* Released under the MIT license\n\
* https://jquery.org/license\n\
*/'
},
plugins: [
replace({
preventAssignment: true,
delimiters: ['', ''],
...replacements
}),
nodeResolve(),
commonjs(),
babel({
babelHelpers: 'bundled',
babelrc: false,
presets: [
['@babel/preset-env', {
targets: {
ie: 11,
safari: 7,
node: 18
}
}]
]
})
]
const replacementOptions = {
preventAssignment: true,
delimiters: ['', ''],
...replacements
};

module.exports = [
{
input: 'src/core/qunit-commonjs.js',
output: {
file: 'qunit/qunit.js',
sourcemap: isCoverage,
format: 'iife',
exports: 'none',
banner: banner
},
plugins: [
replace(replacementOptions),
nodeResolve(),
commonjs(),
babel({
babelHelpers: 'bundled',
babelrc: false,
presets: [
['@babel/preset-env', {
targets: {
ie: 11,
safari: 7,
node: 18
}
}]
]
})
]
},
{
input: 'src/core/qunit.js',
output: {
file: 'qunit/esm/qunit.module.js',
format: 'es',
exports: 'named',
banner: banner
},
plugins: [
replace(replacementOptions),
nodeResolve(),
commonjs(),
babel({
babelHelpers: 'bundled',
babelrc: false,
presets: [
['@babel/preset-env', {
targets: {
safari: 10,
node: 18
}
}]
]
})
]
}
];
51 changes: 0 additions & 51 deletions src/core/export.js

This file was deleted.

12 changes: 12 additions & 0 deletions src/core/qunit-commonjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* global module, exports */
import QUnit from './qunit.js';

// For Node.js
if (typeof module !== 'undefined' && module && module.exports) {
module.exports = QUnit;
}

// For CommonJS with exports, but without module.exports, like Rhino
if (typeof exports !== 'undefined' && exports) {
exports.QUnit = QUnit;
}
9 changes: 9 additions & 0 deletions src/core/qunit-wrapper-bundler-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* eslint-env node */

// In a single bundler invocation, if different parts or dependencies
// of a project mix ESM and CJS, avoid a split-brain state by making
// sure both import and re-use the same instance via this wrapper.
//
// Bundlers generally allow requiring an ESM file from CommonJS.
const { QUnit } = require('./esm/qunit.module.js');
module.exports = QUnit;
42 changes: 42 additions & 0 deletions src/core/qunit-wrapper-nodejs-module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// In a single Node.js process, if different parts or dependencies
// of a project mix ESM and CJS, avoid a split-brain state by making
// sure both import and re-use the same instance via this wrapper.
//
// Node.js 18+ can import a CommonJS file from ESM.
import QUnit from '../qunit.js';

export const {
assert,
begin,
config,
diff,
done,
dump,
equiv,
hooks,
is,
isLocal,
log,
module,
moduleDone,
moduleStart,
objectType,
on,
only,
onUncaughtException,
pushFailure,
reporters,
skip,
stack,
start,
test,
testDone,
testStart,
todo,
urlParams,
version
} = QUnit;

export { QUnit };

export default QUnit;
150 changes: 124 additions & 26 deletions src/core/qunit.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import diff from './diff.js';
import dump from './dump.js';
import equiv from './equiv.js';
import { on } from './events.js';
import { window, document } from './globals.js';
import { globalThis, window, document } from './globals.js';
import hooks from './hooks.js';
import { module, unnamedModule } from './module.js';
import onUncaughtException from './on-uncaught-exception.js';
@@ -21,57 +21,155 @@ import version from './version.js';

// Imports that help with init
import { initBrowser } from './browser/browser-runner.js';
import exportQUnit from './export.js';

// Finalise internal state and exports before we export the API
config.currentModule = unnamedModule;
config._pq = new ProcessingQueue();

const QUnit = {
const assert = Assert.prototype;

const isLocal = (window && window.location && window.location.protocol === 'file:');

const begin = createRegisterCallbackFunction('begin');
const done = createRegisterCallbackFunction('done');
const log = createRegisterCallbackFunction('log');
const moduleDone = createRegisterCallbackFunction('moduleDone');
const moduleStart = createRegisterCallbackFunction('moduleStart');
const testDone = createRegisterCallbackFunction('testDone');
const testStart = createRegisterCallbackFunction('testStart');

// Figure out if we're running the tests from a server or not
isLocal: (window && window.location && window.location.protocol === 'file:'),
const only = test.only;
const skip = test.skip;
const todo = test.todo;

// Expose the current QUnit version
version,
// Export the API
//
// * ESM export
// - Node.js
// - browser
// - any other ESM-capable JS engine
//
// * globalThis
// - browser (globalThis === window)
// - Web Worker (globalThis === self)
// - Node.js
// - SpiderMonkey (mozjs)
// - Rhino 7.14+
// - any other embedded JS engine
//
// The following are handled by the separate export-commonjs.js file:
//
// * CommonJS module.exports (commonjs2)
// - Node.js
//
// * CommonJS exports (commonjs, https://wiki.commonjs.org/wiki/Modules):
// - Rhino

export {
assert,
begin,
config,
diff,
done,
dump,
equiv,
hooks,
is,
isLocal,
log,
module,
moduleDone,
moduleStart,
objectType,
on,
only,
onUncaughtException,
pushFailure,
reporters,
skip,
stack,
start,
test,
testDone,
testStart,
todo,
urlParams,
version
};

const QUnit = {
assert,
begin,
config,
diff,
done,
dump,
equiv,
reporters,
hooks,
is,
on,
isLocal,
log,
module,
moduleDone,
moduleStart,
objectType,
on,
only,
onUncaughtException,
pushFailure,

begin: createRegisterCallbackFunction('begin'),
done: createRegisterCallbackFunction('done'),
log: createRegisterCallbackFunction('log'),
moduleDone: createRegisterCallbackFunction('moduleDone'),
moduleStart: createRegisterCallbackFunction('moduleStart'),
testDone: createRegisterCallbackFunction('testDone'),
testStart: createRegisterCallbackFunction('testStart'),

assert: Assert.prototype,
module,
reporters,
skip,
stack,
start,
test,

// alias other test flavors for easy access
todo: test.todo,
skip: test.skip,
only: test.only
testDone,
testStart,
todo,
urlParams,
version
};

// Inject the exported QUnit API for use by reporters in start()
config._QUnit = QUnit;

exportQUnit(QUnit);
// Support: require('qunit').QUnit
//
// For interop and consistency between Node.js `module.exports = QUnit`
// and CommonJS environments `exports.QUnit = QUnit`, the below will
// effectively assign `module.exports.QUnit = QUnit` as well.
QUnit.QUnit = QUnit;

// Support: named import
//
// import { QUnit } from 'qunit'
//
export { QUnit };

// Support: default import
//
// import QUnit from 'qunit'
//
export default QUnit;

if (window && document) {
// In browsers, throw if QUnit is loaded a second time.
// This must not throw if a global called "QUnit" exists for preconfigurion,
// in that case we simply upgrade/replace it with the proper export.
// Such preconfig global would only have QUnit.config set, not e.g. QUnit.version.
if (globalThis.QUnit && globalThis.QUnit.version) {
throw new Error('QUnit has already been defined.');
}
}

// Ensure the global is available in all environments.
//
// For backward compatibility, we only enforce load-once in browsers above.
// In other environments QUnit is accessible via import/require() and may
// load multiple times, including different versions from different sources.
// Callers decide whether to make their secondary instance global or not.
if (!globalThis.QUnit || !globalThis.QUnit.version) {
globalThis.QUnit = QUnit;
}

if (window && document) {
initBrowser(QUnit, window, document);
1 change: 1 addition & 0 deletions src/core/version.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// Expose the current QUnit version
// Replaced by /rollup.config.js using /build/dist-replace.js
export default '@VERSION';

0 comments on commit 3c697ed

Please sign in to comment.