@vitest/utils 0.29.7 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ const SAFE_TIMERS_SYMBOL = Symbol("vitest:SAFE_TIMERS");
2
+ const SAFE_COLORS_SYMBOL = Symbol("vitest:SAFE_COLORS");
3
+
4
+ const colorsMap = {
5
+ bold: ["\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"],
6
+ dim: ["\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"],
7
+ italic: ["\x1B[3m", "\x1B[23m"],
8
+ underline: ["\x1B[4m", "\x1B[24m"],
9
+ inverse: ["\x1B[7m", "\x1B[27m"],
10
+ hidden: ["\x1B[8m", "\x1B[28m"],
11
+ strikethrough: ["\x1B[9m", "\x1B[29m"],
12
+ black: ["\x1B[30m", "\x1B[39m"],
13
+ red: ["\x1B[31m", "\x1B[39m"],
14
+ green: ["\x1B[32m", "\x1B[39m"],
15
+ yellow: ["\x1B[33m", "\x1B[39m"],
16
+ blue: ["\x1B[34m", "\x1B[39m"],
17
+ magenta: ["\x1B[35m", "\x1B[39m"],
18
+ cyan: ["\x1B[36m", "\x1B[39m"],
19
+ white: ["\x1B[37m", "\x1B[39m"],
20
+ gray: ["\x1B[90m", "\x1B[39m"],
21
+ bgBlack: ["\x1B[40m", "\x1B[49m"],
22
+ bgRed: ["\x1B[41m", "\x1B[49m"],
23
+ bgGreen: ["\x1B[42m", "\x1B[49m"],
24
+ bgYellow: ["\x1B[43m", "\x1B[49m"],
25
+ bgBlue: ["\x1B[44m", "\x1B[49m"],
26
+ bgMagenta: ["\x1B[45m", "\x1B[49m"],
27
+ bgCyan: ["\x1B[46m", "\x1B[49m"],
28
+ bgWhite: ["\x1B[47m", "\x1B[49m"]
29
+ };
30
+ const colorsEntries = Object.entries(colorsMap);
31
+ function string(str) {
32
+ return String(str);
33
+ }
34
+ string.open = "";
35
+ string.close = "";
36
+ const defaultColors = /* @__PURE__ */ colorsEntries.reduce((acc, [key]) => {
37
+ acc[key] = string;
38
+ return acc;
39
+ }, { isColorSupported: false });
40
+ function getDefaultColors() {
41
+ return { ...defaultColors };
42
+ }
43
+ function getColors() {
44
+ return globalThis[SAFE_COLORS_SYMBOL] || defaultColors;
45
+ }
46
+ function createColors(isTTY = false) {
47
+ const enabled = typeof process !== "undefined" && !("NO_COLOR" in process.env || process.argv.includes("--no-color")) && !("GITHUB_ACTIONS" in process.env) && ("FORCE_COLOR" in process.env || process.argv.includes("--color") || process.platform === "win32" || isTTY && process.env.TERM !== "dumb" || "CI" in process.env);
48
+ const replaceClose = (string2, close, replace, index) => {
49
+ const start = string2.substring(0, index) + replace;
50
+ const end = string2.substring(index + close.length);
51
+ const nextIndex = end.indexOf(close);
52
+ return ~nextIndex ? start + replaceClose(end, close, replace, nextIndex) : start + end;
53
+ };
54
+ const formatter = (open, close, replace = open) => {
55
+ const fn = (input) => {
56
+ const string2 = String(input);
57
+ const index = string2.indexOf(close, open.length);
58
+ return ~index ? open + replaceClose(string2, close, replace, index) + close : open + string2 + close;
59
+ };
60
+ fn.open = open;
61
+ fn.close = close;
62
+ return fn;
63
+ };
64
+ const colorsObject = {
65
+ isColorSupported: enabled,
66
+ reset: enabled ? (s) => `\x1B[0m${s}\x1B[0m` : string
67
+ };
68
+ for (const [name, formatterArgs] of colorsEntries) {
69
+ colorsObject[name] = enabled ? formatter(...formatterArgs) : string;
70
+ }
71
+ return colorsObject;
72
+ }
73
+ function setupColors(colors) {
74
+ globalThis[SAFE_COLORS_SYMBOL] = colors;
75
+ }
76
+
77
+ export { SAFE_TIMERS_SYMBOL as S, SAFE_COLORS_SYMBOL as a, getColors as b, createColors as c, getDefaultColors as g, setupColors as s };
package/dist/diff.d.ts CHANGED
@@ -1,13 +1,176 @@
1
- declare function formatLine(line: string, outputTruncateLength?: number): string;
2
- type Color = (str: string) => string;
1
+ import { DisplayOptions } from 'concordance';
2
+
3
+ declare function getConcordanceTheme(): {
4
+ boolean: {
5
+ (input: unknown): string;
6
+ open: string;
7
+ close: string;
8
+ };
9
+ circular: string;
10
+ date: {
11
+ invalid: string;
12
+ value: {
13
+ (input: unknown): string;
14
+ open: string;
15
+ close: string;
16
+ };
17
+ };
18
+ diffGutters: {
19
+ actual: string;
20
+ expected: string;
21
+ padding: string;
22
+ };
23
+ error: {
24
+ ctor: {
25
+ open: string;
26
+ close: string;
27
+ };
28
+ name: {
29
+ (input: unknown): string;
30
+ open: string;
31
+ close: string;
32
+ };
33
+ };
34
+ function: {
35
+ name: {
36
+ (input: unknown): string;
37
+ open: string;
38
+ close: string;
39
+ };
40
+ stringTag: {
41
+ (input: unknown): string;
42
+ open: string;
43
+ close: string;
44
+ };
45
+ };
46
+ global: {
47
+ (input: unknown): string;
48
+ open: string;
49
+ close: string;
50
+ };
51
+ item: {
52
+ after: string;
53
+ };
54
+ list: {
55
+ openBracket: string;
56
+ closeBracket: string;
57
+ };
58
+ mapEntry: {
59
+ after: string;
60
+ };
61
+ maxDepth: string;
62
+ null: {
63
+ (input: unknown): string;
64
+ open: string;
65
+ close: string;
66
+ };
67
+ number: {
68
+ (input: unknown): string;
69
+ open: string;
70
+ close: string;
71
+ };
72
+ object: {
73
+ openBracket: string;
74
+ closeBracket: string;
75
+ ctor: {
76
+ (input: unknown): string;
77
+ open: string;
78
+ close: string;
79
+ };
80
+ stringTag: {
81
+ open: string;
82
+ close: string;
83
+ };
84
+ secondaryStringTag: {
85
+ open: string;
86
+ close: string;
87
+ };
88
+ };
89
+ property: {
90
+ after: string;
91
+ keyBracket: {
92
+ open: string;
93
+ close: string;
94
+ };
95
+ valueFallback: string;
96
+ };
97
+ regexp: {
98
+ source: {
99
+ open: string;
100
+ close: string;
101
+ };
102
+ flags: {
103
+ (input: unknown): string;
104
+ open: string;
105
+ close: string;
106
+ };
107
+ };
108
+ stats: {
109
+ separator: string;
110
+ };
111
+ string: {
112
+ open: string;
113
+ close: string;
114
+ line: {
115
+ open: string;
116
+ close: string;
117
+ };
118
+ multiline: {
119
+ start: string;
120
+ end: string;
121
+ };
122
+ controlPicture: {
123
+ (input: unknown): string;
124
+ open: string;
125
+ close: string;
126
+ };
127
+ diff: {
128
+ insert: {
129
+ open: string;
130
+ close: string;
131
+ };
132
+ delete: {
133
+ open: string;
134
+ close: string;
135
+ };
136
+ equal: {
137
+ (input: unknown): string;
138
+ open: string;
139
+ close: string;
140
+ };
141
+ insertLine: {
142
+ open: string;
143
+ close: string;
144
+ };
145
+ deleteLine: {
146
+ open: string;
147
+ close: string;
148
+ };
149
+ };
150
+ };
151
+ symbol: {
152
+ (input: unknown): string;
153
+ open: string;
154
+ close: string;
155
+ };
156
+ typedArray: {
157
+ bytes: {
158
+ (input: unknown): string;
159
+ open: string;
160
+ close: string;
161
+ };
162
+ };
163
+ undefined: {
164
+ (input: unknown): string;
165
+ open: string;
166
+ close: string;
167
+ };
168
+ };
169
+ declare function diffDescriptors(actual: unknown, expected: unknown, options: DisplayOptions): string;
170
+ declare function formatDescriptor(value: unknown, options: DisplayOptions): string;
171
+
3
172
  interface DiffOptions {
4
- outputDiffMaxLines?: number;
5
- outputTruncateLength?: number;
6
- outputDiffLines?: number;
7
173
  showLegend?: boolean;
8
- colorSuccess?: Color;
9
- colorError?: Color;
10
- colorDim?: Color;
11
174
  }
