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/_util/std_asserts.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/_util/std_asserts.ts')
-rw-r--r-- | ext/node/polyfills/_util/std_asserts.ts | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/ext/node/polyfills/_util/std_asserts.ts b/ext/node/polyfills/_util/std_asserts.ts new file mode 100644 index 000000000..8c4c80078 --- /dev/null +++ b/ext/node/polyfills/_util/std_asserts.ts @@ -0,0 +1,293 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// vendored from std/testing/asserts.ts + +import { red } from "internal:deno_node/polyfills/_util/std_fmt_colors.ts"; +import { + buildMessage, + diff, + diffstr, +} from "internal:deno_node/polyfills/_util/std_testing_diff.ts"; + +/** Converts the input into a string. Objects, Sets and Maps are sorted so as to + * make tests less flaky */ +export function format(v: unknown): string { + // deno-lint-ignore no-explicit-any + const { Deno } = globalThis as any; + return typeof Deno?.inspect === "function" + ? Deno.inspect(v, { + depth: Infinity, + sorted: true, + trailingComma: true, + compact: false, + iterableLimit: Infinity, + // getters should be true in assertEquals. + getters: true, + }) + : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; +} + +const CAN_NOT_DISPLAY = "[Cannot display]"; + +export class AssertionError extends Error { + override name = "AssertionError"; + constructor(message: string) { + super(message); + } +} + +function isKeyedCollection(x: unknown): x is Set<unknown> { + return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>)); +} + +/** Deep equality comparison used in assertions */ +export function equal(c: unknown, d: unknown): boolean { + const seen = new Map(); + return (function compare(a: unknown, b: unknown): boolean { + // Have to render RegExp & Date for string comparison + // unless it's mistreated as object + if ( + a && + b && + ((a instanceof RegExp && b instanceof RegExp) || + (a instanceof URL && b instanceof URL)) + ) { + return String(a) === String(b); + } + if (a instanceof Date && b instanceof Date) { + const aTime = a.getTime(); + const bTime = b.getTime(); + // Check for NaN equality manually since NaN is not + // equal to itself. + if (Number.isNaN(aTime) && Number.isNaN(bTime)) { + return true; + } + return aTime === bTime; + } + if (typeof a === "number" && typeof b === "number") { + return Number.isNaN(a) && Number.isNaN(b) || a === b; + } + if (Object.is(a, b)) { + return true; + } + if (a && typeof a === "object" && b && typeof b === "object") { + if (a && b && !constructorsEqual(a, b)) { + return false; + } + if (a instanceof WeakMap || b instanceof WeakMap) { + if (!(a instanceof WeakMap && b instanceof WeakMap)) return false; + throw new TypeError("cannot compare WeakMap instances"); + } + if (a instanceof WeakSet || b instanceof WeakSet) { + if (!(a instanceof WeakSet && b instanceof WeakSet)) return false; + throw new TypeError("cannot compare WeakSet instances"); + } + if (seen.get(a) === b) { + return true; + } + if (Object.keys(a || {}).length !== Object.keys(b || {}).length) { + return false; + } + seen.set(a, b); + if (isKeyedCollection(a) && isKeyedCollection(b)) { + if (a.size !== b.size) { + return false; + } + + let unmatchedEntries = a.size; + + for (const [aKey, aValue] of a.entries()) { + for (const [bKey, bValue] of b.entries()) { + /* Given that Map keys can be references, we need + * to ensure that they are also deeply equal */ + if ( + (aKey === aValue && bKey === bValue && compare(aKey, bKey)) || + (compare(aKey, bKey) && compare(aValue, bValue)) + ) { + unmatchedEntries--; + break; + } + } + } + + return unmatchedEntries === 0; + } + const merged = { ...a, ...b }; + for ( + const key of [ + ...Object.getOwnPropertyNames(merged), + ...Object.getOwnPropertySymbols(merged), + ] + ) { + type Key = keyof typeof merged; + if (!compare(a && a[key as Key], b && b[key as Key])) { + return false; + } + if (((key in a) && (!(key in b))) || ((key in b) && (!(key in a)))) { + return false; + } + } + if (a instanceof WeakRef || b instanceof WeakRef) { + if (!(a instanceof WeakRef && b instanceof WeakRef)) return false; + return compare(a.deref(), b.deref()); + } + return true; + } + return false; + })(c, d); +} + +// deno-lint-ignore ban-types +function constructorsEqual(a: object, b: object) { + return a.constructor === b.constructor || + a.constructor === Object && !b.constructor || + !a.constructor && b.constructor === Object; +} + +/** Make an assertion, error will be thrown if `expr` does not have truthy value. */ +export function assert(expr: unknown, msg = ""): asserts expr { + if (!expr) { + throw new AssertionError(msg); + } +} + +/** Make an assertion that `actual` and `expected` are equal, deeply. If not + * deeply equal, then throw. */ +export function assertEquals<T>(actual: T, expected: T, msg?: string) { + if (equal(actual, expected)) { + return; + } + let message = ""; + const actualString = format(actual); + const expectedString = format(expected); + try { + const stringDiff = (typeof actual === "string") && + (typeof expected === "string"); + const diffResult = stringDiff + ? diffstr(actual as string, expected as string) + : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n"); + message = `Values are not equal:\n${diffMsg}`; + } catch { + message = `\n${red(red(CAN_NOT_DISPLAY))} + \n\n`; + } + if (msg) { + message = msg; + } + throw new AssertionError(message); +} + +/** Make an assertion that `actual` and `expected` are not equal, deeply. + * If not then throw. */ +export function assertNotEquals<T>(actual: T, expected: T, msg?: string) { + if (!equal(actual, expected)) { + return; + } + let actualString: string; + let expectedString: string; + try { + actualString = String(actual); + } catch { + actualString = "[Cannot display]"; + } + try { + expectedString = String(expected); + } catch { + expectedString = "[Cannot display]"; + } + if (!msg) { + msg = `actual: ${actualString} expected not to be: ${expectedString}`; + } + throw new AssertionError(msg); +} + +/** Make an assertion that `actual` and `expected` are strictly equal. If + * not then throw. */ +export function assertStrictEquals<T>( + actual: unknown, + expected: T, + msg?: string, +): asserts actual is T { + if (Object.is(actual, expected)) { + return; + } + + let message: string; + + if (msg) { + message = msg; + } else { + const actualString = format(actual); + const expectedString = format(expected); + + if (actualString === expectedString) { + const withOffset = actualString + .split("\n") + .map((l) => ` ${l}`) + .join("\n"); + message = + `Values have the same structure but are not reference-equal:\n\n${ + red(withOffset) + }\n`; + } else { + try { + const stringDiff = (typeof actual === "string") && + (typeof expected === "string"); + const diffResult = stringDiff + ? diffstr(actual as string, expected as string) + : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n"); + message = `Values are not strictly equal:\n${diffMsg}`; + } catch { + message = `\n${CAN_NOT_DISPLAY} + \n\n`; + } + } + } + + throw new AssertionError(message); +} + +/** Make an assertion that `actual` and `expected` are not strictly equal. + * If the values are strictly equal then throw. */ +export function assertNotStrictEquals<T>( + actual: T, + expected: T, + msg?: string, +) { + if (!Object.is(actual, expected)) { + return; + } + + throw new AssertionError( + msg ?? `Expected "actual" to be strictly unequal to: ${format(actual)}\n`, + ); +} + +/** Make an assertion that `actual` match RegExp `expected`. If not + * then throw. */ +export function assertMatch( + actual: string, + expected: RegExp, + msg?: string, +) { + if (!expected.test(actual)) { + if (!msg) { + msg = `actual: "${actual}" expected to match: "${expected}"`; + } + throw new AssertionError(msg); + } +} + +/** Make an assertion that `actual` not match RegExp `expected`. If match + * then throw. */ +export function assertNotMatch( + actual: string, + expected: RegExp, + msg?: string, +) { + if (expected.test(actual)) { + if (!msg) { + msg = `actual: "${actual}" expected to not match: "${expected}"`; + } + throw new AssertionError(msg); + } +} |