From 06bdc88f4fd6119212efce69d0500087f604da42 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Thu, 9 Nov 2023 23:55:22 -0800 Subject: [PATCH] Add `window` as alias to `globalThis` Closes #60. --- .changeset/wise-birds-smoke.md | 5 +++ apps/tests/src/main.ts | 1 + apps/tests/src/window.ts | 35 +++++++++++++++ packages/runtime/src/index.ts | 81 +--------------------------------- packages/runtime/src/window.ts | 78 ++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 79 deletions(-) create mode 100644 .changeset/wise-birds-smoke.md create mode 100644 apps/tests/src/window.ts create mode 100644 packages/runtime/src/window.ts diff --git a/.changeset/wise-birds-smoke.md b/.changeset/wise-birds-smoke.md new file mode 100644 index 00000000..9e73eea6 --- /dev/null +++ b/.changeset/wise-birds-smoke.md @@ -0,0 +1,5 @@ +--- +'nxjs-runtime': patch +--- + +Add `window` as alias to `globalThis` diff --git a/apps/tests/src/main.ts b/apps/tests/src/main.ts index 70ecb204..bdabf8c1 100644 --- a/apps/tests/src/main.ts +++ b/apps/tests/src/main.ts @@ -6,3 +6,4 @@ import './import'; import './navigator'; import './switch'; import './wasm'; +import './window'; diff --git a/apps/tests/src/window.ts b/apps/tests/src/window.ts new file mode 100644 index 00000000..554d4b7e --- /dev/null +++ b/apps/tests/src/window.ts @@ -0,0 +1,35 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; + +const test = suite('window'); + +test('instanceof', () => { + assert.equal(window instanceof Window, true); + assert.equal(globalThis instanceof Window, true); + assert.equal(globalThis === window, true); +}); + +test('throws illegal constructor', () => { + let err: Error | undefined; + try { + new Window(); + } catch (e: any) { + err = e; + } + assert.ok(err); + assert.equal(err.message, 'Illegal constructor'); +}); + +test('supports global events', () => { + let gotEvent = false; + addEventListener('test', (e) => { + gotEvent = true; + e.preventDefault(); + }); + const e = new Event('test'); + dispatchEvent(e); + assert.ok(gotEvent); + assert.ok(e.defaultPrevented); +}); + +test.run(); diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 74ae6e03..26a75a16 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -128,85 +128,8 @@ function touchIsEqual(a: Touch, b: Touch) { ); } -// Make `globalThis` inherit from `EventTarget` -Object.setPrototypeOf(globalThis, EventTarget.prototype); -EventTarget.call(globalThis); -def( - 'addEventListener', - EventTarget.prototype.addEventListener.bind(globalThis) -); -def( - 'removeEventListener', - EventTarget.prototype.removeEventListener.bind(globalThis) -); -def('dispatchEvent', EventTarget.prototype.dispatchEvent.bind(globalThis)); - -/** - * The `error` event is sent to the global scope when an unhandled error is thrown. - * - * The default behavior when this event occurs is to print the error to the screen - * using {@link console.error | `console.error()`}, and no further application code - * is executed. The user must then press the `+` button to exit the application. - * Call `event.preventDefault()` to supress this default behavior. - * - * @see https://developer.mozilla.org/docs/Web/API/Window/error_event - */ -export declare function addEventListener( - type: 'error', - callback: (event: ErrorEvent) => any, - options?: AddEventListenerOptions | boolean -): void; - -/** - * The `unhandledrejection` event is sent to the global scope when a JavaScript - * Promise that has no rejection handler is rejected. - * - * The default behavior when this event occurs is to print the error to the screen - * using {@link console.error | `console.error()`}, and no further application code - * is executed. The user must then press the `+` button to exit the application. - * Call `event.preventDefault()` to supress this default behavior. - * - * @see https://developer.mozilla.org/docs/Web/API/Window/unhandledrejection_event - */ -export declare function addEventListener( - type: 'unhandledrejection', - callback: (event: PromiseRejectionEvent) => any, - options?: AddEventListenerOptions | boolean -): void; -export declare function addEventListener( - type: string, - callback: EventListenerOrEventListenerObject | null, - options?: AddEventListenerOptions | boolean -): void; - -export declare function removeEventListener( - type: 'error', - callback: (ev: ErrorEvent) => any, - options?: EventListenerOptions | boolean -): void; -export declare function removeEventListener( - type: 'unhandledrejection', - callback: (ev: PromiseRejectionEvent) => any, - options?: EventListenerOptions | boolean -): void; - -/** - * Removes the event listener in target's event listener list with the same type, callback, and options. - * - * @see {@link https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener | MDN Reference} - */ -export declare function removeEventListener( - type: string, - callback: EventListenerOrEventListenerObject | null, - options?: EventListenerOptions | boolean -): void; - -/** - * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its `preventDefault()` method was not invoked, and false otherwise. - * - * @see {@link https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent | MDN Reference} - */ -export declare function dispatchEvent(event: Event): boolean; +import './window'; +export type * from './window'; $.onError((e) => { const ev = new ErrorEvent('error', { diff --git a/packages/runtime/src/window.ts b/packages/runtime/src/window.ts new file mode 100644 index 00000000..942d9b83 --- /dev/null +++ b/packages/runtime/src/window.ts @@ -0,0 +1,78 @@ +import { assertInternalConstructor, def } from './utils'; + +export class Window extends EventTarget { + /** + * @ignore + */ + constructor() { + assertInternalConstructor(arguments); + super(); + } +} +def('Window', Window); + +export const window: Window & typeof globalThis = globalThis; +def('window', window); +Object.setPrototypeOf(window, Window.prototype); +EventTarget.call(window); + +// Temporary band-aid since EventTarget does not default `this` to `globalThis` +def('addEventListener', window.addEventListener.bind(window)); +def('removeEventListener', window.removeEventListener.bind(window)); +def('dispatchEvent', window.dispatchEvent.bind(window)); + +/** + * The `error` event is sent to the global scope when an unhandled error is thrown. + * + * The default behavior when this event occurs is to print the error to the screen + * using {@link console.error | `console.error()`}, and no further application code + * is executed. The user must then press the `+` button to exit the application. + * Call `event.preventDefault()` to supress this default behavior. + * + * @see https://developer.mozilla.org/docs/Web/API/Window/error_event + */ +export declare function addEventListener( + type: 'error', + callback: (event: ErrorEvent) => any, + options?: AddEventListenerOptions | boolean +): void; + +/** + * The `unhandledrejection` event is sent to the global scope when a JavaScript + * Promise that has no rejection handler is rejected. + * + * The default behavior when this event occurs is to print the error to the screen + * using {@link console.error | `console.error()`}, and no further application code + * is executed. The user must then press the `+` button to exit the application. + * Call `event.preventDefault()` to supress this default behavior. + * + * @see https://developer.mozilla.org/docs/Web/API/Window/unhandledrejection_event + */ +export declare function addEventListener( + type: 'unhandledrejection', + callback: (event: PromiseRejectionEvent) => any, + options?: AddEventListenerOptions | boolean +): void; +export declare function addEventListener( + type: string, + callback: EventListenerOrEventListenerObject | null, + options?: AddEventListenerOptions | boolean +): void; + +/** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * @see {@link https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener | MDN Reference} + */ +export declare function removeEventListener( + type: string, + callback: EventListenerOrEventListenerObject | null, + options?: EventListenerOptions | boolean +): void; + +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its `preventDefault()` method was not invoked, and false otherwise. + * + * @see {@link https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent | MDN Reference} + */ +export declare function dispatchEvent(event: Event): boolean;