summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/_util/std_testing_diff.ts
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/polyfills/_util/std_testing_diff.ts')
-rw-r--r--ext/node/polyfills/_util/std_testing_diff.ts271
1 files changed, 174 insertions, 97 deletions
diff --git a/ext/node/polyfills/_util/std_testing_diff.ts b/ext/node/polyfills/_util/std_testing_diff.ts
index bcd619255..635ce1f29 100644
--- a/ext/node/polyfills/_util/std_testing_diff.ts
+++ b/ext/node/polyfills/_util/std_testing_diff.ts
@@ -1,9 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This file was vendored from std/testing/_diff.ts
-// TODO(petamoriken): enable prefer-primordials for node polyfills
-// deno-lint-ignore-file prefer-primordials
-
+import { primordials } from "ext:core/mod.js";
import {
bgGreen,
bgRed,
@@ -13,6 +11,30 @@ import {
red,
white,
} from "ext:deno_node/_util/std_fmt_colors.ts";
+const {
+ ArrayFrom,
+ ArrayPrototypeFilter,
+ ArrayPrototypeForEach,
+ ArrayPrototypeJoin,
+ ArrayPrototypeMap,
+ ArrayPrototypePop,
+ ArrayPrototypePush,
+ ArrayPrototypePushApply,
+ ArrayPrototypeReverse,
+ ArrayPrototypeShift,
+ ArrayPrototypeSlice,
+ ArrayPrototypeSplice,
+ ArrayPrototypeSome,
+ ArrayPrototypeUnshift,
+ SafeArrayIterator,
+ SafeRegExp,
+ StringPrototypeSplit,
+ StringPrototypeReplace,
+ StringPrototypeTrim,
+ MathMin,
+ ObjectFreeze,
+ Uint32Array,
+} = primordials;
interface FarthestPoint {
y: number;
@@ -28,7 +50,7 @@ export enum DiffType {
export interface DiffResult<T> {
type: DiffType;
value: T;
- details?: Array<DiffResult<T>>;
+ details?: DiffResult<T>[];
}
const REMOVED = 1;
@@ -38,11 +60,11 @@ const ADDED = 3;
function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] {
const common = [];
if (A.length === 0 || B.length === 0) return [];
- for (let i = 0; i < Math.min(A.length, B.length); i += 1) {
+ for (let i = 0; i < MathMin(A.length, B.length); i += 1) {
if (
A[reverse ? A.length - i - 1 : i] === B[reverse ? B.length - i - 1 : i]
) {
- common.push(A[reverse ? A.length - i - 1 : i]);
+ ArrayPrototypePush(common, A[reverse ? A.length - i - 1 : i]);
} else {
return common;
}
@@ -55,44 +77,56 @@ function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] {
* @param A Actual value
* @param B Expected value
*/
-export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
+export function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
const prefixCommon = createCommon(A, B);
- const suffixCommon = createCommon(
- A.slice(prefixCommon.length),
- B.slice(prefixCommon.length),
+ const suffixCommon = ArrayPrototypeReverse(createCommon(
+ ArrayPrototypeSlice(A, prefixCommon.length),
+ ArrayPrototypeSlice(B, prefixCommon.length),
true,
- ).reverse();
+ ));
A = suffixCommon.length
- ? A.slice(prefixCommon.length, -suffixCommon.length)
- : A.slice(prefixCommon.length);
+ ? ArrayPrototypeSlice(A, prefixCommon.length, -suffixCommon.length)
+ : ArrayPrototypeSlice(A, prefixCommon.length);
B = suffixCommon.length
- ? B.slice(prefixCommon.length, -suffixCommon.length)
- : B.slice(prefixCommon.length);
+ ? ArrayPrototypeSlice(B, prefixCommon.length, -suffixCommon.length)
+ : ArrayPrototypeSlice(B, prefixCommon.length);
const swapped = B.length > A.length;
- [A, B] = swapped ? [B, A] : [A, B];
+ if (swapped) {
+ const temp = A;
+ A = B;
+ B = temp;
+ }
const M = A.length;
const N = B.length;
- if (!M && !N && !suffixCommon.length && !prefixCommon.length) return [];
- if (!N) {
+ if (
+ M === 0 && N === 0 && suffixCommon.length === 0 && prefixCommon.length === 0
+ ) return [];
+ if (N === 0) {
return [
- ...prefixCommon.map(
- (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ...new SafeArrayIterator(
+ ArrayPrototypeMap(
+ prefixCommon,
+ (c: T): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ),
),
- ...A.map(
- (a): DiffResult<typeof a> => ({
+ ...new SafeArrayIterator(
+ ArrayPrototypeMap(A, (a: T): DiffResult<typeof a> => ({
type: swapped ? DiffType.added : DiffType.removed,
value: a,
- }),
+ })),
),
- ...suffixCommon.map(
- (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ...new SafeArrayIterator(
+ ArrayPrototypeMap(
+ suffixCommon,
+ (c: T): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ),
),
];
}
const offset = N;
const delta = M - N;
const size = M + N + 1;
- const fp: FarthestPoint[] = Array.from(
+ const fp: FarthestPoint[] = ArrayFrom(
{ length: size },
() => ({ y: -1, id: -1 }),
);
@@ -114,13 +148,13 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
B: T[],
current: FarthestPoint,
swapped: boolean,
- ): Array<{
+ ): {
type: DiffType;
value: T;
- }> {
+ }[] {
const M = A.length;
const N = B.length;
- const result = [];
+ const result: DiffResult<T>[] = [];
let a = M - 1;
let b = N - 1;
let j = routes[current.id];
@@ -129,19 +163,19 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
if (!j && !type) break;
const prev = j;
if (type === REMOVED) {
- result.unshift({
+ ArrayPrototypeUnshift(result, {
type: swapped ? DiffType.removed : DiffType.added,
value: B[b],
});
b -= 1;
} else if (type === ADDED) {
- result.unshift({
+ ArrayPrototypeUnshift(result, {
type: swapped ? DiffType.added : DiffType.removed,
value: A[a],
});
a -= 1;
} else {
- result.unshift({ type: DiffType.common, value: A[a] });
+ ArrayPrototypeUnshift(result, { type: DiffType.common, value: A[a] });
a -= 1;
b -= 1;
}
@@ -234,16 +268,40 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
);
}
return [
- ...prefixCommon.map(
- (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ...new SafeArrayIterator(
+ ArrayPrototypeMap(
+ prefixCommon,
+ (c: T): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ),
),
- ...backTrace(A, B, fp[delta + offset], swapped),
- ...suffixCommon.map(
- (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ...new SafeArrayIterator(backTrace(A, B, fp[delta + offset], swapped)),
+ ...new SafeArrayIterator(
+ ArrayPrototypeMap(
+ suffixCommon,
+ (c: T): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
+ ),
),
];
}
+const ESCAPE_PATTERN = new SafeRegExp(/([\b\f\t\v])/g);
+const ESCAPE_MAP = ObjectFreeze({
+ "\b": "\\b",
+ "\f": "\\f",
+ "\t": "\\t",
+ "\v": "\\v",
+});
+const LINE_BREAK_GLOBAL_PATTERN = new SafeRegExp(/\r\n|\r|\n/g);
+
+const LINE_BREAK_PATTERN = new SafeRegExp(/(\n|\r\n)/);
+const WHITESPACE_PATTERN = new SafeRegExp(/\s+/);
+const WHITESPACE_SYMBOL_PATTERN = new SafeRegExp(
+ /([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/,
+);
+const LATIN_CHARACTER_PATTERN = new SafeRegExp(
+ /^[a-zA-Z\u{C0}-\u{FF}\u{D8}-\u{F6}\u{F8}-\u{2C6}\u{2C8}-\u{2D7}\u{2DE}-\u{2FF}\u{1E00}-\u{1EFF}]+$/u,
+);
+
/**
* Renders the differences between the actual and expected strings
* Partially inspired from https://github.com/kpdecker/jsdiff
@@ -254,44 +312,44 @@ export function diffstr(A: string, B: string) {
function unescape(string: string): string {
// unescape invisible characters.
// ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#escape_sequences
- return string
- .replaceAll("\b", "\\b")
- .replaceAll("\f", "\\f")
- .replaceAll("\t", "\\t")
- .replaceAll("\v", "\\v")
- .replaceAll( // does not remove line breaks
- /\r\n|\r|\n/g,
- (str) => str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n",
- );
+ return StringPrototypeReplace(
+ StringPrototypeReplace(
+ string,
+ ESCAPE_PATTERN,
+ (c: string) => ESCAPE_MAP[c],
+ ),
+ LINE_BREAK_GLOBAL_PATTERN, // does not remove line breaks
+ (str: string) =>
+ str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n",
+ );
}
function tokenize(string: string, { wordDiff = false } = {}): string[] {
if (wordDiff) {
// Split string on whitespace symbols
- const tokens = string.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/);
- // Extended Latin character set
- const words =
- /^[a-zA-Z\u{C0}-\u{FF}\u{D8}-\u{F6}\u{F8}-\u{2C6}\u{2C8}-\u{2D7}\u{2DE}-\u{2FF}\u{1E00}-\u{1EFF}]+$/u;
+ const tokens = StringPrototypeSplit(string, WHITESPACE_SYMBOL_PATTERN);
// Join boundary splits that we do not consider to be boundaries and merge empty strings surrounded by word chars
for (let i = 0; i < tokens.length - 1; i++) {
if (
- !tokens[i + 1] && tokens[i + 2] && words.test(tokens[i]) &&
- words.test(tokens[i + 2])
+ !tokens[i + 1] && tokens[i + 2] &&
+ LATIN_CHARACTER_PATTERN.test(tokens[i]) &&
+ LATIN_CHARACTER_PATTERN.test(tokens[i + 2])
) {
tokens[i] += tokens[i + 2];
- tokens.splice(i + 1, 2);
+ ArrayPrototypeSplice(tokens, i + 1, 2);
i--;
}
}
- return tokens.filter((token) => token);
+ return ArrayPrototypeFilter(tokens, (token: string) => token);
} else {
// Split string on new lines symbols
- const tokens = [], lines = string.split(/(\n|\r\n)/);
+ const tokens: string[] = [],
+ lines: string[] = StringPrototypeSplit(string, LINE_BREAK_PATTERN);
// Ignore final empty token when text ends with a newline
- if (!lines[lines.length - 1]) {
- lines.pop();
+ if (lines[lines.length - 1] === "") {
+ ArrayPrototypePop(lines);
}
// Merge the content and line separators into single tokens
@@ -299,7 +357,7 @@ export function diffstr(A: string, B: string) {
if (i % 2) {
tokens[tokens.length - 1] += lines[i];
} else {
- tokens.push(lines[i]);
+ ArrayPrototypePush(tokens, lines[i]);
}
}
return tokens;
@@ -310,22 +368,28 @@ export function diffstr(A: string, B: string) {
// and merge "space-diff" if surrounded by word-diff for cleaner displays
function createDetails(
line: DiffResult<string>,
- tokens: Array<DiffResult<string>>,
+ tokens: DiffResult<string>[],
) {
- return tokens.filter(({ type }) =>
- type === line.type || type === DiffType.common
- ).map((result, i, t) => {
- if (
- (result.type === DiffType.common) && (t[i - 1]) &&
- (t[i - 1]?.type === t[i + 1]?.type) && /\s+/.test(result.value)
- ) {
- return {
- ...result,
- type: t[i - 1].type,
- };
- }
- return result;
- });
+ return ArrayPrototypeMap(
+ ArrayPrototypeFilter(
+ tokens,
+ ({ type }: DiffResult<string>) =>
+ type === line.type || type === DiffType.common,
+ ),
+ (result: DiffResult<string>, i: number, t: DiffResult<string>[]) => {
+ if (
+ (result.type === DiffType.common) && (t[i - 1]) &&
+ (t[i - 1]?.type === t[i + 1]?.type) &&
+ WHITESPACE_PATTERN.test(result.value)
+ ) {
+ return {
+ ...result,
+ type: t[i - 1].type,
+ };
+ }
+ return result;
+ },
+ );
}
// Compute multi-line diff
@@ -334,32 +398,36 @@ export function diffstr(A: string, B: string) {
tokenize(`${unescape(B)}\n`),
);
- const added = [], removed = [];
- for (const result of diffResult) {
+ const added: DiffResult<string>[] = [], removed: DiffResult<string>[] = [];
+ for (let i = 0; i < diffResult.length; ++i) {
+ const result = diffResult[i];
if (result.type === DiffType.added) {
- added.push(result);
+ ArrayPrototypePush(added, result);
}
if (result.type === DiffType.removed) {
- removed.push(result);
+ ArrayPrototypePush(removed, result);
}
}
// Compute word-diff
const aLines = added.length < removed.length ? added : removed;
const bLines = aLines === removed ? added : removed;
- for (const a of aLines) {
- let tokens = [] as Array<DiffResult<string>>,
+ for (let i = 0; i < aLines.length; ++i) {
+ const a = aLines[i];
+ let tokens = [] as DiffResult<string>[],
b: undefined | DiffResult<string>;
// Search another diff line with at least one common token
- while (bLines.length) {
- b = bLines.shift();
+ while (bLines.length !== 0) {
+ b = ArrayPrototypeShift(bLines);
tokens = diff(
tokenize(a.value, { wordDiff: true }),
tokenize(b?.value ?? "", { wordDiff: true }),
);
if (
- tokens.some(({ type, value }) =>
- type === DiffType.common && value.trim().length
+ ArrayPrototypeSome(
+ tokens,
+ ({ type, value }) =>
+ type === DiffType.common && StringPrototypeTrim(value).length,
)
) {
break;
@@ -418,26 +486,35 @@ export function buildMessage(
{ stringDiff = false } = {},
): string[] {
const messages: string[] = [], diffMessages: string[] = [];
- messages.push("");
- messages.push("");
- messages.push(
+ ArrayPrototypePush(messages, "");
+ ArrayPrototypePush(messages, "");
+ ArrayPrototypePush(
+ messages,
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(bold("Expected"))
}`,
);
- messages.push("");
- messages.push("");
- diffResult.forEach((result: DiffResult<string>) => {
+ ArrayPrototypePush(messages, "");
+ ArrayPrototypePush(messages, "");
+ ArrayPrototypeForEach(diffResult, (result: DiffResult<string>) => {
const c = createColor(result.type);
- const line = result.details?.map((detail) =>
- detail.type !== DiffType.common
- ? createColor(detail.type, { background: true })(detail.value)
- : detail.value
- ).join("") ?? result.value;
- diffMessages.push(c(`${createSign(result.type)}${line}`));
+
+ const line = result.details != null
+ ? ArrayPrototypeJoin(
+ ArrayPrototypeMap(result.details, (detail) =>
+ detail.type !== DiffType.common
+ ? createColor(detail.type, { background: true })(detail.value)
+ : detail.value),
+ "",
+ )
+ : result.value;
+ ArrayPrototypePush(diffMessages, c(`${createSign(result.type)}${line}`));
});
- messages.push(...(stringDiff ? [diffMessages.join("")] : diffMessages));
- messages.push("");
+ ArrayPrototypePushApply(
+ messages,
+ stringDiff ? [ArrayPrototypeJoin(diffMessages, "")] : diffMessages,
+ );
+ ArrayPrototypePush(messages, "");
return messages;
}