diff options
Diffstat (limited to 'cli/js/web')
| -rw-r--r-- | cli/js/web/blob.ts | 11 | ||||
| -rw-r--r-- | cli/js/web/body.ts | 8 | ||||
| -rw-r--r-- | cli/js/web/console.ts | 30 | ||||
| -rw-r--r-- | cli/js/web/console_table.ts | 2 | ||||
| -rw-r--r-- | cli/js/web/custom_event.ts | 23 | ||||
| -rw-r--r-- | cli/js/web/dom_iterable.ts | 2 | ||||
| -rw-r--r-- | cli/js/web/dom_types.ts | 163 | ||||
| -rw-r--r-- | cli/js/web/dom_util.ts | 17 | ||||
| -rw-r--r-- | cli/js/web/event.ts | 22 | ||||
| -rw-r--r-- | cli/js/web/event_target.ts | 18 | ||||
| -rw-r--r-- | cli/js/web/fetch.ts | 102 | ||||
| -rw-r--r-- | cli/js/web/form_data.ts | 8 | ||||
| -rw-r--r-- | cli/js/web/headers.ts | 80 | ||||
| -rw-r--r-- | cli/js/web/location.ts | 15 | ||||
| -rw-r--r-- | cli/js/web/request.ts | 2 | ||||
| -rw-r--r-- | cli/js/web/streams/readable-byte-stream-controller.ts | 2 | ||||
| -rw-r--r-- | cli/js/web/streams/readable-internals.ts | 22 | ||||
| -rw-r--r-- | cli/js/web/streams/readable-stream.ts | 14 | ||||
| -rw-r--r-- | cli/js/web/streams/shared-internals.ts | 14 | ||||
| -rw-r--r-- | cli/js/web/text_encoding.ts | 191 | ||||
| -rw-r--r-- | cli/js/web/timers.ts | 2 | ||||
| -rw-r--r-- | cli/js/web/url.ts | 97 | ||||
| -rw-r--r-- | cli/js/web/url_search_params.ts | 167 | ||||
| -rw-r--r-- | cli/js/web/util.ts | 17 | ||||
| -rw-r--r-- | cli/js/web/workers.ts | 41 |
25 files changed, 691 insertions, 379 deletions
diff --git a/cli/js/web/blob.ts b/cli/js/web/blob.ts index 31674a62d..5309ddaf4 100644 --- a/cli/js/web/blob.ts +++ b/cli/js/web/blob.ts @@ -124,12 +124,8 @@ function processBlobParts( return bytes; } -// A WeakMap holding blob to byte array mapping. -// Ensures it does not impact garbage collection. -export const blobBytesWeakMap = new WeakMap<domTypes.Blob, Uint8Array>(); - export class DenoBlob implements domTypes.Blob { - private readonly [bytesSymbol]: Uint8Array; + [bytesSymbol]: Uint8Array; readonly size: number = 0; readonly type: string = ""; @@ -165,14 +161,11 @@ export class DenoBlob implements domTypes.Blob { this[bytesSymbol] = bytes; this.size = bytes.byteLength; this.type = normalizedType; - - // Register bytes for internal private use. - blobBytesWeakMap.set(this, bytes); } slice(start?: number, end?: number, contentType?: string): DenoBlob { return new DenoBlob([this[bytesSymbol].slice(start, end)], { - type: contentType || this.type + type: contentType || this.type, }); } } diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts index 03c35848a..a16f872b9 100644 --- a/cli/js/web/body.ts +++ b/cli/js/web/body.ts @@ -124,7 +124,7 @@ export const BodyUsedError = "Failed to execute 'clone' on 'Body': body is already used"; export class Body implements domTypes.Body { - protected _stream: domTypes.ReadableStream | null; + protected _stream: domTypes.ReadableStream<string | ArrayBuffer> | null; constructor(protected _bodySource: BodySource, readonly contentType: string) { validateBodyType(this, _bodySource); @@ -148,8 +148,8 @@ export class Body implements domTypes.Body { start(controller: ReadableStreamController): void { controller.enqueue(bodySource); controller.close(); - } - }); + }, + }) as domTypes.ReadableStream<ArrayBuffer | string>; } return this._stream; } @@ -247,7 +247,7 @@ export class Body implements domTypes.Body { if (dispositionParams.has("filename")) { const filename = dispositionParams.get("filename")!; const blob = new DenoBlob([enc.encode(octets)], { - type: partContentType + type: partContentType, }); // TODO: based on spec // https://xhr.spec.whatwg.org/#dom-formdata-append diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts index f95e50b40..554c5a1b3 100644 --- a/cli/js/web/console.ts +++ b/cli/js/web/console.ts @@ -174,7 +174,7 @@ function createArrayString( displayName: "", delims: ["[", "]"], entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) + stringifyWithQuotes(el, ctx, level + 1, maxLevel), }; return createIterableString(value, ctx, level, maxLevel, printConfig); } @@ -191,7 +191,7 @@ function createTypedArrayString( displayName: typedArrayName, delims: ["[", "]"], entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) + stringifyWithQuotes(el, ctx, level + 1, maxLevel), }; return createIterableString(value, ctx, level, maxLevel, printConfig); } @@ -207,7 +207,7 @@ function createSetString( displayName: "Set", delims: ["{", "}"], entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) + stringifyWithQuotes(el, ctx, level + 1, maxLevel), }; return createIterableString(value, ctx, level, maxLevel, printConfig); } @@ -230,7 +230,7 @@ function createMapString( level + 1, maxLevel )} => ${stringifyWithQuotes(val, ctx, level + 1, maxLevel)}`; - } + }, }; return createIterableString(value, ctx, level, maxLevel, printConfig); } @@ -494,10 +494,12 @@ const timerMap = new Map<string, number>(); const isConsoleInstance = Symbol("isConsoleInstance"); export class Console { + #printFunc: PrintFunc; indentLevel: number; [isConsoleInstance] = false; - constructor(private printFunc: PrintFunc) { + constructor(printFunc: PrintFunc) { + this.#printFunc = printFunc; this.indentLevel = 0; this[isConsoleInstance] = true; @@ -511,9 +513,9 @@ export class Console { } log = (...args: unknown[]): void => { - this.printFunc( + this.#printFunc( stringifyArgs(args, { - indentLevel: this.indentLevel + indentLevel: this.indentLevel, }) + "\n", false ); @@ -523,15 +525,15 @@ export class Console { info = this.log; dir = (obj: unknown, options: InspectOptions = {}): void => { - this.printFunc(stringifyArgs([obj], options) + "\n", false); + this.#printFunc(stringifyArgs([obj], options) + "\n", false); }; dirxml = this.dir; warn = (...args: unknown[]): void => { - this.printFunc( + this.#printFunc( stringifyArgs(args, { - indentLevel: this.indentLevel + indentLevel: this.indentLevel, }) + "\n", true ); @@ -604,7 +606,7 @@ export class Console { this.log(cliTable(header, body)); const createColumn = (value: unknown, shift?: number): string[] => [ ...(shift ? [...new Array(shift)].map((): string => "") : []), - stringifyValue(value) + stringifyValue(value), ]; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -660,8 +662,8 @@ export class Console { indexKey, ...(properties || [ ...headerKeys, - !isMap && values.length > 0 && valuesKey - ]) + !isMap && values.length > 0 && valuesKey, + ]), ].filter(Boolean) as string[]; const body = [indexKeys, ...bodyValues, values]; @@ -733,7 +735,7 @@ export class Console { const message = stringifyArgs(args, { indentLevel: 0 }); const err = { name: "Trace", - message + message, }; // @ts-ignore Error.captureStackTrace(err, this.trace); diff --git a/cli/js/web/console_table.ts b/cli/js/web/console_table.ts index 7e698f712..2cb0005d7 100644 --- a/cli/js/web/console_table.ts +++ b/cli/js/web/console_table.ts @@ -19,7 +19,7 @@ const tableChars = { rightMiddle: "┤", left: "│ ", right: " │", - middle: " │ " + middle: " │ ", }; const colorRegExp = /\u001b\[\d\d?m/g; diff --git a/cli/js/web/custom_event.ts b/cli/js/web/custom_event.ts index 24a263baf..418b7ea34 100644 --- a/cli/js/web/custom_event.ts +++ b/cli/js/web/custom_event.ts @@ -1,32 +1,31 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import * as domTypes from "./dom_types.ts"; import * as event from "./event.ts"; -import { getPrivateValue, requiredArguments } from "./util.ts"; - -// WeakMaps are recommended for private attributes (see MDN link below) -// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps -export const customEventAttributes = new WeakMap(); +import { requiredArguments } from "./util.ts"; export class CustomEvent extends event.Event implements domTypes.CustomEvent { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + #detail: any; + constructor( type: string, customEventInitDict: domTypes.CustomEventInit = {} ) { - requiredArguments("CustomEvent", arguments.length, 1); super(type, customEventInitDict); + requiredArguments("CustomEvent", arguments.length, 1); const { detail = null } = customEventInitDict; - customEventAttributes.set(this, { detail }); + this.#detail = detail; } // eslint-disable-next-line @typescript-eslint/no-explicit-any get detail(): any { - return getPrivateValue(this, customEventAttributes, "detail"); + return this.#detail; } initCustomEvent( - type: string, - bubbles?: boolean, - cancelable?: boolean, + _type: string, + _bubbles?: boolean, + _cancelable?: boolean, // eslint-disable-next-line @typescript-eslint/no-explicit-any detail?: any ): void { @@ -34,7 +33,7 @@ export class CustomEvent extends event.Event implements domTypes.CustomEvent { return; } - customEventAttributes.set(this, { detail }); + this.#detail = detail; } get [Symbol.toStringTag](): string { diff --git a/cli/js/web/dom_iterable.ts b/cli/js/web/dom_iterable.ts index e9be9009f..191958f11 100644 --- a/cli/js/web/dom_iterable.ts +++ b/cli/js/web/dom_iterable.ts @@ -72,7 +72,7 @@ export function DomIterableMixin<K, V, TBase extends Constructor>( // we want the Base class name to be the name of the class. Object.defineProperty(DomIterable, "name", { value: Base.name, - configurable: true + configurable: true, }); return DomIterable; diff --git a/cli/js/web/dom_types.ts b/cli/js/web/dom_types.ts index bcc6be468..33cda1582 100644 --- a/cli/js/web/dom_types.ts +++ b/cli/js/web/dom_types.ts @@ -68,7 +68,7 @@ interface AbortSignalEventMap { export enum NodeType { ELEMENT_NODE = 1, TEXT_NODE = 3, - DOCUMENT_FRAGMENT_NODE = 11 + DOCUMENT_FRAGMENT_NODE = 11, } export const eventTargetHost: unique symbol = Symbol(); @@ -153,7 +153,7 @@ export enum EventPhase { NONE = 0, CAPTURING_PHASE = 1, AT_TARGET = 2, - BUBBLING_PHASE = 3 + BUBBLING_PHASE = 3, } export interface EventPath { @@ -280,7 +280,7 @@ export interface Blob { } export interface Body { - readonly body: ReadableStream | null; + readonly body: ReadableStream<Uint8Array> | null; readonly bodyUsed: boolean; arrayBuffer(): Promise<ArrayBuffer>; blob(): Promise<Blob>; @@ -289,11 +289,78 @@ export interface Body { text(): Promise<string>; } -export interface ReadableStream { +export interface ReadableStreamReadDoneResult<T> { + done: true; + value?: T; +} + +export interface ReadableStreamReadValueResult<T> { + done: false; + value: T; +} + +export type ReadableStreamReadResult<T> = + | ReadableStreamReadValueResult<T> + | ReadableStreamReadDoneResult<T>; + +export interface ReadableStreamDefaultReader<R = any> { + readonly closed: Promise<void>; + cancel(reason?: any): Promise<void>; + read(): Promise<ReadableStreamReadResult<R>>; + releaseLock(): void; +} + +export interface PipeOptions { + preventAbort?: boolean; + preventCancel?: boolean; + preventClose?: boolean; + signal?: AbortSignal; +} + +export interface ReadableStream<R = any> { readonly locked: boolean; cancel(reason?: any): Promise<void>; - getReader(): ReadableStreamReader; - tee(): ReadableStream[]; + getReader(options: { mode: "byob" }): ReadableStreamBYOBReader; + getReader(): ReadableStreamDefaultReader<R>; + /* disabled for now + pipeThrough<T>( + { + writable, + readable + }: { + writable: WritableStream<R>; + readable: ReadableStream<T>; + }, + options?: PipeOptions + ): ReadableStream<T>; + pipeTo(dest: WritableStream<R>, options?: PipeOptions): Promise<void>; + */ + tee(): [ReadableStream<R>, ReadableStream<R>]; +} + +export interface ReadableStreamBYOBReader { + readonly closed: Promise<void>; + cancel(reason?: any): Promise<void>; + read<T extends ArrayBufferView>( + view: T + ): Promise<ReadableStreamReadResult<T>>; + releaseLock(): void; +} + +export interface WritableStream<W = any> { + readonly locked: boolean; + abort(reason?: any): Promise<void>; + getWriter(): WritableStreamDefaultWriter<W>; +} + +export interface WritableStreamDefaultWriter<W = any> { + readonly closed: Promise<void>; + readonly desiredSize: number | null; + readonly ready: Promise<void>; + abort(reason?: any): Promise<void>; + close(): Promise<void>; + releaseLock(): void; + write(chunk: W): Promise<void>; } export interface UnderlyingSource<R = any> { @@ -311,9 +378,9 @@ export interface UnderlyingByteSource { type: "bytes"; } -export interface ReadableStreamReader { - cancel(reason?: any): Promise<void>; - read(): Promise<any>; +export interface ReadableStreamReader<R = any> { + cancel(reason: any): Promise<void>; + read(): Promise<ReadableStreamReadResult<R>>; releaseLock(): void; } @@ -530,12 +597,20 @@ export interface Response extends Body { clone(): Response; } +export interface DOMStringList { + readonly length: number; + contains(string: string): boolean; + item(index: number): string | null; + [index: number]: string; +} + export interface Location { - readonly ancestorOrigins: string[]; + readonly ancestorOrigins: DOMStringList; hash: string; host: string; hostname: string; href: string; + toString(): string; readonly origin: string; pathname: string; port: string; @@ -543,6 +618,72 @@ export interface Location { search: string; assign(url: string): void; reload(): void; - reload(forcedReload: boolean): void; replace(url: string): void; } + +export interface URL { + hash: string; + host: string; + hostname: string; + href: string; + toString(): string; + readonly origin: string; + password: string; + pathname: string; + port: string; + protocol: string; + search: string; + readonly searchParams: URLSearchParams; + username: string; + toJSON(): string; +} + +export interface URLSearchParams { + /** + * Appends a specified key/value pair as a new search parameter. + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + */ + delete(name: string): void; + /** + * Returns the first value associated to the given search parameter. + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + */ + has(name: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + */ + set(name: string, value: string): void; + sort(): void; + /** + * Returns a string containing a query string suitable for use in a URL. Does not include the question mark. + */ + toString(): string; + forEach( + callbackfn: (value: string, key: string, parent: URLSearchParams) => void, + thisArg?: any + ): void; + + [Symbol.iterator](): IterableIterator<[string, string]>; + /** + * Returns an array of key, value pairs for every entry in the search params. + */ + entries(): IterableIterator<[string, string]>; + /** + * Returns a list of keys in the search params. + */ + keys(): IterableIterator<string>; + /** + * Returns a list of values in the search params. + */ + values(): IterableIterator<string>; +} diff --git a/cli/js/web/dom_util.ts b/cli/js/web/dom_util.ts index 5780d9c52..40a8c618f 100644 --- a/cli/js/web/dom_util.ts +++ b/cli/js/web/dom_util.ts @@ -2,6 +2,23 @@ // Utility functions for DOM nodes import * as domTypes from "./dom_types.ts"; +export function getDOMStringList(arr: string[]): domTypes.DOMStringList { + Object.defineProperties(arr, { + contains: { + value(searchElement: string): boolean { + return arr.includes(searchElement); + }, + enumerable: true, + }, + item: { + value(idx: number): string | null { + return idx in arr ? arr[idx] : null; + }, + }, + }); + return (arr as unknown) as domTypes.DOMStringList; +} + export function isNode(nodeImpl: domTypes.EventTarget | null): boolean { return Boolean(nodeImpl && "nodeType" in nodeImpl); } diff --git a/cli/js/web/event.ts b/cli/js/web/event.ts index ef5c4d175..b063efa60 100644 --- a/cli/js/web/event.ts +++ b/cli/js/web/event.ts @@ -39,11 +39,11 @@ export class Event implements domTypes.Event { isTrusted: false, relatedTarget: null, target: null, - timeStamp: Date.now() + timeStamp: Date.now(), }); Reflect.defineProperty(this, "isTrusted", { enumerable: true, - get: isTrusted + get: isTrusted, }); } @@ -90,7 +90,7 @@ export class Event implements domTypes.Event { isTrusted: this.isTrusted, relatedTarget: this.relatedTarget, target: this.target, - timeStamp: this.timeStamp + timeStamp: this.timeStamp, }); } @@ -121,7 +121,7 @@ export class Event implements domTypes.Event { isTrusted: this.isTrusted, relatedTarget: this.relatedTarget, target: this.target, - timeStamp: this.timeStamp + timeStamp: this.timeStamp, }); } @@ -156,7 +156,7 @@ export class Event implements domTypes.Event { isTrusted: this.isTrusted, relatedTarget: value, target: this.target, - timeStamp: this.timeStamp + timeStamp: this.timeStamp, }); } @@ -175,7 +175,7 @@ export class Event implements domTypes.Event { isTrusted: this.isTrusted, relatedTarget: this.relatedTarget, target: value, - timeStamp: this.timeStamp + timeStamp: this.timeStamp, }); } @@ -200,8 +200,8 @@ export class Event implements domTypes.Event { rootOfClosedTree: false, slotInClosedTree: false, target: null, - touchTargetList: [] - } + touchTargetList: [], + }, ]; let currentTargetIndex = 0; @@ -242,7 +242,7 @@ export class Event implements domTypes.Event { rootOfClosedTree: false, slotInClosedTree: false, target: null, - touchTargetList: [] + touchTargetList: [], }); } @@ -277,7 +277,7 @@ export class Event implements domTypes.Event { rootOfClosedTree: false, slotInClosedTree: false, target: null, - touchTargetList: [] + touchTargetList: [], }); } @@ -314,7 +314,7 @@ Reflect.defineProperty(Event.prototype, "cancelable", { enumerable: true }); Reflect.defineProperty(Event.prototype, "composed", { enumerable: true }); Reflect.defineProperty(Event.prototype, "currentTarget", { enumerable: true }); Reflect.defineProperty(Event.prototype, "defaultPrevented", { - enumerable: true + enumerable: true, }); Reflect.defineProperty(Event.prototype, "dispatched", { enumerable: true }); Reflect.defineProperty(Event.prototype, "eventPhase", { enumerable: true }); diff --git a/cli/js/web/event_target.ts b/cli/js/web/event_target.ts index 7fe26441d..605504a3a 100644 --- a/cli/js/web/event_target.ts +++ b/cli/js/web/event_target.ts @@ -7,7 +7,7 @@ import { isShadowRoot, isShadowInclusiveAncestor, isSlotable, - retarget + retarget, } from "./dom_util.ts"; // https://dom.spec.whatwg.org/#get-the-parent @@ -70,7 +70,7 @@ export class EventTarget implements domTypes.EventTarget { listeners[type].push({ callback, - options: normalizedOptions + options: normalizedOptions, }); } @@ -438,7 +438,7 @@ const eventTargetHelpers = { const returnValue: domTypes.AddEventListenerOptions = { capture: Boolean(options), once: false, - passive: false + passive: false, }; return returnValue; @@ -452,7 +452,7 @@ const eventTargetHelpers = { ): domTypes.EventListenerOptions { if (typeof options === "boolean" || typeof options === "undefined") { const returnValue: domTypes.EventListenerOptions = { - capture: Boolean(options) + capture: Boolean(options), }; return returnValue; @@ -481,17 +481,17 @@ const eventTargetHelpers = { relatedTarget, touchTargetList: touchTargets, rootOfClosedTree, - slotInClosedTree + slotInClosedTree, }); - } + }, }; Reflect.defineProperty(EventTarget.prototype, "addEventListener", { - enumerable: true + enumerable: true, }); Reflect.defineProperty(EventTarget.prototype, "removeEventListener", { - enumerable: true + enumerable: true, }); Reflect.defineProperty(EventTarget.prototype, "dispatchEvent", { - enumerable: true + enumerable: true, }); diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts index 62e5d7928..6438838c7 100644 --- a/cli/js/web/fetch.ts +++ b/cli/js/web/fetch.ts @@ -32,53 +32,58 @@ function hasHeaderValueOf(s: string, value: string): boolean { return new RegExp(`^${value}[\t\s]*;?`).test(s); } -class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { - private _bodyUsed = false; - private _bodyPromise: null | Promise<ArrayBuffer> = null; - private _data: ArrayBuffer | null = null; +class Body + implements domTypes.Body, domTypes.ReadableStream<Uint8Array>, io.ReadCloser { + #bodyUsed = false; + #bodyPromise: Promise<ArrayBuffer> | null = null; + #data: ArrayBuffer | null = null; + #rid: number; readonly locked: boolean = false; // TODO - readonly body: null | Body = this; + readonly body: domTypes.ReadableStream<Uint8Array>; - constructor(private rid: number, readonly contentType: string) {} + constructor(rid: number, readonly contentType: string) { + this.#rid = rid; + this.body = this; + } - private async _bodyBuffer(): Promise<ArrayBuffer> { - assert(this._bodyPromise == null); + #bodyBuffer = async (): Promise<ArrayBuffer> => { + assert(this.#bodyPromise == null); const buf = new Buffer(); try { const nread = await buf.readFrom(this); const ui8 = buf.bytes(); assert(ui8.byteLength === nread); - this._data = ui8.buffer.slice( + this.#data = ui8.buffer.slice( ui8.byteOffset, ui8.byteOffset + nread ) as ArrayBuffer; - assert(this._data.byteLength === nread); + assert(this.#data.byteLength === nread); } finally { this.close(); } - return this._data; - } + return this.#data; + }; // eslint-disable-next-line require-await async arrayBuffer(): Promise<ArrayBuffer> { // If we've already bufferred the response, just return it. - if (this._data != null) { - return this._data; + if (this.#data != null) { + return this.#data; } // If there is no _bodyPromise yet, start it. - if (this._bodyPromise == null) { - this._bodyPromise = this._bodyBuffer(); + if (this.#bodyPromise == null) { + this.#bodyPromise = this.#bodyBuffer(); } - return this._bodyPromise; + return this.#bodyPromise; } async blob(): Promise<domTypes.Blob> { const arrayBuffer = await this.arrayBuffer(); return new DenoBlob([arrayBuffer], { - type: this.contentType + type: this.contentType, }); } @@ -164,7 +169,7 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { if (dispositionParams.has("filename")) { const filename = dispositionParams.get("filename")!; const blob = new DenoBlob([enc.encode(octets)], { - type: partContentType + type: partContentType, }); // TODO: based on spec // https://xhr.spec.whatwg.org/#dom-formdata-append @@ -220,12 +225,12 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { } read(p: Uint8Array): Promise<number | io.EOF> { - this._bodyUsed = true; - return read(this.rid, p); + this.#bodyUsed = true; + return read(this.#rid, p); } close(): Promise<void> { - close(this.rid); + close(this.#rid); return Promise.resolve(); } @@ -233,7 +238,11 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { return notImplemented(); } - getReader(): domTypes.ReadableStreamReader { + getReader(options: { mode: "byob" }): domTypes.ReadableStreamBYOBReader; + getReader(): domTypes.ReadableStreamDefaultReader<Uint8Array>; + getReader(): + | domTypes.ReadableStreamBYOBReader + | domTypes.ReadableStreamDefaultReader<Uint8Array> { return notImplemented(); } @@ -246,7 +255,24 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { } get bodyUsed(): boolean { - return this._bodyUsed; + return this.#bodyUsed; + } + + pipeThrough<T>( + _: { + writable: domTypes.WritableStream<Uint8Array>; + readable: domTypes.ReadableStream<T>; + }, + _options?: domTypes.PipeOptions + ): domTypes.ReadableStream<T> { + return notImplemented(); + } + + pipeTo( + _dest: domTypes.WritableStream<Uint8Array>, + _options?: domTypes.PipeOptions + ): Promise<void> { + return notImplemented(); } } @@ -255,7 +281,7 @@ export class Response implements domTypes.Response { readonly redirected: boolean; headers: domTypes.Headers; readonly trailer: Promise<domTypes.Headers>; - readonly body: null | Body; + readonly body: Body | null; constructor( readonly url: string, @@ -308,7 +334,7 @@ export class Response implements domTypes.Response { "Content-Type", "Expires", "Last-Modified", - "Pragma" + "Pragma", ].map((c: string) => c.toLowerCase()); for (const h of this.headers) { /* Technically this is still not standards compliant because we are @@ -337,35 +363,36 @@ export class Response implements domTypes.Response { this.redirected = redirected_; } - private bodyViewable(): boolean { + #bodyViewable = (): boolean => { if ( this.type == "error" || this.type == "opaque" || this.type == "opaqueredirect" || this.body == undefined - ) + ) { return true; + } return false; - } + }; arrayBuffer(): Promise<ArrayBuffer> { /* You have to do the null check here and not in the function because * otherwise TS complains about this.body potentially being null */ - if (this.bodyViewable() || this.body == null) { + if (this.#bodyViewable() || this.body == null) { return Promise.reject(new Error("Response body is null")); } return this.body.arrayBuffer(); } blob(): Promise<domTypes.Blob> { - if (this.bodyViewable() || this.body == null) { + if (this.#bodyViewable() || this.body == null) { return Promise.reject(new Error("Response body is null")); } return this.body.blob(); } formData(): Promise<domTypes.FormData> { - if (this.bodyViewable() || this.body == null) { + if (this.#bodyViewable() || this.body == null) { return Promise.reject(new Error("Response body is null")); } return this.body.formData(); @@ -373,14 +400,14 @@ export class Response implements domTypes.Response { // eslint-disable-next-line @typescript-eslint/no-explicit-any json(): Promise<any> { - if (this.bodyViewable() || this.body == null) { + if (this.#bodyViewable() || this.body == null) { return Promise.reject(new Error("Response body is null")); } return this.body.json(); } text(): Promise<string> { - if (this.bodyViewable() || this.body == null) { + if (this.#bodyViewable() || this.body == null) { return Promise.reject(new Error("Response body is null")); } return this.body.text(); @@ -453,7 +480,7 @@ function sendFetchReq( const args = { method, url, - headers: headerArray + headers: headerArray, }; return opFetch(args, body); @@ -527,8 +554,9 @@ export async function fetch( } part += "\r\n"; if (fieldValue instanceof DomFileImpl) { - part += `Content-Type: ${fieldValue.type || - "application/octet-stream"}\r\n`; + part += `Content-Type: ${ + fieldValue.type || "application/octet-stream" + }\r\n`; } part += "\r\n"; if (fieldValue instanceof DomFileImpl) { diff --git a/cli/js/web/form_data.ts b/cli/js/web/form_data.ts index f60a146d9..db5d24ad4 100644 --- a/cli/js/web/form_data.ts +++ b/cli/js/web/form_data.ts @@ -8,7 +8,7 @@ import { requiredArguments } from "./util.ts"; const dataSymbol = Symbol("data"); class FormDataBase { - private [dataSymbol]: Array<[string, domTypes.FormDataEntryValue]> = []; + [dataSymbol]: Array<[string, domTypes.FormDataEntryValue]> = []; append(name: string, value: string): void; append(name: string, value: blob.DenoBlob, filename?: string): void; @@ -17,7 +17,7 @@ class FormDataBase { name = String(name); if (value instanceof blob.DenoBlob) { const dfile = new domFile.DomFileImpl([value], filename || name, { - type: value.type + type: value.type, }); this[dataSymbol].push([name, dfile]); } else { @@ -84,7 +84,7 @@ class FormDataBase { if (!found) { if (value instanceof blob.DenoBlob) { const dfile = new domFile.DomFileImpl([value], filename || name, { - type: value.type + type: value.type, }); this[dataSymbol][i][1] = dfile; } else { @@ -103,7 +103,7 @@ class FormDataBase { if (!found) { if (value instanceof blob.DenoBlob) { const dfile = new domFile.DomFileImpl([value], filename || name, { - type: value.type + type: value.type, }); this[dataSymbol].push([name, dfile]); } else { diff --git a/cli/js/web/headers.ts b/cli/js/web/headers.ts index 9ff594224..e1d81393d 100644 --- a/cli/js/web/headers.ts +++ b/cli/js/web/headers.ts @@ -17,32 +17,32 @@ function isHeaders(value: any): value is domTypes.Headers { const headerMap = Symbol("header map"); -// ref: https://fetch.spec.whatwg.org/#dom-headers -class HeadersBase { - private [headerMap]: Map<string, string>; - // TODO: headerGuard? Investigate if it is needed - // node-fetch did not implement this but it is in the spec - - private _normalizeParams(name: string, value?: string): string[] { - name = String(name).toLowerCase(); - value = String(value).trim(); - return [name, value]; - } +// TODO: headerGuard? Investigate if it is needed +// node-fetch did not implement this but it is in the spec +function normalizeParams(name: string, value?: string): string[] { + name = String(name).toLowerCase(); + value = String(value).trim(); + return [name, value]; +} - // The following name/value validations are copied from - // https://github.com/bitinn/node-fetch/blob/master/src/headers.js - // Copyright (c) 2016 David Frank. MIT License. - private _validateName(name: string): void { - if (invalidTokenRegex.test(name) || name === "") { - throw new TypeError(`${name} is not a legal HTTP header name`); - } +// The following name/value validations are copied from +// https://github.com/bitinn/node-fetch/blob/master/src/headers.js +// Copyright (c) 2016 David Frank. MIT License. +function validateName(name: string): void { + if (invalidTokenRegex.test(name) || name === "") { + throw new TypeError(`${name} is not a legal HTTP header name`); } +} - private _validateValue(value: string): void { - if (invalidHeaderCharRegex.test(value)) { - throw new TypeError(`${value} is not a legal HTTP header value`); - } +function validateValue(value: string): void { + if (invalidHeaderCharRegex.test(value)) { + throw new TypeError(`${value} is not a legal HTTP header value`); } +} + +// ref: https://fetch.spec.whatwg.org/#dom-headers +class HeadersBase { + [headerMap]: Map<string, string>; constructor(init?: domTypes.HeadersInit) { if (init === null) { @@ -64,9 +64,9 @@ class HeadersBase { 2 ); - const [name, value] = this._normalizeParams(tuple[0], tuple[1]); - this._validateName(name); - this._validateValue(value); + const [name, value] = normalizeParams(tuple[0], tuple[1]); + validateName(name); + validateValue(value); const existingValue = this[headerMap].get(name); this[headerMap].set( name, @@ -77,9 +77,9 @@ class HeadersBase { const names = Object.keys(init); for (const rawName of names) { const rawValue = init[rawName]; - const [name, value] = this._normalizeParams(rawName, rawValue); - this._validateName(name); - this._validateValue(value); + const [name, value] = normalizeParams(rawName, rawValue); + validateName(name); + validateValue(value); this[headerMap].set(name, value); } } @@ -101,9 +101,9 @@ class HeadersBase { // ref: https://fetch.spec.whatwg.org/#concept-headers-append append(name: string, value: string): void { requiredArguments("Headers.append", arguments.length, 2); - const [newname, newvalue] = this._normalizeParams(name, value); - this._validateName(newname); - this._validateValue(newvalue); + const [newname, newvalue] = normalizeParams(name, value); + validateName(newname); + validateValue(newvalue); const v = this[headerMap].get(newname); const str = v ? `${v}, ${newvalue}` : newvalue; this[headerMap].set(newname, str); @@ -111,31 +111,31 @@ class HeadersBase { delete(name: string): void { requiredArguments("Headers.delete", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); + const [newname] = normalizeParams(name); + validateName(newname); this[headerMap].delete(newname); } get(name: string): string | null { requiredArguments("Headers.get", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); + const [newname] = normalizeParams(name); + validateName(newname); const value = this[headerMap].get(newname); return value || null; } has(name: string): boolean { requiredArguments("Headers.has", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); + const [newname] = normalizeParams(name); + validateName(newname); return this[headerMap].has(newname); } set(name: string, value: string): void { requiredArguments("Headers.set", arguments.length, 2); - const [newname, newvalue] = this._normalizeParams(name, value); - this._validateName(newname); - this._validateValue(newvalue); + const [newname, newvalue] = normalizeParams(name, value); + validateName(newname); + validateValue(newvalue); this[headerMap].set(newname, newvalue); } diff --git a/cli/js/web/location.ts b/cli/js/web/location.ts index d48cce3c7..862a4c1e4 100644 --- a/cli/js/web/location.ts +++ b/cli/js/web/location.ts @@ -1,12 +1,15 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { URL } from "./url.ts"; import { notImplemented } from "../util.ts"; -import { Location } from "./dom_types.ts"; +import { DOMStringList, Location } from "./dom_types.ts"; +import { getDOMStringList } from "./dom_util.ts"; export class LocationImpl implements Location { + #url: URL; + constructor(url: string) { const u = new URL(url); - this.url = u; + this.#url = u; this.hash = u.hash; this.host = u.host; this.href = u.href; @@ -18,13 +21,11 @@ export class LocationImpl implements Location { this.search = u.search; } - private url: URL; - toString(): string { - return this.url.toString(); + return this.#url.toString(); } - readonly ancestorOrigins: string[] = []; + readonly ancestorOrigins: DOMStringList = getDOMStringList([]); hash: string; host: string; hostname: string; @@ -45,6 +46,8 @@ export class LocationImpl implements Location { } } +/** Sets the `window.location` at runtime. + * @internal */ export function setLocation(url: string): void { globalThis.location = new LocationImpl(url); Object.freeze(globalThis.location); diff --git a/cli/js/web/request.ts b/cli/js/web/request.ts index 8afc35e7a..96edaf59e 100644 --- a/cli/js/web/request.ts +++ b/cli/js/web/request.ts @@ -136,7 +136,7 @@ export class Request extends body.Body implements domTypes.Request { body: body2, method: this.method, headers: new Headers(headersList), - credentials: this.credentials + credentials: this.credentials, }); return cloned; } diff --git a/cli/js/web/streams/readable-byte-stream-controller.ts b/cli/js/web/streams/readable-byte-stream-controller.ts index 8067b5d35..1b473b77a 100644 --- a/cli/js/web/streams/readable-byte-stream-controller.ts +++ b/cli/js/web/streams/readable-byte-stream-controller.ts @@ -148,7 +148,7 @@ export class ReadableByteStreamController bytesFilled: 0, elementSize: 1, ctor: Uint8Array, - readerType: "default" + readerType: "default", }; this[rs.pendingPullIntos_].push(pullIntoDescriptor); } diff --git a/cli/js/web/streams/readable-internals.ts b/cli/js/web/streams/readable-internals.ts index b96262ef7..f46c79850 100644 --- a/cli/js/web/streams/readable-internals.ts +++ b/cli/js/web/streams/readable-internals.ts @@ -10,7 +10,7 @@ import { QueuingStrategy, QueuingStrategySizeCallback, UnderlyingSource, - UnderlyingByteSource + UnderlyingByteSource, } from "../dom_types.ts"; // ReadableStreamDefaultController @@ -345,7 +345,7 @@ export function readableStreamCancel<OutputType>( const sourceCancelPromise = stream[readableStreamController_][cancelSteps_]( reason ); - return sourceCancelPromise.then(_ => undefined); + return sourceCancelPromise.then((_) => undefined); } export function readableStreamClose<OutputType>( @@ -558,13 +558,13 @@ export function setUpReadableStreamDefaultController<OutputType>( const startResult = startAlgorithm(); Promise.resolve(startResult).then( - _ => { + (_) => { controller[started_] = true; // Assert: controller.[[pulling]] is false. // Assert: controller.[[pullAgain]] is false. readableStreamDefaultControllerCallPullIfNeeded(controller); }, - error => { + (error) => { readableStreamDefaultControllerError(controller, error); } ); @@ -678,14 +678,14 @@ export function readableStreamDefaultControllerCallPullIfNeeded<OutputType>( controller[pulling_] = true; controller[pullAlgorithm_](controller).then( - _ => { + (_) => { controller[pulling_] = false; if (controller[pullAgain_]) { controller[pullAgain_] = false; readableStreamDefaultControllerCallPullIfNeeded(controller); } }, - error => { + (error) => { readableStreamDefaultControllerError(controller, error); } ); @@ -768,13 +768,13 @@ export function setUpReadableByteStreamController( // Let startResult be the result of performing startAlgorithm. const startResult = startAlgorithm(); Promise.resolve(startResult).then( - _ => { + (_) => { controller[started_] = true; // Assert: controller.[[pulling]] is false. // Assert: controller.[[pullAgain]] is false. readableByteStreamControllerCallPullIfNeeded(controller); }, - error => { + (error) => { readableByteStreamControllerError(controller, error); } ); @@ -811,14 +811,14 @@ export function readableByteStreamControllerCallPullIfNeeded( // Assert: controller.[[pullAgain]] is false. controller[pulling_] = true; controller[pullAlgorithm_](controller).then( - _ => { + (_) => { controller[pulling_] = false; if (controller[pullAgain_]) { controller[pullAgain_] = false; readableByteStreamControllerCallPullIfNeeded(controller); } }, - error => { + (error) => { readableByteStreamControllerError(controller, error); } ); @@ -1122,7 +1122,7 @@ export function readableByteStreamControllerPullInto( bytesFilled: 0, elementSize, ctor, - readerType: "byob" + readerType: "byob", }; if (controller[pendingPullIntos_].length > 0) { diff --git a/cli/js/web/streams/readable-stream.ts b/cli/js/web/streams/readable-stream.ts index e062c278e..50753260d 100644 --- a/cli/js/web/streams/readable-stream.ts +++ b/cli/js/web/streams/readable-stream.ts @@ -11,18 +11,18 @@ import { QueuingStrategy, QueuingStrategySizeCallback, UnderlyingSource, - UnderlyingByteSource + UnderlyingByteSource, } from "../dom_types.ts"; import { ReadableStreamDefaultController, - setUpReadableStreamDefaultControllerFromUnderlyingSource + setUpReadableStreamDefaultControllerFromUnderlyingSource, } from "./readable-stream-default-controller.ts"; import { ReadableStreamDefaultReader } from "./readable-stream-default-reader.ts"; import { ReadableByteStreamController, - setUpReadableByteStreamControllerFromUnderlyingSource + setUpReadableByteStreamControllerFromUnderlyingSource, } from "./readable-byte-stream-controller.ts"; import { SDReadableStreamBYOBReader } from "./readable-stream-byob-reader.ts"; @@ -123,7 +123,7 @@ export class SDReadableStream<OutputType> return rs.readableStreamCancel(this, reason); } - tee(): Array<SDReadableStream<OutputType>> { + tee(): [SDReadableStream<OutputType>, SDReadableStream<OutputType>] { return readableStreamTee(this, false); } @@ -280,7 +280,9 @@ export function readableStreamTee<OutputType>( let branch2: SDReadableStream<OutputType>; let cancelResolve: (reason: shared.ErrorResult) => void; - const cancelPromise = new Promise<void>(resolve => (cancelResolve = resolve)); + const cancelPromise = new Promise<void>( + (resolve) => (cancelResolve = resolve) + ); const pullAlgorithm = (): Promise<void> => { return rs @@ -362,7 +364,7 @@ export function readableStreamTee<OutputType>( cancel2Algorithm ); - reader[rs.closedPromise_].promise.catch(error => { + reader[rs.closedPromise_].promise.catch((error) => { if (!closedOrErrored) { rs.readableStreamDefaultControllerError( branch1![ diff --git a/cli/js/web/streams/shared-internals.ts b/cli/js/web/streams/shared-internals.ts index 3d802b083..7b0de2274 100644 --- a/cli/js/web/streams/shared-internals.ts +++ b/cli/js/web/streams/shared-internals.ts @@ -223,7 +223,7 @@ export function createAlgorithmFromUnderlyingMethod< if (typeof method !== "function") { throw new TypeError(`Field "${methodName}" is not a function.`); } - return function(...fnArgs: any[]): any { + return function (...fnArgs: any[]): any { return promiseCall(method, obj, fnArgs.concat(extraArgs)); }; } @@ -252,7 +252,7 @@ export function makeSizeAlgorithmFromSizeFunction<T>( if (typeof sizeFn !== "function" && typeof sizeFn !== "undefined") { throw new TypeError("size function must be undefined or a function"); } - return function(chunk: T): number { + return function (chunk: T): number { if (typeof sizeFn === "function") { return sizeFn(chunk); } @@ -265,7 +265,7 @@ export function makeSizeAlgorithmFromSizeFunction<T>( export const enum ControlledPromiseState { Pending, Resolved, - Rejected + Rejected, } export interface ControlledPromise<V> { @@ -277,14 +277,14 @@ export interface ControlledPromise<V> { export function createControlledPromise<V>(): ControlledPromise<V> { const conProm = { - state: ControlledPromiseState.Pending + state: ControlledPromiseState.Pending, } as ControlledPromise<V>; - conProm.promise = new Promise<V>(function(resolve, reject) { - conProm.resolve = function(v?: V): void { + conProm.promise = new Promise<V>(function (resolve, reject) { + conProm.resolve = function (v?: V): void { conProm.state = ControlledPromiseState.Resolved; resolve(v); }; - conProm.reject = function(e?: ErrorResult): void { + conProm.reject = function (e?: ErrorResult): void { conProm.state = ControlledPromiseState.Rejected; reject(e); }; diff --git a/cli/js/web/text_encoding.ts b/cli/js/web/text_encoding.ts index 5f04972aa..6fd498e59 100644 --- a/cli/js/web/text_encoding.ts +++ b/cli/js/web/text_encoding.ts @@ -149,8 +149,8 @@ interface Encoder { } class SingleByteDecoder implements Decoder { - private _index: number[]; - private _fatal: boolean; + #index: number[]; + #fatal: boolean; constructor( index: number[], @@ -159,20 +159,20 @@ class SingleByteDecoder implements Decoder { if (ignoreBOM) { throw new TypeError("Ignoring the BOM is available only with utf-8."); } - this._fatal = fatal; - this._index = index; + this.#fatal = fatal; + this.#index = index; } - handler(stream: Stream, byte: number): number { + handler(_stream: Stream, byte: number): number { if (byte === END_OF_STREAM) { return FINISHED; } if (isASCIIByte(byte)) { return byte; } - const codePoint = this._index[byte - 0x80]; + const codePoint = this.#index[byte - 0x80]; if (codePoint == null) { - return decoderError(this._fatal); + return decoderError(this.#fatal); } return codePoint; @@ -199,9 +199,9 @@ const encodingMap: { [key: string]: string[] } = { "latin1", "us-ascii", "windows-1252", - "x-cp1252" + "x-cp1252", ], - "utf-8": ["unicode-1-1-utf-8", "utf-8", "utf8"] + "utf-8": ["unicode-1-1-utf-8", "utf-8", "utf8"], }; // We convert these into a Map where every label resolves to its canonical // encoding type. @@ -221,13 +221,134 @@ const decoders = new Map<string, (options: DecoderOptions) => Decoder>(); const encodingIndexes = new Map<string, number[]>(); // prettier-ignore encodingIndexes.set("windows-1252", [ - 8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144, - 8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161, - 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, - 181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218, - 219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237, - 238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 + 8364, + 129, + 8218, + 402, + 8222, + 8230, + 8224, + 8225, + 710, + 8240, + 352, + 8249, + 338, + 141, + 381, + 143, + 144, + 8216, + 8217, + 8220, + 8221, + 8226, + 8211, + 8212, + 732, + 8482, + 353, + 8250, + 339, + 157, + 382, + 376, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 ]); for (const [key, index] of encodingIndexes) { decoders.set( @@ -247,37 +368,37 @@ function codePointsToString(codePoints: number[]): string { } class Stream { - private _tokens: number[]; + #tokens: number[]; constructor(tokens: number[] | Uint8Array) { - this._tokens = [].slice.call(tokens); - this._tokens.reverse(); + this.#tokens = [...tokens]; + this.#tokens.reverse(); } endOfStream(): boolean { - return !this._tokens.length; + return !this.#tokens.length; } read(): number { - return !this._tokens.length ? END_OF_STREAM : this._tokens.pop()!; + return !this.#tokens.length ? END_OF_STREAM : this.#tokens.pop()!; } prepend(token: number | number[]): void { if (Array.isArray(token)) { while (token.length) { - this._tokens.push(token.pop()!); + this.#tokens.push(token.pop()!); } } else { - this._tokens.push(token); + this.#tokens.push(token); } } push(token: number | number[]): void { if (Array.isArray(token)) { while (token.length) { - this._tokens.unshift(token.shift()!); + this.#tokens.unshift(token.shift()!); } } else { - this._tokens.unshift(token); + this.#tokens.unshift(token); } } } @@ -299,10 +420,10 @@ function isEitherArrayBuffer(x: any): x is EitherArrayBuffer { } export class TextDecoder { - private _encoding: string; + #encoding: string; get encoding(): string { - return this._encoding; + return this.#encoding; } readonly fatal: boolean = false; readonly ignoreBOM: boolean = false; @@ -314,9 +435,7 @@ export class TextDecoder { if (options.fatal) { this.fatal = true; } - label = String(label) - .trim() - .toLowerCase(); + label = String(label).trim().toLowerCase(); const encoding = encodings.get(label); if (!encoding) { throw new RangeError( @@ -326,7 +445,7 @@ export class TextDecoder { if (!decoders.has(encoding) && encoding !== "utf-8") { throw new TypeError(`Internal decoder ('${encoding}') not found.`); } - this._encoding = encoding; + this.#encoding = encoding; } decode( @@ -354,7 +473,7 @@ export class TextDecoder { // For simple utf-8 decoding "Deno.core.decode" can be used for performance if ( - this._encoding === "utf-8" && + this.#encoding === "utf-8" && this.fatal === false && this.ignoreBOM === false ) { @@ -363,13 +482,13 @@ export class TextDecoder { // For performance reasons we utilise a highly optimised decoder instead of // the general decoder. - if (this._encoding === "utf-8") { + if (this.#encoding === "utf-8") { return decodeUtf8(bytes, this.fatal, this.ignoreBOM); } - const decoder = decoders.get(this._encoding)!({ + const decoder = decoders.get(this.#encoding)!({ fatal: this.fatal, - ignoreBOM: this.ignoreBOM + ignoreBOM: this.ignoreBOM, }); const inputStream = new Stream(bytes); const output: number[] = []; @@ -455,7 +574,7 @@ export class TextEncoder { return { read, - written + written, }; } get [Symbol.toStringTag](): string { diff --git a/cli/js/web/timers.ts b/cli/js/web/timers.ts index 9a957f3fe..ff18543fa 100644 --- a/cli/js/web/timers.ts +++ b/cli/js/web/timers.ts @@ -223,7 +223,7 @@ function setTimer( delay, due: now + delay, repeat, - scheduled: false + scheduled: false, }; // Register the timer's existence in the id-to-timer map. idMap.set(timer.id, timer); diff --git a/cli/js/web/url.ts b/cli/js/web/url.ts index 6ef6b367c..2b6a0d341 100644 --- a/cli/js/web/url.ts +++ b/cli/js/web/url.ts @@ -1,8 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import * as urlSearchParams from "./url_search_params.ts"; +import { customInspect } from "./console.ts"; import * as domTypes from "./dom_types.ts"; +import { urls, URLSearchParams } from "./url_search_params.ts"; import { getRandomValues } from "../ops/get_random_values.ts"; -import { customInspect } from "./console.ts"; interface URLParts { protocol: string; @@ -24,7 +24,7 @@ const patterns = { authentication: "(?:([^:]*)(?::([^@]*))?@)", hostname: "([^:]+)", - port: "(?::(\\d+))" + port: "(?::(\\d+))", }; const urlRegExp = new RegExp( @@ -35,10 +35,10 @@ const authorityRegExp = new RegExp( `^${patterns.authentication}?${patterns.hostname}${patterns.port}?$` ); -const searchParamsMethods: Array<keyof urlSearchParams.URLSearchParams> = [ +const searchParamsMethods: Array<keyof URLSearchParams> = [ "append", "delete", - "set" + "set", ]; function parse(url: string): URLParts | undefined { @@ -57,7 +57,7 @@ function parse(url: string): URLParts | undefined { port: authorityMatch[4] || "", path: urlMatch[3] || "", query: urlMatch[4] || "", - hash: urlMatch[5] || "" + hash: urlMatch[5] || "", }; } } @@ -136,9 +136,11 @@ function resolvePathFromBase(path: string, basePath: string): string { return normalizePath(prefix + suffix); } -export class URL { - private _parts: URLParts; - private _searchParams!: urlSearchParams.URLSearchParams; +/** @internal */ +export const parts = new WeakMap<URL, URLParts>(); + +export class URL implements domTypes.URL { + #searchParams!: URLSearchParams; [customInspect](): string { const keys = [ @@ -152,7 +154,7 @@ export class URL { "port", "pathname", "hash", - "search" + "search", ]; const objectString = keys .map((key: string) => `${key}: "${this[key as keyof this] || ""}"`) @@ -160,8 +162,8 @@ export class URL { return `URL { ${objectString} }`; } - private _updateSearchParams(): void { - const searchParams = new urlSearchParams.URLSearchParams(this.search); + #updateSearchParams = (): void => { + const searchParams = new URLSearchParams(this.search); for (const methodName of searchParamsMethods) { /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -172,27 +174,25 @@ export class URL { }; /* eslint-enable */ } - this._searchParams = searchParams; + this.#searchParams = searchParams; - // convert to `any` that has avoided the private limit - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this._searchParams as any).url = this; - } + urls.set(searchParams, this); + }; get hash(): string { - return this._parts.hash; + return parts.get(this)!.hash; } set hash(value: string) { value = unescape(String(value)); if (!value) { - this._parts.hash = ""; + parts.get(this)!.hash = ""; } else { if (value.charAt(0) !== "#") { value = `#${value}`; } // hashes can contain % and # unescaped - this._parts.hash = escape(value) + parts.get(this)!.hash = escape(value) .replace(/%25/g, "%") .replace(/%23/g, "#"); } @@ -205,17 +205,17 @@ export class URL { set host(value: string) { value = String(value); const url = new URL(`http://${value}`); - this._parts.hostname = url.hostname; - this._parts.port = url.port; + parts.get(this)!.hostname = url.hostname; + parts.get(this)!.port = url.port; } get hostname(): string { - return this._parts.hostname; + return parts.get(this)!.hostname; } set hostname(value: string) { value = String(value); - this._parts.hostname = encodeURIComponent(value); + parts.get(this)!.hostname = encodeURIComponent(value); } get href(): string { @@ -234,8 +234,8 @@ export class URL { value = String(value); if (value !== this.href) { const url = new URL(value); - this._parts = { ...url._parts }; - this._updateSearchParams(); + parts.set(this, { ...parts.get(url)! }); + this.#updateSearchParams(); } } @@ -247,16 +247,16 @@ export class URL { } get password(): string { - return this._parts.password; + return parts.get(this)!.password; } set password(value: string) { value = String(value); - this._parts.password = encodeURIComponent(value); + parts.get(this)!.password = encodeURIComponent(value); } get pathname(): string { - return this._parts.path ? this._parts.path : "/"; + return parts.get(this)?.path || "/"; } set pathname(value: string) { @@ -265,22 +265,22 @@ export class URL { value = `/${value}`; } // paths can contain % unescaped - this._parts.path = escape(value).replace(/%25/g, "%"); + parts.get(this)!.path = escape(value).replace(/%25/g, "%"); } get port(): string { - return this._parts.port; + return parts.get(this)!.port; } set port(value: string) { const port = parseInt(String(value), 10); - this._parts.port = isNaN(port) + parts.get(this)!.port = isNaN(port) ? "" : Math.max(0, port % 2 ** 16).toString(); } get protocol(): string { - return `${this._parts.protocol}:`; + return `${parts.get(this)!.protocol}:`; } set protocol(value: string) { @@ -289,16 +289,17 @@ export class URL { if (value.charAt(value.length - 1) === ":") { value = value.slice(0, -1); } - this._parts.protocol = encodeURIComponent(value); + parts.get(this)!.protocol = encodeURIComponent(value); } } get search(): string { - if (this._parts.query === null || this._parts.query === "") { + const query = parts.get(this)!.query; + if (query === null || query === "") { return ""; } - return this._parts.query; + return query; } set search(value: string) { @@ -313,27 +314,27 @@ export class URL { query = value; } - this._parts.query = query; - this._updateSearchParams(); + parts.get(this)!.query = query; + this.#updateSearchParams(); } get username(): string { - return this._parts.username; + return parts.get(this)!.username; } set username(value: string) { value = String(value); - this._parts.username = encodeURIComponent(value); + parts.get(this)!.username = encodeURIComponent(value); } - get searchParams(): urlSearchParams.URLSearchParams { - return this._searchParams; + get searchParams(): URLSearchParams { + return this.#searchParams; } constructor(url: string, base?: string | URL) { let baseParts: URLParts | undefined; if (base) { - baseParts = typeof base === "string" ? parse(base) : base._parts; + baseParts = typeof base === "string" ? parse(base) : parts.get(base); if (!baseParts || baseParts.protocol == "") { throw new TypeError("Invalid base URL."); } @@ -345,9 +346,9 @@ export class URL { } if (urlParts.protocol) { - this._parts = urlParts; + parts.set(this, urlParts); } else if (baseParts) { - this._parts = { + parts.set(this, { protocol: baseParts.protocol, username: baseParts.username, password: baseParts.password, @@ -355,12 +356,12 @@ export class URL { port: baseParts.port, path: resolvePathFromBase(urlParts.path, baseParts.path || "/"), query: urlParts.query, - hash: urlParts.hash - }; + hash: urlParts.hash, + }); } else { throw new TypeError("URL requires a base URL."); } - this._updateSearchParams(); + this.#updateSearchParams(); } toString(): string { diff --git a/cli/js/web/url_search_params.ts b/cli/js/web/url_search_params.ts index 8f60f2918..aad59bb8c 100644 --- a/cli/js/web/url_search_params.ts +++ b/cli/js/web/url_search_params.ts @@ -1,33 +1,61 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { URL } from "./url.ts"; -import { requiredArguments } from "./util.ts"; - -// Returns whether o is iterable. -// @internal -export function isIterable<T, P extends keyof T, K extends T[P]>( - o: T -): o is T & Iterable<[P, K]> { - // checks for null and undefined - if (o == null) { - return false; - } - return ( - typeof ((o as unknown) as Iterable<[P, K]>)[Symbol.iterator] === "function" - ); +import * as domTypes from "./dom_types.ts"; +import { URL, parts } from "./url.ts"; +import { isIterable, requiredArguments } from "./util.ts"; + +/** @internal */ +export const urls = new WeakMap<URLSearchParams, URL | null>(); + +function handleStringInitialization( + searchParams: URLSearchParams, + init: string +): void { + // Overload: USVString + // If init is a string and starts with U+003F (?), + // remove the first code point from init. + if (init.charCodeAt(0) === 0x003f) { + init = init.slice(1); + } + + for (const pair of init.split("&")) { + // Empty params are ignored + if (pair.length === 0) { + continue; + } + const position = pair.indexOf("="); + const name = pair.slice(0, position === -1 ? pair.length : position); + const value = pair.slice(name.length + 1); + searchParams.append(decodeURIComponent(name), decodeURIComponent(value)); + } +} + +function handleArrayInitialization( + searchParams: URLSearchParams, + init: string[][] | Iterable<[string, string]> +): void { + // Overload: sequence<sequence<USVString>> + for (const tuple of init) { + // If pair does not contain exactly two items, then throw a TypeError. + if (tuple.length !== 2) { + throw new TypeError( + "URLSearchParams.constructor tuple array argument must only contain pair elements" + ); + } + searchParams.append(tuple[0], tuple[1]); + } } -export class URLSearchParams { - private params: Array<[string, string]> = []; - private url: URL | null = null; +export class URLSearchParams implements domTypes.URLSearchParams { + #params: Array<[string, string]> = []; constructor(init: string | string[][] | Record<string, string> = "") { if (typeof init === "string") { - this._handleStringInitialization(init); + handleStringInitialization(this, init); return; } if (Array.isArray(init) || isIterable(init)) { - this._handleArrayInitialization(init); + handleArrayInitialization(this, init); return; } @@ -36,7 +64,7 @@ export class URLSearchParams { } if (init instanceof URLSearchParams) { - this.params = init.params; + this.#params = [...init.#params]; return; } @@ -44,10 +72,13 @@ export class URLSearchParams { for (const key of Object.keys(init)) { this.append(key, init[key]); } + + urls.set(this, null); } - private updateSteps(): void { - if (this.url === null) { + #updateSteps = (): void => { + const url = urls.get(this); + if (url == null) { return; } @@ -56,35 +87,34 @@ export class URLSearchParams { query = null; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this.url as any)._parts.query = query; - } + parts.get(url)!.query = query; + }; append(name: string, value: string): void { requiredArguments("URLSearchParams.append", arguments.length, 2); - this.params.push([String(name), String(value)]); - this.updateSteps(); + this.#params.push([String(name), String(value)]); + this.#updateSteps(); } delete(name: string): void { requiredArguments("URLSearchParams.delete", arguments.length, 1); name = String(name); let i = 0; - while (i < this.params.length) { - if (this.params[i][0] === name) { - this.params.splice(i, 1); + while (i < this.#params.length) { + if (this.#params[i][0] === name) { + this.#params.splice(i, 1); } else { i++; } } - this.updateSteps(); + this.#updateSteps(); } getAll(name: string): string[] { requiredArguments("URLSearchParams.getAll", arguments.length, 1); name = String(name); const values = []; - for (const entry of this.params) { + for (const entry of this.#params) { if (entry[0] === name) { values.push(entry[1]); } @@ -96,7 +126,7 @@ export class URLSearchParams { get(name: string): string | null { requiredArguments("URLSearchParams.get", arguments.length, 1); name = String(name); - for (const entry of this.params) { + for (const entry of this.#params) { if (entry[0] === name) { return entry[1]; } @@ -108,7 +138,7 @@ export class URLSearchParams { has(name: string): boolean { requiredArguments("URLSearchParams.has", arguments.length, 1); name = String(name); - return this.params.some((entry): boolean => entry[0] === name); + return this.#params.some((entry) => entry[0] === name); } set(name: string, value: string): void { @@ -121,14 +151,14 @@ export class URLSearchParams { value = String(value); let found = false; let i = 0; - while (i < this.params.length) { - if (this.params[i][0] === name) { + while (i < this.#params.length) { + if (this.#params[i][0] === name) { if (!found) { - this.params[i][1] = value; + this.#params[i][1] = value; found = true; i++; } else { - this.params.splice(i, 1); + this.#params.splice(i, 1); } } else { i++; @@ -141,14 +171,12 @@ export class URLSearchParams { this.append(name, value); } - this.updateSteps(); + this.#updateSteps(); } sort(): void { - this.params = this.params.sort((a, b): number => - a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1 - ); - this.updateSteps(); + this.#params.sort((a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1)); + this.#updateSteps(); } forEach( @@ -168,66 +196,31 @@ export class URLSearchParams { } *keys(): IterableIterator<string> { - for (const entry of this.params) { - yield entry[0]; + for (const [key] of this.#params) { + yield key; } } *values(): IterableIterator<string> { - for (const entry of this.params) { - yield entry[1]; + for (const [, value] of this.#params) { + yield value; } } *entries(): IterableIterator<[string, string]> { - yield* this.params; + yield* this.#params; } *[Symbol.iterator](): IterableIterator<[string, string]> { - yield* this.params; + yield* this.#params; } toString(): string { - return this.params + return this.#params .map( - (tuple): string => + (tuple) => `${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}` ) .join("&"); } - - private _handleStringInitialization(init: string): void { - // Overload: USVString - // If init is a string and starts with U+003F (?), - // remove the first code point from init. - if (init.charCodeAt(0) === 0x003f) { - init = init.slice(1); - } - - for (const pair of init.split("&")) { - // Empty params are ignored - if (pair.length === 0) { - continue; - } - const position = pair.indexOf("="); - const name = pair.slice(0, position === -1 ? pair.length : position); - const value = pair.slice(name.length + 1); - this.append(decodeURIComponent(name), decodeURIComponent(value)); - } - } - - private _handleArrayInitialization( - init: string[][] | Iterable<[string, string]> - ): void { - // Overload: sequence<sequence<USVString>> - for (const tuple of init) { - // If pair does not contain exactly two items, then throw a TypeError. - if (tuple.length !== 2) { - throw new TypeError( - "URLSearchParams.constructor tuple array argument must only contain pair elements" - ); - } - this.append(tuple[0], tuple[1]); - } - } } diff --git a/cli/js/web/util.ts b/cli/js/web/util.ts index 8d248ce1f..19a30a675 100644 --- a/cli/js/web/util.ts +++ b/cli/js/web/util.ts @@ -31,7 +31,7 @@ export function immutableDefine( Object.defineProperty(o, p, { value, configurable: false, - writable: false + writable: false, }); } @@ -53,3 +53,18 @@ export function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean { } return Object.prototype.hasOwnProperty.call(obj, v); } + +/** Returns whether o is iterable. + * + * @internal */ +export function isIterable<T, P extends keyof T, K extends T[P]>( + o: T +): o is T & Iterable<[P, K]> { + // checks for null and undefined + if (o == null) { + return false; + } + return ( + typeof ((o as unknown) as Iterable<[P, K]>)[Symbol.iterator] === "function" + ); +} diff --git a/cli/js/web/workers.ts b/cli/js/web/workers.ts index 256090d57..834d0f297 100644 --- a/cli/js/web/workers.ts +++ b/cli/js/web/workers.ts @@ -4,13 +4,12 @@ import { createWorker, hostTerminateWorker, hostPostMessage, - hostGetMessage + hostGetMessage, } from "../ops/worker_host.ts"; import { log } from "../util.ts"; import { TextDecoder, TextEncoder } from "./text_encoding.ts"; /* import { blobURLMap } from "./web/url.ts"; -import { blobBytesWeakMap } from "./web/blob.ts"; */ import { Event } from "./event.ts"; import { EventTarget } from "./event_target.ts"; @@ -48,13 +47,13 @@ export interface WorkerOptions { } export class WorkerImpl extends EventTarget implements Worker { - private readonly id: number; - private isClosing = false; + readonly #id: number; + #name: string; + #terminated = false; + public onerror?: (e: any) => void; public onmessage?: (data: any) => void; public onmessageerror?: () => void; - private name: string; - private terminated = false; constructor(specifier: string, options?: WorkerOptions) { super(); @@ -66,7 +65,7 @@ export class WorkerImpl extends EventTarget implements Worker { ); } - this.name = name; + this.#name = name; const hasSourceCode = false; const sourceCode = decoder.decode(new Uint8Array()); @@ -92,11 +91,11 @@ export class WorkerImpl extends EventTarget implements Worker { sourceCode, options?.name ); - this.id = id; + this.#id = id; this.poll(); } - private handleError(e: any): boolean { + #handleError = (e: any): boolean => { // TODO: this is being handled in a type unsafe way, it should be type safe // eslint-disable-next-line @typescript-eslint/no-explicit-any const event = new Event("error", { cancelable: true }) as any; @@ -115,14 +114,14 @@ export class WorkerImpl extends EventTarget implements Worker { } return handled; - } + }; async poll(): Promise<void> { - while (!this.terminated) { - const event = await hostGetMessage(this.id); + while (!this.#terminated) { + const event = await hostGetMessage(this.#id); // If terminate was called then we ignore all messages - if (this.terminated) { + if (this.#terminated) { return; } @@ -137,15 +136,15 @@ export class WorkerImpl extends EventTarget implements Worker { } if (type === "error") { - if (!this.handleError(event.error)) { + if (!this.#handleError(event.error)) { throw Error(event.error.message); } continue; } if (type === "close") { - log(`Host got "close" message from worker: ${this.name}`); - this.terminated = true; + log(`Host got "close" message from worker: ${this.#name}`); + this.#terminated = true; return; } @@ -154,17 +153,17 @@ export class WorkerImpl extends EventTarget implements Worker { } postMessage(data: any): void { - if (this.terminated) { + if (this.#terminated) { return; } - hostPostMessage(this.id, encodeMessage(data)); + hostPostMessage(this.#id, encodeMessage(data)); } terminate(): void { - if (!this.terminated) { - this.terminated = true; - hostTerminateWorker(this.id); + if (!this.#terminated) { + this.#terminated = true; + hostTerminateWorker(this.#id); } } } |