12
175
  /**
13
176
  * Returns unified diff between two strings with coloured ANSI output.
@@ -17,6 +180,6 @@ interface DiffOptions {
17
180
  * @param {String} expected
18
181
  * @return {string} The diff.
19
182
  */
20
- declare function unifiedDiff(actual: string, expected: string, options?: DiffOptions): string;
183
+ declare function unifiedDiff(actual: unknown, expected: unknown, options?: DiffOptions): string;
21
184
 
22
- export { DiffOptions, formatLine, unifiedDiff };
185
+ export { DiffOptions, diffDescriptors, formatDescriptor, getConcordanceTheme, unifiedDiff };
package/dist/diff.js CHANGED
@@ -1,97 +1,118 @@
1
- import * as diff from 'diff';
2
- import cliTruncate from 'cli-truncate';
1
+ import { b as getColors } from './chunk-colors.js';
2
+ import concordance from 'concordance';
3
3
 
4
- function formatLine(line, outputTruncateLength) {
5
- var _a;
6
- return cliTruncate(line, (outputTruncateLength ?? (((_a = process.stdout) == null ? void 0 : _a.columns) || 80)) - 4);
4
+ function getConcordanceTheme() {
5
+ const c = getColors();
6
+ return {
7
+ boolean: c.yellow,
8
+ circular: c.gray("[Circular]"),
9
+ date: {
10
+ invalid: c.red("invalid"),
11
+ value: c.blue
12
+ },
13
+ diffGutters: {
14
+ actual: ` ${c.red("-")} `,
15
+ expected: ` ${c.green("+")} `,
16
+ padding: " "
17
+ },
18
+ error: {
19
+ ctor: { open: `${c.gray.open}(`, close: `)${c.gray.close}` },
20
+ name: c.magenta
21
+ },
22
+ function: {
23
+ name: c.blue,
24
+ stringTag: c.magenta
25
+ },
26
+ global: c.magenta,
27
+ item: { after: c.gray(",") },
28
+ list: { openBracket: c.gray("["), closeBracket: c.gray("]") },
29
+ mapEntry: { after: c.gray(",") },
30
+ maxDepth: c.gray("\u2026"),
31
+ null: c.yellow,
32
+ number: c.yellow,
33
+ object: {
34
+ openBracket: c.gray("{"),
35
+ closeBracket: c.gray("}"),
36
+ ctor: c.magenta,
37
+ stringTag: { open: `${c.magenta.open}@`, close: c.magenta.close },
38
+ secondaryStringTag: { open: `${c.gray.open}@`, close: c.gray.close }
39
+ },
40
+ property: {
41
+ after: c.gray(","),
42
+ keyBracket: { open: c.gray("["), close: c.gray("]") },
43
+ valueFallback: c.gray("\u2026")
44
+ },
45
+ regexp: {
46
+ source: { open: `${c.blue.open}/`, close: `/${c.blue.close}` },
47
+ flags: c.yellow
48
+ },
49
+ stats: { separator: c.gray("---") },
50
+ string: {
51
+ open: c.blue.open,
52
+ close: c.blue.close,
53
+ line: { open: c.blue("'"), close: c.blue("'") },
54
+ multiline: { start: c.blue("`"), end: c.blue("`") },
55
+ controlPicture: c.gray,
56
+ diff: {
57
+ insert: {
58
+ open: c.bgGreen.open + c.black.open,
59
+ close: c.black.close + c.bgGreen.close
60
+ },
61
+ delete: {
62
+ open: c.bgRed.open + c.black.open,
63
+ close: c.black.close + c.bgRed.close
64
+ },
65
+ equal: c.blue,
66
+ insertLine: {
67
+ open: c.green.open,
68
+ close: c.green.close
69
+ },
70
+ deleteLine: {
71
+ open: c.red.open,
72
+ close: c.red.close
73
+ }
74
+ }
75
+ },
76
+ symbol: c.yellow,
77
+ typedArray: {
78
+ bytes: c.yellow
79
+ },
80
+ undefined: c.yellow
81
+ };
82
+ }
83
+ function diffDescriptors(actual, expected, options) {
84
+ return concordance.diff(expected, actual, options);
7
85
  }
86
+ function formatDescriptor(value, options) {
87
+ return concordance.formatDescriptor(value, options);
88
+ }
89
+
8
90
  function unifiedDiff(actual, expected, options = {}) {
9
- if (actual === expected)
10
- return "";
11
- const { outputTruncateLength, outputDiffLines, outputDiffMaxLines, showLegend = true } = options;
12
- const indent = " ";
13
- const diffLimit = outputDiffLines || 15;
14
- const diffMaxLines = outputDiffMaxLines || 50;
91
+ const theme = getConcordanceTheme();
92
+ const diff = diffDescriptors(actual, expected, { theme });
93
+ const { showLegend = true } = options;
15
94
  const counts = {
16
95
  "+": 0,
17
96
  "-": 0
18
97
  };
19
- let previousState = null;
20
- let previousCount = 0;
21
- const str = (str2) => str2;
22
- const dim = options.colorDim || str;
23
- const green = options.colorSuccess || str;
24
- const red = options.colorError || str;
25
- function preprocess(line) {
26
- if (!line || line.match(/\\ No newline/))
27
- return;
28
- const char = line[0];
29
- if ("-+".includes(char)) {
30
- if (previousState !== char) {
31
- previousState = char;
32
- previousCount = 0;
33
- }
34
- previousCount++;
35
- counts[char]++;
36
- if (previousCount === diffLimit)
37
- return dim(`${char} ...`);
38
- else if (previousCount > diffLimit)
39
- return;
40
- }
41
- return line;
42
- }
43
- const msg = diff.createPatch("string", expected, actual);
44
- let lines = msg.split("\n").slice(5).map(preprocess).filter(Boolean);
45
- let moreLines = 0;
46
- const isCompact = counts["+"] === 1 && counts["-"] === 1 && lines.length === 2;
47
- if (lines.length > diffMaxLines) {
48
- const firstDiff = lines.findIndex((line) => line[0] === "-" || line[0] === "+");
49
- const displayLines = lines.slice(firstDiff - 2, diffMaxLines);
50
- const lastDisplayedIndex = firstDiff - 2 + diffMaxLines;
51
- if (lastDisplayedIndex < lines.length)
52
- moreLines = lines.length - lastDisplayedIndex;
53
- lines = displayLines;
54
- }
55
- let formatted = lines.map((line) => {
56
- line = line.replace(/\\"/g, '"');
57
- if (line[0] === "-") {
58
- line = formatLine(line.slice(1), outputTruncateLength);
59
- if (isCompact)
60
- return green(line);
61
- return green(`- ${formatLine(line, outputTruncateLength)}`);
62
- }
63
- if (line[0] === "+") {
64
- line = formatLine(line.slice(1), outputTruncateLength);
65
- if (isCompact)
66
- return red(line);
67
- return red(`+ ${formatLine(line, outputTruncateLength)}`);
68
- }
69
- if (line.match(/@@/))
70
- return "--";
71
- return ` ${line}`;
98
+ const c = getColors();
99
+ const plus = theme.diffGutters.actual;
100
+ const minus = ` ${c.green("+")}`;
101
+ const lines = diff.split(/\r?\n/g);
102
+ lines.forEach((line) => {
103
+ if (line.startsWith(plus))
104
+ counts["+"]++;
105
+ else if (line.startsWith(minus))
106
+ counts["-"]++;
72
107
  });
73
- if (moreLines)
74
- formatted.push(dim(`... ${moreLines} more lines`));
108
+ let legend = "";
75
109
  if (showLegend) {
76
- if (isCompact) {
77
- formatted = [
78
- `${green("- Expected")} ${formatted[0]}`,
79
- `${red("+ Received")} ${formatted[1]}`
80
- ];
81
- } else {
82
- if (formatted[0].includes('"'))
83
- formatted[0] = formatted[0].replace('"', "");
84
- const last = formatted.length - 1;
85
- if (formatted[last].endsWith('"'))
86
- formatted[last] = formatted[last].slice(0, formatted[last].length - 1);
87
- formatted.unshift(
88
- green(`- Expected - ${counts["-"]}`),
89
- red(`+ Received + ${counts["+"]}`),
90
- ""
91
- );
92
- }
110
+ legend = ` ${c.green(`- Expected - ${counts["-"]}`)}
111
+ ${c.red(`+ Received + ${counts["+"]}`)}
112
+
113
+ `;
93
114
  }
94
- return formatted.map((i) => i ? indent + i : i).join("\n");
115
+ return legend + diff.replace(/␊\s*$/mg, "");
95
116
  }
96
117
 
97
- export { formatLine, unifiedDiff };
118
+ export { diffDescriptors, formatDescriptor, getConcordanceTheme, unifiedDiff };
package/dist/helpers.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Nullable, Arrayable } from './types.js';
2
2
 
3
+ declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
3
4
  declare function assertTypes(value: unknown, name: string, types: string[]): void;
5
+ declare function isPrimitive(value: unknown): boolean;
4
6
  declare function slash(path: string): string;
5
7
  declare function parseRegexp(input: string): RegExp;
6
8
  declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
@@ -16,5 +18,15 @@ type DeferPromise<T> = Promise<T> & {
16
18
  reject: (reason?: any) => void;
17
19
  };
18
20
  declare function createDefer<T>(): DeferPromise<T>;
21
+ /**
22
+ * If code starts with a function call, will return its last index, respecting arguments.
23
+ * This will return 25 - last ending character of toMatch ")"
24
+ * Also works with callbacks
25
+ * ```
26
+ * toMatch({ test: '123' });
27
+ * toBeAliased('123')
28
+ * ```
29
+ */
30
+ declare function getCallLastIndex(code: string): number | null;
19
31
 
20
- export { assertTypes, clone, createDefer, deepClone, getOwnProperties, getType, isObject, noop, objectAttr, parseRegexp, slash, toArray };
32
+ export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray };
package/dist/helpers.js CHANGED
@@ -1,9 +1,15 @@
1
+ function notNullish(v) {
2
+ return v != null;
3
+ }
1
4
  function assertTypes(value, name, types) {
2
5
  const receivedType = typeof value;
3
6
  const pass = types.includes(receivedType);
4
7
  if (!pass)
5
8
  throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`);
6
9
  }
10
+ function isPrimitive(value) {
11
+ return value === null || typeof value !== "function" && typeof value !== "object";
12
+ }
7
13
  function slash(path) {
8
14
  return path.replace(/\\/g, "/");
9
15
  }
@@ -62,8 +68,25 @@ function clone(val, seen) {
62
68
  out = Object.create(Object.getPrototypeOf(val));
63
69
  seen.set(val, out);
64
70
  const props = getOwnProperties(val);
65
- for (const k2 of props)
66
- out[k2] = clone(val[k2], seen);
71
+ for (const k2 of props) {
72
+ const descriptor = Object.getOwnPropertyDescriptor(val, k2);
73
+ if (!descriptor)
74
+ continue;
75
+ const cloned = clone(val[k2], seen);
76
+ if ("get" in descriptor) {
77
+ Object.defineProperty(out, k2, {
78
+ ...descriptor,
79
+ get() {
80
+ return cloned;
81
+ }
82
+ });
83
+ } else {
84
+ Object.defineProperty(out, k2, {
85
+ ...descriptor,
86
+ value: cloned
87
+ });
88
+ }
89
+ }
67
90
  return out;
68
91
  }
69
92
  return val;
@@ -91,5 +114,33 @@ function createDefer() {
91
114
  p.reject = reject;
92
115
  return p;
93
116
  }
117
+ function getCallLastIndex(code) {
118
+ let charIndex = -1;
119
+ let inString = null;
120
+ let startedBracers = 0;
121
+ let endedBracers = 0;
122
+ let beforeChar = null;
123
+ while (charIndex <= code.length) {
124
+ beforeChar = code[charIndex];
125
+ charIndex++;
126
+ const char = code[charIndex];
127
+ const isCharString = char === '"' || char === "'" || char === "`";
128
+ if (isCharString && beforeChar !== "\\") {
129
+ if (inString === char)
130
+ inString = null;
131
+ else if (!inString)
132
+ inString = char;
133
+ }
134
+ if (!inString) {
135
+ if (char === "(")
136
+ startedBracers++;
137
+ if (char === ")")
138
+ endedBracers++;
139
+ }
140
+ if (startedBracers && endedBracers && startedBracers === endedBracers)
141
+ return charIndex;
142
+ }
143
+ return null;
144
+ }
94
145
 
95
- export { assertTypes, clone, createDefer, deepClone, getOwnProperties, getType, isObject, noop, objectAttr, parseRegexp, slash, toArray };
146
+ export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- export { assertTypes, clone, createDefer, deepClone, getOwnProperties, getType, isObject, noop, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
1
+ export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
2
+ import { ParsedStack, ErrorWithDiff } from './types.js';
2
3
  export { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, MergeInsertions, MutableArray, Nullable } from './types.js';
3
4
  import { PrettyFormatOptions } from 'pretty-format';
5
+ import util from 'util';
4
6
 
5
7
  declare function stringify(object: unknown, maxDepth?: number, { maxLength, ...options }?: PrettyFormatOptions & {
6
8
  maxLength?: number;
@@ -20,7 +22,8 @@ declare function setSafeTimers(): void;
20
22
  declare function shuffle<T>(array: T[], seed?: number): T[];
21
23
 
22
24
  declare function format(...args: any[]): string;
23
- declare function inspect(obj: unknown): string;
25
+ declare function utilInspect(item: unknown, options?: util.InspectOptions): string;
26
+ declare function loupeInspect(obj: unknown): string;
24
27
  declare function objDisplay(obj: unknown): string;
25
28
 
26
29
  declare const SAFE_TIMERS_SYMBOL: unique symbol;
@@ -54,7 +57,11 @@ declare const colorsMap: {
54
57
  };
55
58
  type ColorName = keyof typeof colorsMap;
56
59
  type ColorsMethods = {
57
- [Key in ColorName]: (input: unknown) => string;
60
+ [Key in ColorName]: {
61
+ (input: unknown): string;
62
+ open: string;
63
+ close: string;
64
+ };
58
65
  };
59
66
  type Colors = ColorsMethods & {
60
67
  isColorSupported: boolean;
@@ -76,4 +83,11 @@ interface ErrorOptions {
76
83
  */
77
84
  declare function createSimpleStackTrace(options?: ErrorOptions): string;
78
85
 
79
- export { SAFE_COLORS_SYMBOL, SAFE_TIMERS_SYMBOL, createColors, createSimpleStackTrace, format, getColors, getDefaultColors, getSafeTimers, inspect, objDisplay, setSafeTimers, setupColors, shuffle, stringify };
86
+ declare const lineSplitRE: RegExp;
87
+ declare function parseSingleStack(raw: string): ParsedStack | null;
88
+ declare function parseStacktrace(stack: string, ignore?: (string | RegExp)[]): ParsedStack[];
89
+ declare function parseErrorStacktrace(e: ErrorWithDiff, ignore?: (string | RegExp)[]): ParsedStack[];
90
+ declare function positionToOffset(source: string, lineNumber: number, columnNumber: number): number;
91
+ declare function offsetToLineNumber(source: string, offset: number): number;
92
+
93
+ export { ErrorWithDiff, ParsedStack, SAFE_COLORS_SYMBOL, SAFE_TIMERS_SYMBOL, createColors, createSimpleStackTrace, format, getColors, getDefaultColors, getSafeTimers, lineSplitRE, loupeInspect, objDisplay, offsetToLineNumber, parseErrorStacktrace, parseSingleStack, parseStacktrace, positionToOffset, setSafeTimers, setupColors, shuffle, stringify, utilInspect };
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
- export { assertTypes, clone, createDefer, deepClone, getOwnProperties, getType, isObject, noop, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
1
+ import { notNullish, isPrimitive } from './helpers.js';
2
+ export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, noop, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
2
3
  import { format as format$1, plugins } from 'pretty-format';
4
+ import { S as SAFE_TIMERS_SYMBOL } from './chunk-colors.js';
5
+ export { a as SAFE_COLORS_SYMBOL, c as createColors, b as getColors, g as getDefaultColors, s as setupColors } from './chunk-colors.js';
3
6
  import util from 'util';
4
7
  import loupeImport from 'loupe';
5
8
 
@@ -26,6 +29,7 @@ function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
26
29
  result = format$1(object, {
27
30
  maxDepth,
28
31
  escapeString: false,
32
+ // min: true,
29
33
  plugins: PLUGINS,
30
34
  ...options
31
35
  });
@@ -34,6 +38,7 @@ function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
34
38
  callToJSON: false,
35
39
  maxDepth,
36
40
  escapeString: false,
41
+ // min: true,
37
42
  plugins: PLUGINS,
38
43
  ...options
39
44
  });
@@ -41,9 +46,6 @@ function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
41
46
  return result.length >= MAX_LENGTH && maxDepth > 1 ? stringify(object, Math.floor(maxDepth / 2)) : result;
42
47
  }
43
48
 
44
- const SAFE_TIMERS_SYMBOL = Symbol("vitest:SAFE_TIMERS");
45
- const SAFE_COLORS_SYMBOL = Symbol("vitest:SAFE_COLORS");
46
-
47
49
  function getSafeTimers() {
48
50
  const {
49
51
  setTimeout: safeSetTimeout,
@@ -111,7 +113,10 @@ const loupe = typeof loupeImport.default === "function" ? loupeImport.default :
111
113
  function format(...args) {
112
114
  return util.format(...args);
113
115
  }
114
- function inspect(obj) {
116
+ function utilInspect(item, options) {
117
+ return util.inspect(item, options);
118
+ }
119
+ function loupeInspect(obj) {
115
120
  return loupe(obj, {
116
121
  depth: 2,
117
122
  truncate: 40
@@ -119,7 +124,7 @@ function inspect(obj) {
119
124
  }
120
125
  function objDisplay(obj) {
121
126
  const truncateThreshold = 40;
122
- const str = inspect(obj);
127
+ const str = loupeInspect(obj);
123
128
  const type = Object.prototype.toString.call(obj);
124
129
  if (str.length >= truncateThreshold) {
125
130
  if (type === "[object Function]") {
@@ -138,77 +143,6 @@ function objDisplay(obj) {
138
143
  return str;
139
144
  }
140
145
 
141
- const colorsMap = {
142
- bold: ["\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"],
143
- dim: ["\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"],
144
- italic: ["\x1B[3m", "\x1B[23m"],
145
- underline: ["\x1B[4m", "\x1B[24m"],
146
- inverse: ["\x1B[7m", "\x1B[27m"],
147
- hidden: ["\x1B[8m", "\x1B[28m"],
148
- strikethrough: ["\x1B[9m", "\x1B[29m"],
149
- black: ["\x1B[30m", "\x1B[39m"],
150
- red: ["\x1B[31m", "\x1B[39m"],
151
- green: ["\x1B[32m", "\x1B[39m"],
152
- yellow: ["\x1B[33m", "\x1B[39m"],
153
- blue: ["\x1B[34m", "\x1B[39m"],
154
- magenta: ["\x1B[35m", "\x1B[39m"],
155
- cyan: ["\x1B[36m", "\x1B[39m"],
156
- white: ["\x1B[37m", "\x1B[39m"],
157
- gray: ["\x1B[90m", "\x1B[39m"],
158
- bgBlack: ["\x1B[40m", "\x1B[49m"],
159
- bgRed: ["\x1B[41m", "\x1B[49m"],
160
- bgGreen: ["\x1B[42m", "\x1B[49m"],
161
- bgYellow: ["\x1B[43m", "\x1B[49m"],
162
- bgBlue: ["\x1B[44m", "\x1B[49m"],
163
- bgMagenta: ["\x1B[45m", "\x1B[49m"],
164
- bgCyan: ["\x1B[46m", "\x1B[49m"],
165
- bgWhite: ["\x1B[47m", "\x1B[49m"]
166
- };
167
- const colorsEntries = Object.entries(colorsMap);
168
- const string = (str) => String(str);
169
- string.open = "";
170
- string.close = "";
171
- const defaultColors = colorsEntries.reduce((acc, [key]) => {
172
- acc[key] = string;
173
- return acc;
174
- }, { isColorSupported: false });
175
- function getDefaultColors() {
176
- return { ...defaultColors };
177
- }
178
- function getColors() {
179
- return globalThis[SAFE_COLORS_SYMBOL] || defaultColors;
180
- }
181
- function createColors(isTTY = false) {
182
- const enabled = typeof process !== "undefined" && !("NO_COLOR" in process.env || process.argv.includes("--no-color")) && !("GITHUB_ACTIONS" in process.env) && ("FORCE_COLOR" in process.env || process.argv.includes("--color") || process.platform === "win32" || isTTY && process.env.TERM !== "dumb" || "CI" in process.env);
183
- const replaceClose = (string2, close, replace, index) => {
184
- const start = string2.substring(0, index) + replace;
185
- const end = string2.substring(index + close.length);
186
- const nextIndex = end.indexOf(close);
187
- return ~nextIndex ? start + replaceClose(end, close, replace, nextIndex) : start + end;
188
- };
189
- const formatter = (open, close, replace = open) => {
190
- const fn = (input) => {
191
- const string2 = String(input);
192
- const index = string2.indexOf(close, open.length);
193
- return ~index ? open + replaceClose(string2, close, replace, index) + close : open + string2 + close;
194
- };
195
- fn.open = open;
196
- fn.close = close;
197
- return fn;
198
- };
199
- const colorsObject = {
200
- isColorSupported: enabled,
201
- reset: enabled ? (s) => `\x1B[0m${s}\x1B[0m` : string
202
- };
203
- for (const [name, formatterArgs] of colorsEntries) {
204
- colorsObject[name] = enabled ? formatter(...formatterArgs) : string;
205
- }
206
- return colorsObject;
207
- }
208
- function setupColors(colors) {
209
- globalThis[SAFE_COLORS_SYMBOL] = colors;
210
- }
211
-
212
146
  function createSimpleStackTrace(options) {
213
147
  const { message = "error", stackTraceLimit = 1 } = options || {};
214
148
  const limit = Error.stackTraceLimit;
@@ -222,4 +156,192 @@ function createSimpleStackTrace(options) {
222
156
  return stackTrace;
223
157
  }
224
158
 
225
- export { SAFE_COLORS_SYMBOL, SAFE_TIMERS_SYMBOL, createColors, createSimpleStackTrace, format, getColors, getDefaultColors, getSafeTimers, inspect, objDisplay, setSafeTimers, setupColors, shuffle, stringify };
159
+ function normalizeWindowsPath(input = "") {
160
+ if (!input || !input.includes("\\")) {
161
+ return input;
162
+ }
163
+ return input.replace(/\\/g, "/");
164
+ }
165
+ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
166
+ function cwd() {
167
+ if (typeof process !== "undefined") {
168
+ return process.cwd().replace(/\\/g, "/");
169
+ }
170
+ return "/";
171
+ }
172
+ const resolve = function(...arguments_) {
173
+ arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
174
+ let resolvedPath = "";
175
+ let resolvedAbsolute = false;
176
+ for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
177
+ const path = index >= 0 ? arguments_[index] : cwd();
178
+ if (!path || path.length === 0) {
179
+ continue;
180
+ }
181
+ resolvedPath = `${path}/${resolvedPath}`;
182
+ resolvedAbsolute = isAbsolute(path);
183
+ }
184
+ resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
185
+ if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
186
+ return `/${resolvedPath}`;
187
+ }
188
+ return resolvedPath.length > 0 ? resolvedPath : ".";
189
+ };
190
+ function normalizeString(path, allowAboveRoot) {
191
+ let res = "";
192
+ let lastSegmentLength = 0;
193
+ let lastSlash = -1;
194
+ let dots = 0;
195
+ let char = null;
196
+ for (let index = 0; index <= path.length; ++index) {
197
+ if (index < path.length) {
198
+ char = path[index];
199
+ } else if (char === "/") {
200
+ break;
201
+ } else {
202
+ char = "/";
203
+ }
204
+ if (char === "/") {
205
+ if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
206
+ if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
207
+ if (res.length > 2) {
208
+ const lastSlashIndex = res.lastIndexOf("/");
209
+ if (lastSlashIndex === -1) {
210
+ res = "";
211
+ lastSegmentLength = 0;
212
+ } else {
213
+ res = res.slice(0, lastSlashIndex);
214
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
215
+ }
216
+ lastSlash = index;
217
+ dots = 0;
218
+ continue;
219
+ } else if (res.length > 0) {
220
+ res = "";
221
+ lastSegmentLength = 0;
222
+ lastSlash = index;
223
+ dots = 0;
224
+ continue;
225
+ }
226
+ }
227
+ if (allowAboveRoot) {
228
+ res += res.length > 0 ? "/.." : "..";
229
+ lastSegmentLength = 2;
230
+ }
231
+ } else {
232
+ if (res.length > 0) {
233
+ res += `/${path.slice(lastSlash + 1, index)}`;
234
+ } else {
235
+ res = path.slice(lastSlash + 1, index);
236
+ }
237
+ lastSegmentLength = index - lastSlash - 1;
238
+ }
239
+ lastSlash = index;
240
+ dots = 0;
241
+ } else if (char === "." && dots !== -1) {
242
+ ++dots;
243
+ } else {
244
+ dots = -1;
245
+ }
246
+ }
247
+ return res;
248
+ }
249
+ const isAbsolute = function(p) {
250
+ return _IS_ABSOLUTE_RE.test(p);
251
+ };
252
+
253
+ const lineSplitRE = /\r?\n/;
254
+ const stackIgnorePatterns = [
255
+ "node:internal",
256
+ /\/packages\/\w+\/dist\//,
257
+ /\/@vitest\/\w+\/dist\//,
258
+ "/vitest/dist/",
259
+ "/vitest/src/",
260
+ "/vite-node/dist/",
261
+ "/vite-node/src/",
262
+ "/node_modules/chai/",
263
+ "/node_modules/tinypool/",
264
+ "/node_modules/tinyspy/"
265
+ ];
266
+ function extractLocation(urlLike) {
267
+ if (!urlLike.includes(":"))
268
+ return [urlLike];
269
+ const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
270
+ const parts = regExp.exec(urlLike.replace(/[()]/g, ""));
271
+ if (!parts)
272
+ return [urlLike];
273
+ return [parts[1], parts[2] || void 0, parts[3] || void 0];
274
+ }
275
+ function parseSingleStack(raw) {
276
+ let line = raw.trim();
277
+ if (line.includes("(eval "))
278
+ line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
279
+ let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
280
+ const location = sanitizedLine.match(/ (\(.+\)$)/);
281
+ sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
282
+ const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
283
+ let method = location && sanitizedLine || "";
284
+ let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
285
+ if (!file || !lineNumber || !columnNumber)
286
+ return null;
287
+ if (method.startsWith("async "))
288
+ method = method.slice(6);
289
+ if (file.startsWith("file://"))
290
+ file = file.slice(7);
291
+ file = resolve(file);
292
+ return {
293
+ method,
294
+ file,
295
+ line: parseInt(lineNumber),
296
+ column: parseInt(columnNumber)
297
+ };
298
+ }
299
+ function parseStacktrace(stack, ignore = stackIgnorePatterns) {
300
+ const stackFrames = stack.split("\n").map((raw) => {
301
+ const stack2 = parseSingleStack(raw);
302
+ if (!stack2 || ignore.length && ignore.some((p) => stack2.file.match(p)))
303
+ return null;
304
+ return stack2;
305
+ }).filter(notNullish);
306
+ return stackFrames;
307
+ }
308
+ function parseErrorStacktrace(e, ignore = stackIgnorePatterns) {
309
+ if (!e || isPrimitive(e))
310
+ return [];
311
+ if (e.stacks)
312
+ return e.stacks;
313
+ const stackStr = e.stack || e.stackStr || "";
314
+ const stackFrames = parseStacktrace(stackStr, ignore);
315
+ e.stacks = stackFrames;
316
+ return stackFrames;
317
+ }
318
+ function positionToOffset(source, lineNumber, columnNumber) {
319
+ const lines = source.split(lineSplitRE);
320
+ const nl = /\r\n/.test(source) ? 2 : 1;
321
+ let start = 0;
322
+ if (lineNumber > lines.length)
323
+ return source.length;
324
+ for (let i = 0; i < lineNumber - 1; i++)
325
+ start += lines[i].length + nl;
326
+ return start + columnNumber;
327
+ }
328
+ function offsetToLineNumber(source, offset) {
329
+ if (offset > source.length) {
330
+ throw new Error(
331
+ `offset is longer than source length! offset ${offset} > length ${source.length}`
332
+ );
333
+ }
334
+ const lines = source.split(lineSplitRE);
335
+ const nl = /\r\n/.test(source) ? 2 : 1;
336
+ let counted = 0;
337
+ let line = 0;
338
+ for (; line < lines.length; line++) {
339
+ const lineLength = lines[line].length + nl;
340
+ if (counted + lineLength >= offset)
341
+ break;
342
+ counted += lineLength;
343
+ }
344
+ return line + 1;
345
+ }
346
+
347
+ export { SAFE_TIMERS_SYMBOL, createSimpleStackTrace, format, getSafeTimers, isPrimitive, lineSplitRE, loupeInspect, notNullish, objDisplay, offsetToLineNumber, parseErrorStacktrace, parseSingleStack, parseStacktrace, positionToOffset, setSafeTimers, shuffle, stringify, utilInspect };
package/dist/types.d.ts CHANGED
@@ -14,5 +14,25 @@ type MutableArray<T extends readonly any[]> = {
14
14
  interface Constructable {
15
15
  new (...args: any[]): any;
16
16
  }
17
+ interface ParsedStack {
18
+ method: string;
19
+ file: string;
20
+ line: number;
21
+ column: number;
22
+ }
23
+ interface ErrorWithDiff extends Error {
24
+ name: string;
25
+ nameStr?: string;
26
+ stack?: string;
27
+ stackStr?: string;
28
+ stacks?: ParsedStack[];
29
+ showDiff?: boolean;
30
+ actual?: any;
31
+ expected?: any;
32
+ operator?: string;
33
+ type?: string;
34
+ frame?: string;
35
+ diff?: string;
36
+ }
17
37
 
18
- export { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, MergeInsertions, MutableArray, Nullable };
38
+ export { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, ErrorWithDiff, MergeInsertions, MutableArray, Nullable, ParsedStack };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/utils",
3
3
  "type": "module",
4
- "version": "0.29.7",
4
+ "version": "0.30.0",
5
5
  "description": "Shared Vitest utility functions",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -32,14 +32,10 @@
32
32
  "dist"
33
33
  ],
34
34
  "dependencies": {
35
- "cli-truncate": "^3.1.0",
36
- "diff": "^5.1.0",
35
+ "concordance": "^5.0.4",
37
36
  "loupe": "^2.3.6",
38
37
  "pretty-format": "^27.5.1"
39
38
  },
40
- "devDependencies": {
41
- "@types/diff": "^5.0.2"
42
- },
43
39
  "scripts": {
44
40
  "build": "rimraf dist && rollup -c",
45
41
  "dev": "rollup -c --watch"