Skip to content

Commit

Permalink
Build: Fix native use of ES6 Map
Browse files Browse the repository at this point in the history
Remove global-this-polyfill, and replace it with a simpler alternative,
similer to what we already rely on in lib/promise-polyfill.js.

Then, with this available internally, move our es6-map.js back into
the regular build (thus effectively reverting 259f9af) where we
can access it as a regular exported value for internal use, including
the use of globalThis for accessing the native Map when available.

This resolves the canundrum we had before, where either `typeof Map`
is done within the bundle and it gets renamed by Rollup, or we do
it outside the bundle and it always finds undefined due to still being
within the outer IIFE (which was at the same time a good thing as we
don't want to leak this export to outside the QUnit scope). The fix
is obvious in retrospect, which is to access it via globalThis.

It seems we can consolidate the export.js handling for Web Workers
with the globalThis fallback (for SpiderMonkey), so let's do that.
  • Loading branch information
Krinkle committed Mar 27, 2022
1 parent 4b9b5e5 commit aa7314b
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 122 deletions.
49 changes: 0 additions & 49 deletions lib/global-this-polyfill.js

This file was deleted.

16 changes: 1 addition & 15 deletions rollup.config.js
@@ -1,6 +1,5 @@
/* eslint-env node */

const fs = require( "fs" );
const { babel } = require( "@rollup/plugin-babel" );
const { nodeResolve } = require( "@rollup/plugin-node-resolve" );
const commonjs = require( "@rollup/plugin-commonjs" );
Expand All @@ -25,20 +24,7 @@ module.exports = {
* Copyright OpenJS Foundation and other contributors\n\
* Released under the MIT license\n\
* https://jquery.org/license\n\
*/",

intro: function() {

// Define the (partial) ES6 Map polyfill for "fuzzysort".
// Per https://github.com/qunitjs/qunit/issues/1508:
// 1. Must not leak as global variable, since it's not full Map implementation.
// 2. Must be seen by fuzzysort as-is (e.g. not get renamed as normal
// variables in an imported file would be).
return fs.readFileSync(
__dirname + "/src/html-reporter/es6-map.js",
"utf8"
).toString().trim();
}
*/"
},
plugins: [
replace( {
Expand Down
14 changes: 3 additions & 11 deletions src/export.js
@@ -1,6 +1,5 @@
/* global module, exports, define */
import { window, document, self } from "./globals";
import globalThis from "../lib/global-this-polyfill";
import { window, document, globalThis } from "./globals";

export default function exportQUnit( QUnit ) {
let exportedModule = false;
Expand Down Expand Up @@ -44,15 +43,8 @@ export default function exportQUnit( QUnit ) {
exportedModule = true;
}

// For Web/Service Workers
if ( self && self.WorkerGlobalScope && self instanceof self.WorkerGlobalScope ) {
self.QUnit = QUnit;

exportedModule = true;
}

// For other environments, such as SpiderMonkey (mozjs) and other
// embedded JavaScript engines
// For other environments, including Web Workers (globalThis === self),
// SpiderMonkey (mozjs), and other embedded JavaScript engines
if ( !exportedModule ) {
globalThis.QUnit = QUnit;
}
Expand Down
95 changes: 86 additions & 9 deletions src/globals.js
@@ -1,21 +1,98 @@
import globalThis from "../lib/global-this-polyfill";
// We don't use global-this-polyfill [1], because it modifies
// the globals scope by default. QUnit must not affect the host context
// as developers may test their project may be such a polyfill, and/or
// they may intentionally test their project with and without certain
// polyfills and we must not affect that. It also uses an obscure
// mechanism that seems to sometimes causes a runtime error in older
// browsers (specifically Safari and IE versions that support
// Object.defineProperty but then report _T_ as undefined).
// [1] https://github.com/ungap/global-this/blob/v0.4.4/esm/index.js
//
// Another way is `Function('return this')()`, but doing so relies
// on eval which will cause a CSP error on some servers.
//
// Instead, simply check the four options that already exist
// in all supported environments.
function getGlobalThis() {
if ( typeof globalThis !== "undefined" ) {

export const window = globalThis.window;
export const self = globalThis.self;
export const console = globalThis.console;
export const setTimeout = globalThis.setTimeout;
export const clearTimeout = globalThis.clearTimeout;
// For SpiderMonkey, modern browsers, and recent Node.js
// eslint-disable-next-line no-undef
return globalThis;
}
if ( typeof self !== "undefined" ) {

// For web workers
// eslint-disable-next-line no-undef
return self;
}
if ( typeof window !== "undefined" ) {

// For document context in browsers
return window;
}
if ( typeof global !== "undefined" ) {

// For Node.js
// eslint-disable-next-line no-undef
return global;
}
throw new Error( "Unable to locate global object" );
}


// This avoids a simple `export const` assignment as that would lead Rollup
// to change getGlobalThis and use the same (generated) variable name there.
const g = getGlobalThis();
export { g as globalThis };
export const window = g.window;
export const console = g.console;
export const setTimeout = g.setTimeout;
export const clearTimeout = g.clearTimeout;

export const document = window && window.document;
export const navigator = window && window.navigator;

export const localSessionStorage = ( function() {
const x = "qunit-test-string";
try {
globalThis.sessionStorage.setItem( x, x );
globalThis.sessionStorage.removeItem( x );
return globalThis.sessionStorage;
g.sessionStorage.setItem( x, x );
g.sessionStorage.removeItem( x );
return g.sessionStorage;
} catch ( e ) {
return undefined;
}
}() );

// Basic fallback for ES6 Map
// Support: IE 9-10, Safari 7, PhantomJS
export const StringMap = typeof g.Map === "function" ? g.Map : function StringMap() {
var store = Object.create( null );
var hasOwn = Object.prototype.hasOwnProperty;
this.get = function( strKey ) {
return store[ strKey ];
};
this.set = function( strKey, val ) {
if ( !hasOwn.call( store, strKey ) ) {
this.size++;
}
store[ strKey ] = val;
return this;
};
this.delete = function( strKey ) {
if ( hasOwn.call( store, strKey ) ) {
delete store[ strKey ];
this.size--;
}
};
this.forEach = function( callback ) {
for ( var strKey in store ) {
callback( store[ strKey ], strKey );
}
};
this.clear = function() {
store = Object.create( null );
this.size = 0;
};
this.size = 0;
};
35 changes: 0 additions & 35 deletions src/html-reporter/es6-map.js

This file was deleted.

5 changes: 2 additions & 3 deletions src/test.js
@@ -1,5 +1,4 @@
import globalThis from "../lib/global-this-polyfill";
import { setTimeout, clearTimeout } from "./globals";
import { globalThis, setTimeout, clearTimeout, StringMap } from "./globals";
import { emit } from "./events";
import Assert from "./assert";
import Logger from "./logger";
Expand Down Expand Up @@ -30,7 +29,7 @@ export default function Test( settings ) {
this.timeout = undefined;
this.data = undefined;
this.withData = false;
this.pauses = new Map();
this.pauses = new StringMap();
this.nextPauseId = 1;
extend( this, settings );

Expand Down

0 comments on commit aa7314b

Please sign in to comment.