diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-02-14 17:38:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 17:38:45 +0100 |
commit | d47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch) | |
tree | 6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/_utils.ts | |
parent | 1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff) |
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is
transpiled and snapshotted during the build process.
During the first pass a minimal amount of work was done to create the
snapshot, a lot of code in "ext/node" depends on presence of "Deno"
global. This code will be gradually fixed in the follow up PRs to migrate
it to import relevant APIs from "internal:" modules.
Currently the code from snapshot is not used in any way, and all
Node/npm compatibility still uses code from
"https://deno.land/std/node" (or from the location specified by
"DENO_NODE_COMPAT_URL"). This will also be handled in a follow
up PRs.
---------
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/_utils.ts')
-rw-r--r-- | ext/node/polyfills/_utils.ts | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/ext/node/polyfills/_utils.ts b/ext/node/polyfills/_utils.ts new file mode 100644 index 000000000..85398ead9 --- /dev/null +++ b/ext/node/polyfills/_utils.ts @@ -0,0 +1,210 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { errorMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; + +export type BinaryEncodings = "binary"; + +export type TextEncodings = + | "ascii" + | "utf8" + | "utf-8" + | "utf16le" + | "ucs2" + | "ucs-2" + | "base64" + | "latin1" + | "hex"; + +export type Encodings = BinaryEncodings | TextEncodings; + +export function notImplemented(msg: string): never { + const message = msg ? `Not implemented: ${msg}` : "Not implemented"; + throw new Error(message); +} + +export function warnNotImplemented(msg?: string) { + const message = msg + ? `Warning: Not implemented: ${msg}` + : "Warning: Not implemented"; + console.warn(message); +} + +export type _TextDecoder = typeof TextDecoder.prototype; +export const _TextDecoder = TextDecoder; + +export type _TextEncoder = typeof TextEncoder.prototype; +export const _TextEncoder = TextEncoder; + +// API helpers + +export type MaybeNull<T> = T | null; +export type MaybeDefined<T> = T | undefined; +export type MaybeEmpty<T> = T | null | undefined; + +export function intoCallbackAPI<T>( + // deno-lint-ignore no-explicit-any + func: (...args: any[]) => Promise<T>, + cb: MaybeEmpty<(err: MaybeNull<Error>, value?: MaybeEmpty<T>) => void>, + // deno-lint-ignore no-explicit-any + ...args: any[] +) { + func(...args).then( + (value) => cb && cb(null, value), + (err) => cb && cb(err), + ); +} + +export function intoCallbackAPIWithIntercept<T1, T2>( + // deno-lint-ignore no-explicit-any + func: (...args: any[]) => Promise<T1>, + interceptor: (v: T1) => T2, + cb: MaybeEmpty<(err: MaybeNull<Error>, value?: MaybeEmpty<T2>) => void>, + // deno-lint-ignore no-explicit-any + ...args: any[] +) { + func(...args).then( + (value) => cb && cb(null, interceptor(value)), + (err) => cb && cb(err), + ); +} + +export function spliceOne(list: string[], index: number) { + for (; index + 1 < list.length; index++) list[index] = list[index + 1]; + list.pop(); +} + +// Taken from: https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L125 +// Return undefined if there is no match. +// Move the "slow cases" to a separate function to make sure this function gets +// inlined properly. That prioritizes the common case. +export function normalizeEncoding( + enc: string | null, +): TextEncodings | undefined { + if (enc == null || enc === "utf8" || enc === "utf-8") return "utf8"; + return slowCases(enc); +} + +// https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L130 +function slowCases(enc: string): TextEncodings | undefined { + switch (enc.length) { + case 4: + if (enc === "UTF8") return "utf8"; + if (enc === "ucs2" || enc === "UCS2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf8") return "utf8"; + if (enc === "ucs2") return "utf16le"; + break; + case 3: + if (enc === "hex" || enc === "HEX" || `${enc}`.toLowerCase() === "hex") { + return "hex"; + } + break; + case 5: + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + if (enc === "UTF-8") return "utf8"; + if (enc === "ASCII") return "ascii"; + if (enc === "UCS-2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf-8") return "utf8"; + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + break; + case 6: + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + if (enc === "BASE64") return "base64"; + if (enc === "LATIN1" || enc === "BINARY") return "latin1"; + enc = `${enc}`.toLowerCase(); + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + break; + case 7: + if ( + enc === "utf16le" || + enc === "UTF16LE" || + `${enc}`.toLowerCase() === "utf16le" + ) { + return "utf16le"; + } + break; + case 8: + if ( + enc === "utf-16le" || + enc === "UTF-16LE" || + `${enc}`.toLowerCase() === "utf-16le" + ) { + return "utf16le"; + } + break; + default: + if (enc === "") return "utf8"; + } +} + +export function validateIntegerRange( + value: number, + name: string, + min = -2147483648, + max = 2147483647, +) { + // 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}`, + ); + } +} + +type OptionalSpread<T> = T extends undefined ? [] + : [T]; + +export function once<T = undefined>( + callback: (...args: OptionalSpread<T>) => void, +) { + let called = false; + return function (this: unknown, ...args: OptionalSpread<T>) { + if (called) return; + called = true; + callback.apply(this, args); + }; +} + +export function makeMethodsEnumerable(klass: { new (): unknown }) { + const proto = klass.prototype; + for (const key of Object.getOwnPropertyNames(proto)) { + const value = proto[key]; + if (typeof value === "function") { + const desc = Reflect.getOwnPropertyDescriptor(proto, key); + if (desc) { + desc.enumerable = true; + Object.defineProperty(proto, key, desc); + } + } + } +} + +const NumberIsSafeInteger = Number.isSafeInteger; + +/** + * Returns a system error name from an error code number. + * @param code error code number + */ +export function getSystemErrorName(code: number): string | undefined { + if (typeof code !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE("err", "number", code); + } + if (code >= 0 || !NumberIsSafeInteger(code)) { + throw new codes.ERR_OUT_OF_RANGE("err", "a negative integer", code); + } + return errorMap.get(code)?.[0]; +} |