diff options
author | Chris Knight <cknight1234@gmail.com> | 2020-02-10 23:19:48 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-10 18:19:48 -0500 |
commit | 81905a867ea3f942619229e330840d132c57a5da (patch) | |
tree | 4bcdac6a58dfc5fd14e83f04c3f388a6d43968d3 /std/node | |
parent | e1105a159406d8b64a833fa3266fd4ac7fc47a00 (diff) |
feat: Event emitter node polyfill (#3944)
Diffstat (limited to 'std/node')
-rw-r--r-- | std/node/README.md | 2 | ||||
-rw-r--r-- | std/node/events.ts | 383 | ||||
-rw-r--r-- | std/node/events_test.ts | 403 | ||||
-rw-r--r-- | std/node/module.ts | 8 | ||||
-rw-r--r-- | std/node/os.ts | 24 | ||||
-rw-r--r-- | std/node/util.ts | 17 |
6 files changed, 814 insertions, 23 deletions
diff --git a/std/node/README.md b/std/node/README.md index 8d9f4ff40..37084ed25 100644 --- a/std/node/README.md +++ b/std/node/README.md @@ -16,7 +16,7 @@ deno standard library as it's a compatiblity module. - [ ] crypto - [ ] dgram - [ ] dns -- [ ] events +- [x] events - [x] fs _partly_ - [ ] http - [ ] http2 diff --git a/std/node/events.ts b/std/node/events.ts new file mode 100644 index 000000000..f035da2fc --- /dev/null +++ b/std/node/events.ts @@ -0,0 +1,383 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +// Copyright (c) 2019 Denolibs authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { validateIntegerRange } from "./util.ts"; + +export interface WrappedFunction extends Function { + listener: Function; +} + +/** + * See also https://nodejs.org/api/events.html + */ +export default class EventEmitter { + public static defaultMaxListeners = 10; + private maxListeners: number | undefined; + private _events: Map<string | symbol, Array<Function | WrappedFunction>>; + + public constructor() { + this._events = new Map(); + } + + private _addListener( + eventName: string | symbol, + listener: Function | WrappedFunction, + prepend: boolean + ): this { + this.emit("newListener", eventName, listener); + if (this._events.has(eventName)) { + const listeners = this._events.get(eventName) as Array< + Function | WrappedFunction + >; + if (prepend) { + listeners.unshift(listener); + } else { + listeners.push(listener); + } + } else { + this._events.set(eventName, [listener]); + } + const max = this.getMaxListeners(); + if (max > 0 && this.listenerCount(eventName) > max) { + const warning = new Error( + `Possible EventEmitter memory leak detected. + ${this.listenerCount(eventName)} ${eventName.toString()} listeners. + Use emitter.setMaxListeners() to increase limit` + ); + warning.name = "MaxListenersExceededWarning"; + console.warn(warning); + } + + return this; + } + + /** Alias for emitter.on(eventName, listener). */ + public addListener( + eventName: string | symbol, + listener: Function | WrappedFunction + ): this { + return this._addListener(eventName, listener, false); + } + + /** + * Synchronously calls each of the listeners registered for the event named + * eventName, in the order they were registered, passing the supplied + * arguments to each. + * @return true if the event had listeners, false otherwise + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public emit(eventName: string | symbol, ...args: any[]): boolean { + if (this._events.has(eventName)) { + const listeners = (this._events.get(eventName) as Function[]).slice(); // We copy with slice() so array is not mutated during emit + for (const listener of listeners) { + try { + listener.apply(this, args); + } catch (err) { + this.emit("error", err); + } + } + return true; + } else if (eventName === "error") { + const errMsg = args.length > 0 ? args[0] : Error("Unhandled error."); + throw errMsg; + } + return false; + } + + /** + * Returns an array listing the events for which the emitter has + * registered listeners. + */ + public eventNames(): [string | symbol] { + return Array.from(this._events.keys()) as [string | symbol]; + } + + /** + * Returns the current max listener value for the EventEmitter which is + * either set by emitter.setMaxListeners(n) or defaults to + * EventEmitter.defaultMaxListeners. + */ + public getMaxListeners(): number { + return this.maxListeners || EventEmitter.defaultMaxListeners; + } + + /** + * Returns the number of listeners listening to the event named + * eventName. + */ + public listenerCount(eventName: string | symbol): number { + if (this._events.has(eventName)) { + return (this._events.get(eventName) as Function[]).length; + } else { + return 0; + } + } + + private _listeners( + target: EventEmitter, + eventName: string | symbol, + unwrap: boolean + ): Function[] { + if (!target._events.has(eventName)) { + return []; + } + const eventListeners: Function[] = target._events.get( + eventName + ) as Function[]; + + return unwrap + ? this.unwrapListeners(eventListeners) + : eventListeners.slice(0); + } + + private unwrapListeners(arr: Function[]): Function[] { + const unwrappedListeners: Function[] = new Array(arr.length) as Function[]; + for (let i = 0; i < arr.length; i++) { + unwrappedListeners[i] = arr[i]["listener"] || arr[i]; + } + return unwrappedListeners; + } + + /** Returns a copy of the array of listeners for the event named eventName.*/ + public listeners(eventName: string | symbol): Function[] { + return this._listeners(this, eventName, true); + } + + /** + * Returns a copy of the array of listeners for the event named eventName, + * including any wrappers (such as those created by .once()). + */ + public rawListeners( + eventName: string | symbol + ): Array<Function | WrappedFunction> { + return this._listeners(this, eventName, false); + } + + /** Alias for emitter.removeListener(). */ + public off(eventName: string | symbol, listener: Function): this { + return this.removeListener(eventName, listener); + } + + /** + * Adds the listener function to the end of the listeners array for the event + * named eventName. No checks are made to see if the listener has already + * been added. Multiple calls passing the same combination of eventName and + * listener will result in the listener being added, and called, multiple + * times. + */ + public on( + eventName: string | symbol, + listener: Function | WrappedFunction + ): this { + return this.addListener(eventName, listener); + } + + /** + * Adds a one-time listener function for the event named eventName. The next + * time eventName is triggered, this listener is removed and then invoked. + */ + public once(eventName: string | symbol, listener: Function): this { + const wrapped: WrappedFunction = this.onceWrap(eventName, listener); + this.on(eventName, wrapped); + return this; + } + + // Wrapped function that calls EventEmitter.removeListener(eventName, self) on execution. + private onceWrap( + eventName: string | symbol, + listener: Function + ): WrappedFunction { + const wrapper = function( + this: { + eventName: string | symbol; + listener: Function; + rawListener: Function; + context: EventEmitter; + }, + ...args: any[] // eslint-disable-line @typescript-eslint/no-explicit-any + ): void { + this.context.removeListener(this.eventName, this.rawListener); + this.listener.apply(this.context, args); + }; + const wrapperContext = { + eventName: eventName, + listener: listener, + rawListener: wrapper, + context: this + }; + const wrapped = wrapper.bind(wrapperContext); + wrapperContext.rawListener = wrapped; + wrapped.listener = listener; + return wrapped as WrappedFunction; + } + + /** + * Adds the listener function to the beginning of the listeners array for the + * event named eventName. No checks are made to see if the listener has + * already been added. Multiple calls passing the same combination of + * eventName and listener will result in the listener being added, and + * called, multiple times. + */ + public prependListener( + eventName: string | symbol, + listener: Function | WrappedFunction + ): this { + return this._addListener(eventName, listener, true); + } + + /** + * Adds a one-time listener function for the event named eventName to the + * beginning of the listeners array. The next time eventName is triggered, + * this listener is removed, and then invoked. + */ + public prependOnceListener( + eventName: string | symbol, + listener: Function + ): this { + const wrapped: WrappedFunction = this.onceWrap(eventName, listener); + this.prependListener(eventName, wrapped); + return this; + } + + /** Removes all listeners, or those of the specified eventName. */ + public removeAllListeners(eventName?: string | symbol): this { + if (this._events === undefined) { + return this; + } + + if (this._events.has(eventName)) { + const listeners = (this._events.get(eventName) as Array< + Function | WrappedFunction + >).slice(); // Create a copy; We use it AFTER it's deleted. + this._events.delete(eventName); + for (const listener of listeners) { + this.emit("removeListener", eventName, listener); + } + } else { + const eventList: [string | symbol] = this.eventNames(); + eventList.map((value: string | symbol) => { + this.removeAllListeners(value); + }); + } + + return this; + } + + /** + * Removes the specified listener from the listener array for the event + * named eventName. + */ + public removeListener(eventName: string | symbol, listener: Function): this { + if (this._events.has(eventName)) { + const arr: Array<Function | WrappedFunction> = this._events.get( + eventName + ); + + let listenerIndex = -1; + for (let i = arr.length - 1; i >= 0; i--) { + // arr[i]["listener"] is the reference to the listener inside a bound 'once' wrapper + if (arr[i] == listener || arr[i]["listener"] == listener) { + listenerIndex = i; + break; + } + } + + if (listenerIndex >= 0) { + arr.splice(listenerIndex, 1); + this.emit("removeListener", eventName, listener); + if (arr.length === 0) { + this._events.delete(eventName); + } + } + } + return this; + } + + /** + * By default EventEmitters will print a warning if more than 10 listeners + * are added for a particular event. This is a useful default that helps + * finding memory leaks. Obviously, not all events should be limited to just + * 10 listeners. The emitter.setMaxListeners() method allows the limit to be + * modified for this specific EventEmitter instance. The value can be set to + * Infinity (or 0) to indicate an unlimited number of listeners. + */ + public setMaxListeners(n: number): this { + validateIntegerRange(n, "maxListeners", 0); + this.maxListeners = n; + return this; + } +} + +/** + * Creates a Promise that is fulfilled when the EventEmitter emits the given + * event or that is rejected when the EventEmitter emits 'error'. The Promise + * will resolve with an array of all the arguments emitted to the given event. + */ +export function once( + emitter: EventEmitter | EventTarget, + name: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): Promise<any[]> { + return new Promise((resolve, reject) => { + if (emitter instanceof EventTarget) { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen to `error` events here. + emitter.addEventListener( + name, + (...args) => { + resolve(args); + }, + { once: true, passive: false, capture: false } + ); + return; + } else if (emitter instanceof EventEmitter) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const eventListener = (...args: any[]): void => { + if (errorListener !== undefined) { + emitter.removeListener("error", errorListener); + } + resolve(args); + }; + let errorListener: Function; + + // Adding an error listener is not optional because + // if an error is thrown on an event emitter we cannot + // guarantee that the actual event we are waiting will + // be fired. The result could be a silent way to create + // memory or file descriptor leaks, which is something + // we should avoid. + if (name !== "error") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + errorListener = (err: any): void => { + emitter.removeListener(name, eventListener); + reject(err); + }; + + emitter.once("error", errorListener); + } + + emitter.once(name, eventListener); + return; + } + }); +} diff --git a/std/node/events_test.ts b/std/node/events_test.ts new file mode 100644 index 000000000..0d8dd52e1 --- /dev/null +++ b/std/node/events_test.ts @@ -0,0 +1,403 @@ +import { test } from "../testing/mod.ts"; +import { + assert, + assertEquals, + fail, + assertThrows +} from "../testing/asserts.ts"; +import EventEmitter, { WrappedFunction, once } from "./events.ts"; + +const shouldNeverBeEmitted: Function = () => { + fail("Should never be called"); +}; + +test({ + name: + 'When adding a new event, "eventListener" event is fired before adding the listener', + fn() { + let eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("newListener", event => { + if (event !== "newListener") { + eventsFired.push("newListener"); + } + }); + testEmitter.on("event", () => { + eventsFired.push("event"); + }); + assertEquals(eventsFired, ["newListener"]); + eventsFired = []; + testEmitter.emit("event"); + assertEquals(eventsFired, ["event"]); + } +}); + +test({ + name: + 'When removing a listenert, "removeListener" event is fired after removal', + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("removeListener", () => { + eventsFired.push("removeListener"); + }); + const eventFunction = function(): void { + eventsFired.push("event"); + }; + testEmitter.on("event", eventFunction); + + assertEquals(eventsFired, []); + testEmitter.removeListener("event", eventFunction); + assertEquals(eventsFired, ["removeListener"]); + } +}); + +test({ + name: + "Default max listeners is 10, but can be changed by direct assignment only", + fn() { + assertEquals(EventEmitter.defaultMaxListeners, 10); + new EventEmitter().setMaxListeners(20); + assertEquals(EventEmitter.defaultMaxListeners, 10); + EventEmitter.defaultMaxListeners = 20; + assertEquals(EventEmitter.defaultMaxListeners, 20); + EventEmitter.defaultMaxListeners = 10; //reset back to original value + } +}); + +test({ + name: "addListener adds a listener, and listener count is correct", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(1, testEmitter.listenerCount("event")); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(2, testEmitter.listenerCount("event")); + } +}); + +test({ + name: "Emitted events are called synchronously in the order they were added", + fn() { + const testEmitter = new EventEmitter(); + const eventsFired = []; + testEmitter.on("event", oneArg => { + eventsFired.push("event(" + oneArg + ")"); + }); + testEmitter.on("event", (oneArg, twoArg) => { + eventsFired.push("event(" + oneArg + ", " + twoArg + ")"); + }); + + testEmitter.on("non-event", shouldNeverBeEmitted); + + testEmitter.on("event", (oneArg, twoArg, threeArg) => { + eventsFired.push( + "event(" + oneArg + ", " + twoArg + ", " + threeArg + ")" + ); + }); + testEmitter.emit("event", 1, 2, 3); + assertEquals(eventsFired, ["event(1)", "event(1, 2)", "event(1, 2, 3)"]); + } +}); + +test({ + name: "Registered event names are returned as strings or Sybols", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + const sym = Symbol("symbol"); + testEmitter.on(sym, shouldNeverBeEmitted); + assertEquals(testEmitter.eventNames(), ["event", sym]); + } +}); + +test({ + name: "You can set and get max listeners", + fn() { + const testEmitter = new EventEmitter(); + assertEquals(testEmitter.getMaxListeners(), 10); + testEmitter.setMaxListeners(20); + assertEquals(testEmitter.getMaxListeners(), 20); + } +}); + +test({ + name: "You can retrieve registered functions for an event", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("someOtherEvent", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + const testFunction = (): void => {}; + testEmitter.on("event", testFunction); + assertEquals(testEmitter.listeners("event"), [ + shouldNeverBeEmitted, + testFunction + ]); + } +}); + +test({ + name: "Off is alias for removeListener", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 1); + testEmitter.off("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 0); + } +}); + +test({ + name: "Event registration can be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter + .on("event", shouldNeverBeEmitted) + .on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + } +}); + +test({ + name: "Events can be registered to only fire once", + fn() { + let eventsFired = []; + const testEmitter = new EventEmitter(); + //prove multiple emits on same event first (when registered with 'on') + testEmitter.on("multiple event", () => { + eventsFired.push("multiple event"); + }); + testEmitter.emit("multiple event"); + testEmitter.emit("multiple event"); + assertEquals(eventsFired, ["multiple event", "multiple event"]); + + //now prove multiple events registered via 'once' only emit once + eventsFired = []; + testEmitter.once("single event", () => { + eventsFired.push("single event"); + }); + testEmitter.emit("single event"); + testEmitter.emit("single event"); + assertEquals(eventsFired, ["single event"]); + } +}); + +test({ + name: + "You can inject a listener into the start of the stack, rather than at the end", + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("event", () => { + eventsFired.push("first"); + }); + testEmitter.on("event", () => { + eventsFired.push("second"); + }); + testEmitter.prependListener("event", () => { + eventsFired.push("third"); + }); + testEmitter.emit("event"); + assertEquals(eventsFired, ["third", "first", "second"]); + } +}); + +test({ + name: 'You can prepend a "once" listener', + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("event", () => { + eventsFired.push("first"); + }); + testEmitter.on("event", () => { + eventsFired.push("second"); + }); + testEmitter.prependOnceListener("event", () => { + eventsFired.push("third"); + }); + testEmitter.emit("event"); + testEmitter.emit("event"); + assertEquals(eventsFired, ["third", "first", "second", "first", "second"]); + } +}); + +test({ + name: "Remove all listeners, which can also be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("other event", shouldNeverBeEmitted); + testEmitter.on("other event", shouldNeverBeEmitted); + testEmitter.once("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 3); + + testEmitter.removeAllListeners("event").removeAllListeners("other event"); + + assertEquals(testEmitter.listenerCount("event"), 0); + assertEquals(testEmitter.listenerCount("other event"), 0); + } +}); + +test({ + name: "Remove individual listeners, which can also be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.once("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 1); + + testEmitter.removeListener("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 0); + + testEmitter + .removeListener("event", shouldNeverBeEmitted) + .removeListener("event", shouldNeverBeEmitted); + + assertEquals(testEmitter.listenerCount("event"), 0); + assertEquals(testEmitter.listenerCount("other event"), 0); + } +}); + +test({ + name: "It is OK to try to remove non-existant listener", + fn() { + const testEmitter = new EventEmitter(); + + const madeUpEvent = (): void => { + fail("Should never be called"); + }; + + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 1); + + testEmitter.removeListener("event", madeUpEvent); + testEmitter.removeListener("non-existant event", madeUpEvent); + + assertEquals(testEmitter.listenerCount("event"), 1); + } +}); + +test({ + name: "all listeners complete execution even if removed before execution", + fn() { + const testEmitter = new EventEmitter(); + let eventsProcessed = []; + const listenerB = (): number => eventsProcessed.push("B"); + const listenerA = (): void => { + eventsProcessed.push("A"); + testEmitter.removeListener("event", listenerB); + }; + + testEmitter.on("event", listenerA); + testEmitter.on("event", listenerB); + + testEmitter.emit("event"); + assertEquals(eventsProcessed, ["A", "B"]); + + eventsProcessed = []; + testEmitter.emit("event"); + assertEquals(eventsProcessed, ["A"]); + } +}); + +test({ + name: 'Raw listener will return event listener or wrapped "once" function', + fn() { + const testEmitter = new EventEmitter(); + const eventsProcessed = []; + const listenerA = (): number => eventsProcessed.push("A"); + const listenerB = (): number => eventsProcessed.push("B"); + testEmitter.on("event", listenerA); + testEmitter.once("once-event", listenerB); + + const rawListenersForEvent = testEmitter.rawListeners("event"); + const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); + + assertEquals(rawListenersForEvent.length, 1); + assertEquals(rawListenersForOnceEvent.length, 1); + assertEquals(rawListenersForEvent[0], listenerA); + assertEquals( + (rawListenersForOnceEvent[0] as WrappedFunction).listener, + listenerB + ); + } +}); + +test({ + name: + "Once wrapped raw listeners may be executed multiple times, until the wrapper is executed", + fn() { + const testEmitter = new EventEmitter(); + let eventsProcessed = []; + const listenerA = (): number => eventsProcessed.push("A"); + testEmitter.once("once-event", listenerA); + + const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); + const wrappedFn: WrappedFunction = rawListenersForOnceEvent[0] as WrappedFunction; + wrappedFn.listener(); + wrappedFn.listener(); + wrappedFn.listener(); + assertEquals(eventsProcessed, ["A", "A", "A"]); + + eventsProcessed = []; + wrappedFn(); // executing the wrapped listener function will remove it from the event + assertEquals(eventsProcessed, ["A"]); + assertEquals(testEmitter.listeners("once-event").length, 0); + } +}); + +test({ + name: "Can add once event listener to EventEmitter via standalone function", + async fn() { + const ee: EventEmitter = new EventEmitter(); + setTimeout(() => { + ee.emit("event", 42, "foo"); + }, 0); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const valueArr: any[] = await once(ee, "event"); + assertEquals(valueArr, [42, "foo"]); + } +}); + +test({ + name: "Can add once event listener to EventTarget via standalone function", + async fn() { + const et: EventTarget = new EventTarget(); + setTimeout(() => { + const event: Event = new Event("event", { composed: true }); + et.dispatchEvent(event); + }, 0); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const eventObj: any[] = await once(et, "event"); + assert(!eventObj[0].isTrusted); + } +}); + +test({ + name: "Only valid integers are allowed for max listeners", + fn() { + const ee: EventEmitter = new EventEmitter(); + ee.setMaxListeners(0); + assertThrows( + () => { + ee.setMaxListeners(-1); + }, + Error, + "must be >= 0" + ); + assertThrows( + () => { + ee.setMaxListeners(3.45); + }, + Error, + "must be 'an integer'" + ); + } +}); diff --git a/std/node/module.ts b/std/node/module.ts index f27ef25f2..ac436c555 100644 --- a/std/node/module.ts +++ b/std/node/module.ts @@ -26,6 +26,7 @@ import * as nodeUtil from "./util.ts"; import * as nodePath from "./path.ts"; import * as nodeTimers from "./timers.ts"; import * as nodeOs from "./os.ts"; +import * as nodeEvents from "./events.ts"; import * as path from "../path/mod.ts"; import { assert } from "../testing/asserts.ts"; @@ -579,11 +580,14 @@ function createNativeModule(id: string, exports: any): Module { mod.loaded = true; return mod; } + nativeModulePolyfill.set("fs", createNativeModule("fs", nodeFS)); -nativeModulePolyfill.set("util", createNativeModule("util", nodeUtil)); +nativeModulePolyfill.set("events", createNativeModule("events", nodeEvents)); +nativeModulePolyfill.set("os", createNativeModule("os", nodeOs)); nativeModulePolyfill.set("path", createNativeModule("path", nodePath)); nativeModulePolyfill.set("timers", createNativeModule("timers", nodeTimers)); -nativeModulePolyfill.set("os", createNativeModule("os", nodeOs)); +nativeModulePolyfill.set("util", createNativeModule("util", nodeUtil)); + function loadNativeModule( _filename: string, request: string diff --git a/std/node/os.ts b/std/node/os.ts index c5e0f6c76..e4a00c450 100644 --- a/std/node/os.ts +++ b/std/node/os.ts @@ -19,6 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. import { notImplemented } from "./_utils.ts"; +import { validateIntegerRange } from "./util.ts"; import { EOL as fsEOL } from "../fs/eol.ts"; const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; @@ -117,7 +118,7 @@ export function freemem(): number { /** Not yet implemented */ export function getPriority(pid = 0): number { - validateInt32(pid, "pid"); + validateIntegerRange(pid, "pid"); notImplemented(SEE_GITHUB_ISSUE); } @@ -162,8 +163,8 @@ export function setPriority(pid: number, priority?: number): void { priority = pid; pid = 0; } - validateInt32(pid, "pid"); - validateInt32(priority, "priority", -20, 19); + validateIntegerRange(pid, "pid"); + validateIntegerRange(priority, "priority", -20, 19); notImplemented(SEE_GITHUB_ISSUE); } @@ -211,20 +212,3 @@ export const constants = { }; export const EOL = Deno.build.os == "win" ? fsEOL.CRLF : fsEOL.LF; - -const validateInt32 = ( - value: number, - name: string, - min = -2147483648, - max = 2147483647 -): void => { - // The defaults for min and max correspond to the limits of 32-bit integers. - if (!Number.isInteger(value)) { - throw new Error(`${name} must be 'an integer' but was ${value}`); - } - if (value < min || value > max) { - throw new Error( - `${name} must be >= ${min} && <= ${max}. Value was ${value}` - ); - } -}; diff --git a/std/node/util.ts b/std/node/util.ts index d0187e616..9879da513 100644 --- a/std/node/util.ts +++ b/std/node/util.ts @@ -45,3 +45,20 @@ export function isFunction(value: unknown): boolean { export function isRegExp(value: unknown): boolean { return value instanceof RegExp; } + +export function validateIntegerRange( + value: number, + name: string, + min = -2147483648, + max = 2147483647 +): void { + // The defaults for min and max correspond to the limits of 32-bit integers. + if (!Number.isInteger(value)) { + throw new Error(`${name} must be 'an integer' but was ${value}`); + } + if (value < min || value > max) { + throw new Error( + `${name} must be >= ${min} && <= ${max}. Value was ${value}` + ); + } +} |