dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- New /developers/ page with API docs, SDKs, pricing, use cases - OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics - Case study: Glasskiosken i Flatenbadet — complete ROI analysis - Updated /order/ with recurring missions and frequency dropdown
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-Present Vitest Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# @vitest/expect
|
||||
|
||||
Jest's expect matchers as a Chai plugin.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import * as chai from 'chai'
|
||||
import { JestAsymmetricMatchers, JestChaiExpect, JestExtend } from '@vitest/expect'
|
||||
|
||||
// allows using expect.extend instead of chai.use to extend plugins
|
||||
chai.use(JestExtend)
|
||||
// adds all jest matchers to expect
|
||||
chai.use(JestChaiExpect)
|
||||
// adds asymmetric matchers like stringContaining, objectContaining
|
||||
chai.use(JestAsymmetricMatchers)
|
||||
```
|
||||
+1968
@@ -0,0 +1,1968 @@
|
||||
// Type definitions for chai 4.3
|
||||
// Project: http://chaijs.com/
|
||||
// Definitions by: Bart van der Schoor <https://github.com/Bartvds>
|
||||
// Andrew Brown <https://github.com/AGBrown>
|
||||
// Olivier Chevet <https://github.com/olivr70>
|
||||
// Matt Wistrand <https://github.com/mwistrand>
|
||||
// Shaun Luttin <https://github.com/shaunluttin>
|
||||
// Satana Charuwichitratana <https://github.com/micksatana>
|
||||
// Erik Schierboom <https://github.com/ErikSchierboom>
|
||||
// Bogdan Paranytsia <https://github.com/bparan>
|
||||
// CXuesong <https://github.com/CXuesong>
|
||||
// Joey Kilpatrick <https://github.com/joeykilpatrick>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 3.0
|
||||
|
||||
declare namespace Chai {
|
||||
type Message = string | (() => string);
|
||||
type ObjectProperty = string | symbol | number;
|
||||
|
||||
interface PathInfo {
|
||||
parent: object;
|
||||
name: string;
|
||||
value?: any;
|
||||
exists: boolean;
|
||||
}
|
||||
|
||||
interface ErrorConstructor {
|
||||
new(...args: any[]): Error;
|
||||
}
|
||||
|
||||
interface ChaiUtils {
|
||||
addChainableMethod(
|
||||
// object to define the method on, e.g. chai.Assertion.prototype
|
||||
ctx: object,
|
||||
// method name
|
||||
name: string,
|
||||
// method itself; any arguments
|
||||
method: (...args: any[]) => void,
|
||||
// called when property is accessed
|
||||
chainingBehavior?: () => void,
|
||||
): void;
|
||||
overwriteChainableMethod(
|
||||
ctx: object,
|
||||
name: string,
|
||||
method: (...args: any[]) => void,
|
||||
chainingBehavior?: () => void,
|
||||
): void;
|
||||
addLengthGuard(
|
||||
fn: Function,
|
||||
assertionName: string,
|
||||
isChainable: boolean,
|
||||
): void;
|
||||
addMethod(ctx: object, name: string, method: Function): void;
|
||||
addProperty(ctx: object, name: string, getter: () => any): void;
|
||||
overwriteMethod(ctx: object, name: string, method: Function): void;
|
||||
overwriteProperty(ctx: object, name: string, getter: () => any): void;
|
||||
compareByInspect(a: object, b: object): -1 | 1;
|
||||
expectTypes(obj: object, types: string[]): void;
|
||||
flag(obj: object, key: string, value?: any): any;
|
||||
getActual(obj: object, args: AssertionArgs): any;
|
||||
getProperties(obj: object): string[];
|
||||
getEnumerableProperties(obj: object): string[];
|
||||
getOwnEnumerablePropertySymbols(obj: object): symbol[];
|
||||
getOwnEnumerableProperties(obj: object): Array<string | symbol>;
|
||||
getMessage(errorLike: Error | string): string;
|
||||
getMessage(obj: any, args: AssertionArgs): string;
|
||||
inspect(obj: any, showHidden?: boolean, depth?: number, colors?: boolean): string;
|
||||
isProxyEnabled(): boolean;
|
||||
objDisplay(obj: object): void;
|
||||
proxify(obj: object, nonChainableMethodName: string): object;
|
||||
test(obj: object, args: AssertionArgs): boolean;
|
||||
transferFlags(assertion: Assertion, obj: object, includeAll?: boolean): void;
|
||||
compatibleInstance(thrown: Error, errorLike: Error | ErrorConstructor): boolean;
|
||||
compatibleConstructor(thrown: Error, errorLike: Error | ErrorConstructor): boolean;
|
||||
compatibleMessage(thrown: Error, errMatcher: string | RegExp): boolean;
|
||||
getConstructorName(constructorFn: Function): string;
|
||||
getFuncName(constructorFn: Function): string | null;
|
||||
|
||||
// Reexports from pathval:
|
||||
hasProperty(obj: object | undefined | null, name: ObjectProperty): boolean;
|
||||
getPathInfo(obj: object, path: string): PathInfo;
|
||||
getPathValue(obj: object, path: string): object | undefined;
|
||||
}
|
||||
|
||||
type ChaiPlugin = (chai: ChaiStatic, utils: ChaiUtils) => void;
|
||||
|
||||
interface ChaiStatic {
|
||||
expect: ExpectStatic;
|
||||
should(): Should;
|
||||
/**
|
||||
* Provides a way to extend the internals of Chai
|
||||
*/
|
||||
use(fn: ChaiPlugin): ChaiStatic;
|
||||
util: ChaiUtils;
|
||||
assert: AssertStatic;
|
||||
config: Config;
|
||||
Assertion: AssertionStatic;
|
||||
AssertionError: typeof AssertionError;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface ExpectStatic {
|
||||
(val: any, message?: string): Assertion;
|
||||
fail(message?: string): never;
|
||||
fail(actual: any, expected: any, message?: string, operator?: Operator): never;
|
||||
}
|
||||
|
||||
export interface AssertStatic extends Assert {
|
||||
}
|
||||
|
||||
// chai.Assertion.prototype.assert arguments
|
||||
type AssertionArgs = [
|
||||
// 'expression to be tested'
|
||||
// This parameter is unused and the docs list its type as
|
||||
// 'Philosophical', which is mentioned nowhere else in the source. Do
|
||||
// with that what you will!
|
||||
any,
|
||||
Message, // message if value fails
|
||||
Message, // message if negated value fails
|
||||
any, // expected value
|
||||
any?, // actual value
|
||||
boolean?, // showDiff
|
||||
];
|
||||
|
||||
export interface AssertionPrototype {
|
||||
assert(...args: AssertionArgs): void;
|
||||
_obj: any;
|
||||
}
|
||||
|
||||
export interface AssertionStatic extends AssertionPrototype {
|
||||
prototype: AssertionPrototype;
|
||||
|
||||
new(target: any, message?: string, ssfi?: Function, lockSsfi?: boolean): Assertion;
|
||||
|
||||
// Deprecated properties:
|
||||
includeStack: boolean;
|
||||
showDiff: boolean;
|
||||
|
||||
// Partials of functions on ChaiUtils:
|
||||
addProperty(name: string, getter: (this: AssertionStatic) => any): void;
|
||||
addMethod(name: string, method: (this: AssertionStatic, ...args: any[]) => any): void;
|
||||
addChainableMethod(
|
||||
name: string,
|
||||
method: (this: AssertionStatic, ...args: any[]) => void,
|
||||
chainingBehavior?: () => void,
|
||||
): void;
|
||||
overwriteProperty(name: string, getter: (this: AssertionStatic) => any): void;
|
||||
overwriteMethod(name: string, method: (this: AssertionStatic, ...args: any[]) => any): void;
|
||||
overwriteChainableMethod(
|
||||
name: string,
|
||||
method: (this: AssertionStatic, ...args: any[]) => void,
|
||||
chainingBehavior?: () => void,
|
||||
): void;
|
||||
}
|
||||
|
||||
export type Operator = string; // "==" | "===" | ">" | ">=" | "<" | "<=" | "!=" | "!==";
|
||||
|
||||
export type OperatorComparable = boolean | null | number | string | undefined | Date;
|
||||
|
||||
interface ShouldAssertion {
|
||||
equal(value1: any, value2: any, message?: string): void;
|
||||
Throw: ShouldThrow;
|
||||
throw: ShouldThrow;
|
||||
exist(value: any, message?: string): void;
|
||||
}
|
||||
|
||||
interface Should extends ShouldAssertion {
|
||||
not: ShouldAssertion;
|
||||
fail(message?: string): never;
|
||||
fail(actual: any, expected: any, message?: string, operator?: Operator): never;
|
||||
}
|
||||
|
||||
interface ShouldThrow {
|
||||
(actual: Function, expected?: string | RegExp, message?: string): void;
|
||||
(actual: Function, constructor: Error | Function, expected?: string | RegExp, message?: string): void;
|
||||
}
|
||||
|
||||
interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
|
||||
not: Assertion;
|
||||
deep: Deep;
|
||||
ordered: Ordered;
|
||||
nested: Nested;
|
||||
own: Own;
|
||||
any: KeyFilter;
|
||||
all: KeyFilter;
|
||||
a: Assertion;
|
||||
an: Assertion;
|
||||
include: Include;
|
||||
includes: Include;
|
||||
contain: Include;
|
||||
contains: Include;
|
||||
ok: Assertion;
|
||||
true: Assertion;
|
||||
false: Assertion;
|
||||
null: Assertion;
|
||||
undefined: Assertion;
|
||||
NaN: Assertion;
|
||||
exist: Assertion;
|
||||
empty: Assertion;
|
||||
arguments: Assertion;
|
||||
Arguments: Assertion;
|
||||
finite: Assertion;
|
||||
equal: Equal;
|
||||
equals: Equal;
|
||||
eq: Equal;
|
||||
eql: Equal;
|
||||
eqls: Equal;
|
||||
property: Property;
|
||||
ownProperty: Property;
|
||||
haveOwnProperty: Property;
|
||||
ownPropertyDescriptor: OwnPropertyDescriptor;
|
||||
haveOwnPropertyDescriptor: OwnPropertyDescriptor;
|
||||
length: Length;
|
||||
lengthOf: Length;
|
||||
match: Match;
|
||||
matches: Match;
|
||||
string(string: string, message?: string): Assertion;
|
||||
keys: Keys;
|
||||
key(string: string): Assertion;
|
||||
throw: Throw;
|
||||
throws: Throw;
|
||||
Throw: Throw;
|
||||
respondTo: RespondTo;
|
||||
respondsTo: RespondTo;
|
||||
itself: Assertion;
|
||||
satisfy: Satisfy;
|
||||
satisfies: Satisfy;
|
||||
closeTo: CloseTo;
|
||||
approximately: CloseTo;
|
||||
members: Members;
|
||||
increase: PropertyChange;
|
||||
increases: PropertyChange;
|
||||
decrease: PropertyChange;
|
||||
decreases: PropertyChange;
|
||||
change: PropertyChange;
|
||||
changes: PropertyChange;
|
||||
extensible: Assertion;
|
||||
sealed: Assertion;
|
||||
frozen: Assertion;
|
||||
oneOf: OneOf;
|
||||
}
|
||||
|
||||
interface LanguageChains {
|
||||
to: Assertion;
|
||||
be: Assertion;
|
||||
been: Assertion;
|
||||
is: Assertion;
|
||||
that: Assertion;
|
||||
which: Assertion;
|
||||
and: Assertion;
|
||||
has: Assertion;
|
||||
have: Assertion;
|
||||
with: Assertion;
|
||||
at: Assertion;
|
||||
of: Assertion;
|
||||
same: Assertion;
|
||||
but: Assertion;
|
||||
does: Assertion;
|
||||
}
|
||||
|
||||
interface NumericComparison {
|
||||
above: NumberComparer;
|
||||
gt: NumberComparer;
|
||||
greaterThan: NumberComparer;
|
||||
least: NumberComparer;
|
||||
gte: NumberComparer;
|
||||
greaterThanOrEqual: NumberComparer;
|
||||
below: NumberComparer;
|
||||
lt: NumberComparer;
|
||||
lessThan: NumberComparer;
|
||||
most: NumberComparer;
|
||||
lte: NumberComparer;
|
||||
lessThanOrEqual: NumberComparer;
|
||||
within(start: number, finish: number, message?: string): Assertion;
|
||||
within(start: Date, finish: Date, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface NumberComparer {
|
||||
(value: number | Date, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface TypeComparison {
|
||||
(type: string, message?: string): Assertion;
|
||||
instanceof: InstanceOf;
|
||||
instanceOf: InstanceOf;
|
||||
}
|
||||
|
||||
interface InstanceOf {
|
||||
(constructor: any, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface CloseTo {
|
||||
(expected: number, delta: number, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Nested {
|
||||
include: Include;
|
||||
includes: Include;
|
||||
contain: Include;
|
||||
contains: Include;
|
||||
property: Property;
|
||||
members: Members;
|
||||
}
|
||||
|
||||
interface Own {
|
||||
include: Include;
|
||||
includes: Include;
|
||||
contain: Include;
|
||||
contains: Include;
|
||||
property: Property;
|
||||
}
|
||||
|
||||
interface Deep extends KeyFilter {
|
||||
be: Assertion;
|
||||
equal: Equal;
|
||||
equals: Equal;
|
||||
eq: Equal;
|
||||
include: Include;
|
||||
includes: Include;
|
||||
contain: Include;
|
||||
contains: Include;
|
||||
property: Property;
|
||||
ordered: Ordered;
|
||||
nested: Nested;
|
||||
oneOf: OneOf;
|
||||
own: Own;
|
||||
}
|
||||
|
||||
interface Ordered {
|
||||
members: Members;
|
||||
}
|
||||
|
||||
interface KeyFilter {
|
||||
keys: Keys;
|
||||
members: Members;
|
||||
}
|
||||
|
||||
interface Equal {
|
||||
(value: any, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Property {
|
||||
(name: string | symbol, value: any, message?: string): Assertion;
|
||||
(name: string | symbol, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface OwnPropertyDescriptor {
|
||||
(name: string | symbol, descriptor: PropertyDescriptor, message?: string): Assertion;
|
||||
(name: string | symbol, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Length extends LanguageChains, NumericComparison {
|
||||
(length: number, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Include {
|
||||
(value: any, message?: string): Assertion;
|
||||
keys: Keys;
|
||||
deep: Deep;
|
||||
ordered: Ordered;
|
||||
members: Members;
|
||||
any: KeyFilter;
|
||||
all: KeyFilter;
|
||||
oneOf: OneOf;
|
||||
}
|
||||
|
||||
interface OneOf {
|
||||
(list: ReadonlyArray<unknown>, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Match {
|
||||
(regexp: RegExp, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Keys {
|
||||
(...keys: string[]): Assertion;
|
||||
(keys: ReadonlyArray<any> | Object): Assertion;
|
||||
}
|
||||
|
||||
interface Throw {
|
||||
(expected?: string | RegExp, message?: string): Assertion;
|
||||
(constructor: Error | Function, expected?: string | RegExp, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface RespondTo {
|
||||
(method: string, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Satisfy {
|
||||
(matcher: Function, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface Members {
|
||||
(set: ReadonlyArray<any>, message?: string): Assertion;
|
||||
}
|
||||
|
||||
interface PropertyChange {
|
||||
(object: Object, property?: string, message?: string): DeltaAssertion;
|
||||
}
|
||||
|
||||
interface DeltaAssertion extends Assertion {
|
||||
by(delta: number, msg?: string): Assertion;
|
||||
}
|
||||
|
||||
export interface Assert {
|
||||
/**
|
||||
* @param expression Expression to test for truthiness.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
(expression: any, message?: string): asserts expression;
|
||||
|
||||
/**
|
||||
* Throws a failure.
|
||||
*
|
||||
* @param message Message to display on error.
|
||||
* @remarks Node.js assert module-compatible.
|
||||
*/
|
||||
fail(message?: string): never;
|
||||
|
||||
/**
|
||||
* Throws a failure.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
* @param operator Comparison operator, if not strict equality.
|
||||
* @remarks Node.js assert module-compatible.
|
||||
*/
|
||||
fail<T>(actual: T, expected: T, message?: string, operator?: Operator): never;
|
||||
|
||||
/**
|
||||
* Asserts that object is truthy.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isOk<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is truthy.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
ok<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is falsy.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotOk<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is falsy.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notOk<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts non-strict equality (==) of actual and expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
equal<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts non-strict inequality (!=) of actual and expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts strict equality (===) of actual and expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
strictEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts strict inequality (!==) of actual and expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notStrictEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that actual is deeply equal to expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that actual is not deeply equal to expected.
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Alias to deepEqual
|
||||
*
|
||||
* T Type of the objects.
|
||||
* @param actual Actual value.
|
||||
* @param expected Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepStrictEqual<T>(actual: T, expected: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts valueToCheck is strictly greater than (>) valueToBeAbove.
|
||||
*
|
||||
* @param valueToCheck Actual value.
|
||||
* @param valueToBeAbove Minimum Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast.
|
||||
*
|
||||
* @param valueToCheck Actual value.
|
||||
* @param valueToBeAtLeast Minimum Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts valueToCheck is strictly less than (<) valueToBeBelow.
|
||||
*
|
||||
* @param valueToCheck Actual value.
|
||||
* @param valueToBeBelow Minimum Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts valueToCheck is less than or equal to (<=) valueToBeAtMost.
|
||||
*
|
||||
* @param valueToCheck Actual value.
|
||||
* @param valueToBeAtMost Minimum Potential expected value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is true.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isTrue<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is false.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isFalse<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not true.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotTrue<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not false.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotFalse<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is null.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNull<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not null.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotNull<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is NaN.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNaN<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not NaN.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotNaN<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target is neither null nor undefined.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
exists<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target is either null or undefined.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notExists<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is undefined.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isUndefined<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not undefined.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isDefined<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is a function.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isFunction<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not a function.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotFunction<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is an object of type 'Object'
|
||||
* (as revealed by Object.prototype.toString).
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
* @remarks The assertion does not match subclassed objects.
|
||||
*/
|
||||
isObject<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not an object of type 'Object'
|
||||
* (as revealed by Object.prototype.toString).
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotObject<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is an array.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isArray<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not an array.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotArray<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is a string.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isString<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not a string.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotString<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is a number.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNumber<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not a number.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotNumber<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is a finite number.
|
||||
* Unlike `.isNumber`, this will fail for `NaN` and `Infinity`.
|
||||
*
|
||||
* T Type of value
|
||||
* @param value Actual value
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isFinite<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is a boolean.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isBoolean<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not a boolean.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotBoolean<T>(value: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value's type is name, as determined by Object.prototype.toString.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param name Potential expected type name of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
typeOf<T>(value: T, name: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value's type is not name, as determined by Object.prototype.toString.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param name Potential expected type name of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notTypeOf<T>(value: T, name: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is an instance of constructor.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param constructor Potential expected contructor of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
instanceOf<T>(value: T, constructor: Function, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value is not an instance of constructor.
|
||||
*
|
||||
* T Type of value.
|
||||
* @param value Actual value.
|
||||
* @param constructor Potential expected contructor of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notInstanceOf<T>(value: T, type: Function, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle.
|
||||
*
|
||||
* @param haystack Container string.
|
||||
* @param needle Potential substring of haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
include(haystack: string, needle: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack Container array, set or map.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
include<T>(
|
||||
haystack: ReadonlyArray<T> | ReadonlySet<T> | ReadonlyMap<any, T>,
|
||||
needle: T,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack WeakSet container.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
include<T extends object>(haystack: WeakSet<T>, needle: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle.
|
||||
*
|
||||
* T Type of haystack.
|
||||
* @param haystack Object.
|
||||
* @param needle Potential subset of the haystack's properties.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
include<T>(haystack: T, needle: Partial<T>, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle.
|
||||
*
|
||||
* @param haystack Container string.
|
||||
* @param needle Potential substring of haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notInclude(haystack: string, needle: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack Container array, set or map.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notInclude<T>(
|
||||
haystack: ReadonlyArray<T> | ReadonlySet<T> | ReadonlyMap<any, T>,
|
||||
needle: T,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack WeakSet container.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notInclude<T extends object>(haystack: WeakSet<T>, needle: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle.
|
||||
*
|
||||
* T Type of haystack.
|
||||
* @param haystack Object.
|
||||
* @param needle Potential subset of the haystack's properties.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notInclude<T>(haystack: T, needle: Partial<T>, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle. Deep equality is used.
|
||||
*
|
||||
* @param haystack Container string.
|
||||
* @param needle Potential substring of haystack.
|
||||
* @param message Message to display on error.
|
||||
*
|
||||
* @deprecated Does not have any effect on string. Use {@link Assert#include} instead.
|
||||
*/
|
||||
deepInclude(haystack: string, needle: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack includes needle. Deep equality is used.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack Container array, set or map.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepInclude<T>(
|
||||
haystack: ReadonlyArray<T> | ReadonlySet<T> | ReadonlyMap<any, T>,
|
||||
needle: T,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle.
|
||||
*
|
||||
* T Type of haystack.
|
||||
* @param haystack Object.
|
||||
* @param needle Potential subset of the haystack's properties.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepInclude<T>(haystack: T, needle: T extends WeakSet<any> ? never : Partial<T>, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle. Deep equality is used.
|
||||
*
|
||||
* @param haystack Container string.
|
||||
* @param needle Potential substring of haystack.
|
||||
* @param message Message to display on error.
|
||||
*
|
||||
* @deprecated Does not have any effect on string. Use {@link Assert#notInclude} instead.
|
||||
*/
|
||||
notDeepInclude(haystack: string, needle: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle. Deep equality is used.
|
||||
*
|
||||
* T Type of values in haystack.
|
||||
* @param haystack Container array, set or map.
|
||||
* @param needle Potential value contained in haystack.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepInclude<T>(
|
||||
haystack: ReadonlyArray<T> | ReadonlySet<T> | ReadonlyMap<any, T>,
|
||||
needle: T,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that haystack does not includes needle. Deep equality is used.
|
||||
*
|
||||
* T Type of haystack.
|
||||
* @param haystack Object.
|
||||
* @param needle Potential subset of the haystack's properties.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepInclude<T>(haystack: T, needle: T extends WeakSet<any> ? never : Partial<T>, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the inclusion of a subset of properties in an object.
|
||||
*
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.Asserts that ‘haystack’ includes ‘needle’.
|
||||
* Can be used to assert the inclusion of a subset of properties in an object.
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
nestedInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ does not include ‘needle’. Can be used to assert the absence of a subset of properties in an object.
|
||||
*
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.Asserts that ‘haystack’ includes ‘needle’.
|
||||
* Can be used to assert the inclusion of a subset of properties in an object.
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notNestedInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the inclusion of a subset of properties in an object while checking for deep equality
|
||||
*
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.Asserts that ‘haystack’ includes ‘needle’.
|
||||
* Can be used to assert the inclusion of a subset of properties in an object.
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepNestedInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ does not include ‘needle’. Can be used to assert the absence of a subset of properties in an object while checking for deep equality.
|
||||
*
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.Asserts that ‘haystack’ includes ‘needle’.
|
||||
* Can be used to assert the inclusion of a subset of properties in an object.
|
||||
* Enables the use of dot- and bracket-notation for referencing nested properties.
|
||||
* ‘[]’ and ‘.’ in property names can be escaped using double backslashes.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepNestedInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
ownInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notOwnInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties and checking for deep
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepOwnInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that ‘haystack’ includes ‘needle’. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties and checking for deep equality.
|
||||
*
|
||||
* @param haystack
|
||||
* @param needle
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepOwnInclude(haystack: any, needle: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value matches the regular expression regexp.
|
||||
*
|
||||
* @param value Actual value.
|
||||
* @param regexp Potential match of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
match(value: string, regexp: RegExp, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that value does not match the regular expression regexp.
|
||||
*
|
||||
* @param value Actual value.
|
||||
* @param regexp Potential match of value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notMatch(expected: any, regexp: RegExp, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
property<T>(object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notProperty<T>(object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property, which can be a string
|
||||
* using dot- and bracket-notation for deep reference.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepProperty<T>(object: T, property: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object does not have a property named by property, which can be a
|
||||
* string using dot- and bracket-notation for deep reference.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepProperty<T>(object: T, property: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property with value given by value.
|
||||
*
|
||||
* T Type of object.
|
||||
* V Type of value.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param value Potential expected property value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
propertyVal<T, V>(object: T, property: string, /* keyof T */ value: V, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property with value given by value.
|
||||
*
|
||||
* T Type of object.
|
||||
* V Type of value.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param value Potential expected property value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notPropertyVal<T, V>(object: T, property: string, /* keyof T */ value: V, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property, which can be a string
|
||||
* using dot- and bracket-notation for deep reference.
|
||||
*
|
||||
* T Type of object.
|
||||
* V Type of value.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param value Potential expected property value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepPropertyVal<T, V>(object: T, property: string, value: V, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object does not have a property named by property, which can be a
|
||||
* string using dot- and bracket-notation for deep reference.
|
||||
*
|
||||
* T Type of object.
|
||||
* V Type of value.
|
||||
* @param object Container object.
|
||||
* @param property Potential contained property of object.
|
||||
* @param value Potential expected property value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepPropertyVal<T, V>(object: T, property: string, value: V, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a length property with the expected value.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Container object.
|
||||
* @param length Potential expected length of object.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
lengthOf<T extends { readonly length?: number | undefined }>(object: T, length: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param ignored Ignored parameter.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errorLike Expected error constructor or error instance.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
throw(
|
||||
fn: () => void,
|
||||
errorLike?: ErrorConstructor | Error | null,
|
||||
errMsgMatcher?: RegExp | string | null,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param ignored Ignored parameter.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
throws(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errorLike Expected error constructor or error instance.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
throws(
|
||||
fn: () => void,
|
||||
errorLike?: ErrorConstructor | Error | null,
|
||||
errMsgMatcher?: RegExp | string | null,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param ignored Ignored parameter.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
Throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errorLike Expected error constructor or error instance.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
Throw(
|
||||
fn: () => void,
|
||||
errorLike?: ErrorConstructor | Error | null,
|
||||
errMsgMatcher?: RegExp | string | null,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will not throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param ignored Ignored parameter.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotThrow(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that fn will not throw an error.
|
||||
*
|
||||
* @param fn Function that may throw.
|
||||
* @param errorLike Expected error constructor or error instance.
|
||||
* @param errMsgMatcher Expected error message matcher.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotThrow(
|
||||
fn: () => void,
|
||||
errorLike?: ErrorConstructor | Error | null,
|
||||
errMsgMatcher?: RegExp | string | null,
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Compares two values using operator.
|
||||
*
|
||||
* @param val1 Left value during comparison.
|
||||
* @param operator Comparison operator.
|
||||
* @param val2 Right value during comparison.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target is equal to expected, to within a +/- delta range.
|
||||
*
|
||||
* @param actual Actual value
|
||||
* @param expected Potential expected value.
|
||||
* @param delta Maximum differenced between values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
closeTo(actual: number, expected: number, delta: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target is equal to expected, to within a +/- delta range.
|
||||
*
|
||||
* @param actual Actual value
|
||||
* @param expected Potential expected value.
|
||||
* @param delta Maximum differenced between values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
approximately(act: number, exp: number, delta: number, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 have the same members. Order is not take into account.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
sameMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 have the same members using deep equality checking.
|
||||
* Order is not take into account.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
sameDeepMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 have the same members in the same order.
|
||||
* Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
sameOrderedMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 don’t have the same members in the same order.
|
||||
* Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notSameOrderedMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 have the same members in the same order.
|
||||
* Uses a deep equality check.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
sameDeepOrderedMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that set1 and set2 don’t have the same members in the same order.
|
||||
* Uses a deep equality check.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param set1 Actual set of values.
|
||||
* @param set2 Potential expected set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notSameDeepOrderedMembers<T>(set1: T[], set2: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset is included in superset in the same order beginning with the first element in superset.
|
||||
* Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
includeOrderedMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset isn’t included in superset in the same order beginning with the first element in superset.
|
||||
* Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notIncludeOrderedMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset is included in superset in the same order beginning with the first element in superset.
|
||||
* Uses a deep equality check.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
includeDeepOrderedMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset isn’t included in superset in the same order beginning with the first element in superset.
|
||||
* Uses a deep equality check.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notIncludeDeepOrderedMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset is included in superset. Order is not take into account.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
includeMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset isn’t included in superset in any order.
|
||||
* Uses a strict equality check (===). Duplicates are ignored.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential not contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notIncludeMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that subset is included in superset using deep equality checking.
|
||||
* Order is not take into account.
|
||||
*
|
||||
* T Type of set values.
|
||||
* @param superset Actual set of values.
|
||||
* @param subset Potential contained set of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
includeDeepMembers<T>(superset: T[], subset: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that non-object, non-array value inList appears in the flat array list.
|
||||
*
|
||||
* T Type of list values.
|
||||
* @param inList Value expected to be in the list.
|
||||
* @param list List of values.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
oneOf<T>(inList: T, list: T[], message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function changes the value of a property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected to be modified.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
changes<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function does not change the value of a property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected not to be modified.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotChange<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function increases an object property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected to be increased.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
increases<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function does not increase an object property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected not to be increased.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotIncrease<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function decreases an object property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected to be decreased.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
decreases<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that a function does not decrease an object property.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param modifier Function to run.
|
||||
* @param object Container object.
|
||||
* @param property Property of object expected not to be decreased.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotDecrease<T>(modifier: Function, object: T, property: string, /* keyof T */ message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts if value is not a false value, and throws if it is a true value.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
* @remarks This is added to allow for chai to be a drop-in replacement for
|
||||
* Node’s assert class.
|
||||
*/
|
||||
ifError<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is extensible (can have new properties added to it).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isExtensible<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is extensible (can have new properties added to it).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
extensible<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not extensible.
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotExtensible<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not extensible.
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notExtensible<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is sealed (can have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isSealed<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is sealed (can have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
sealed<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not sealed.
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotSealed<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not sealed.
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notSealed<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is frozen (cannot have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isFrozen<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is frozen (cannot have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
frozen<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not frozen (cannot have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotFrozen<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object is not frozen (cannot have new properties added to it
|
||||
* and its existing properties cannot be removed).
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notFrozen<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target does not contain any values. For arrays and
|
||||
* strings, it checks the length property. For Map and Set instances, it
|
||||
* checks the size property. For non-function objects, it gets the count
|
||||
* of own enumerable string keys.
|
||||
*
|
||||
* T Type of object
|
||||
* @param object Actual value.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isEmpty<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that the target contains values. For arrays and strings, it checks
|
||||
* the length property. For Map and Set instances, it checks the size property.
|
||||
* For non-function objects, it gets the count of own enumerable string keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
isNotEmpty<T>(object: T, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has at least one of the `keys` provided.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
hasAnyKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has all and only all of the `keys` provided.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
hasAllKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has all of the `keys` provided but may have more keys not listed.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
containsAllKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has none of the `keys` provided.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotHaveAnyKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` does not have at least one of the `keys` provided.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotHaveAllKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has at least one of the `keys` provided.
|
||||
* Since Sets and Maps can have objects as keys you can use this assertion to perform
|
||||
* a deep comparison.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
hasAnyDeepKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` has all and only all of the `keys` provided.
|
||||
* Since Sets and Maps can have objects as keys you can use this assertion to perform
|
||||
* a deep comparison.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
hasAllDeepKeys<T>(object: T, keys: Array<Object | string> | { [key: string]: any }, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` contains all of the `keys` provided.
|
||||
* Since Sets and Maps can have objects as keys you can use this assertion to perform
|
||||
* a deep comparison.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
containsAllDeepKeys<T>(
|
||||
object: T,
|
||||
keys: Array<Object | string> | { [key: string]: any },
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` contains all of the `keys` provided.
|
||||
* Since Sets and Maps can have objects as keys you can use this assertion to perform
|
||||
* a deep comparison.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotHaveAnyDeepKeys<T>(
|
||||
object: T,
|
||||
keys: Array<Object | string> | { [key: string]: any },
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that `object` contains all of the `keys` provided.
|
||||
* Since Sets and Maps can have objects as keys you can use this assertion to perform
|
||||
* a deep comparison.
|
||||
* You can also provide a single object instead of a `keys` array and its keys
|
||||
* will be used as the expected set of keys.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param keys Keys to check
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
doesNotHaveAllDeepKeys<T>(
|
||||
object: T,
|
||||
keys: Array<Object | string> | { [key: string]: any },
|
||||
message?: string,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a direct or inherited property named by property,
|
||||
* which can be a string using dot- and bracket-notation for nested reference.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
nestedProperty<T>(object: T, property: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object does not have a property named by property,
|
||||
* which can be a string using dot- and bracket-notation for nested reference.
|
||||
* The property cannot exist on the object nor anywhere in its prototype chain.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notNestedProperty<T>(object: T, property: string, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property with value given by value.
|
||||
* property can use dot- and bracket-notation for nested reference. Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param value Value to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
nestedPropertyVal<T>(object: T, property: string, value: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object does not have a property named by property with value given by value.
|
||||
* property can use dot- and bracket-notation for nested reference. Uses a strict equality check (===).
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param value Value to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notNestedPropertyVal<T>(object: T, property: string, value: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object has a property named by property with a value given by value.
|
||||
* property can use dot- and bracket-notation for nested reference. Uses a deep equality check.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param value Value to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
deepNestedPropertyVal<T>(object: T, property: string, value: any, message?: string): void;
|
||||
|
||||
/**
|
||||
* Asserts that object does not have a property named by property with value given by value.
|
||||
* property can use dot- and bracket-notation for nested reference. Uses a deep equality check.
|
||||
*
|
||||
* T Type of object.
|
||||
* @param object Object to test.
|
||||
* @param property Property to test.
|
||||
* @param value Value to test.
|
||||
* @param message Message to display on error.
|
||||
*/
|
||||
notDeepNestedPropertyVal<T>(object: T, property: string, value: any, message?: string): void;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
/**
|
||||
* Default: false
|
||||
*/
|
||||
includeStack: boolean;
|
||||
|
||||
/**
|
||||
* Default: true
|
||||
*/
|
||||
showDiff: boolean;
|
||||
|
||||
/**
|
||||
* Default: 40
|
||||
*/
|
||||
truncateThreshold: number;
|
||||
|
||||
/**
|
||||
* Default: true
|
||||
*/
|
||||
useProxy: boolean;
|
||||
|
||||
/**
|
||||
* Default: ['then', 'catch', 'inspect', 'toJSON']
|
||||
*/
|
||||
proxyExcludedKeys: string[];
|
||||
}
|
||||
|
||||
export class AssertionError {
|
||||
constructor(message: string, _props?: any, ssf?: Function);
|
||||
name: string;
|
||||
message: string;
|
||||
showDiff: boolean;
|
||||
stack: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const chai: Chai.ChaiStatic;
|
||||
|
||||
declare module "chai" {
|
||||
export = chai;
|
||||
}
|
||||
|
||||
// interface Object {
|
||||
// should: Chai.Assertion;
|
||||
// }
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
import * as _vitest_utils from '@vitest/utils';
|
||||
import { stringify, Constructable } from '@vitest/utils';
|
||||
export { setupColors } from '@vitest/utils';
|
||||
import { diff } from '@vitest/utils/diff';
|
||||
export { DiffOptions } from '@vitest/utils/diff';
|
||||
|
||||
type Formatter = (input: string | number | null | undefined) => string;
|
||||
|
||||
declare function getMatcherUtils(): {
|
||||
EXPECTED_COLOR: _vitest_utils.ColorMethod;
|
||||
RECEIVED_COLOR: _vitest_utils.ColorMethod;
|
||||
INVERTED_COLOR: _vitest_utils.ColorMethod;
|
||||
BOLD_WEIGHT: _vitest_utils.ColorMethod;
|
||||
DIM_COLOR: _vitest_utils.ColorMethod;
|
||||
matcherHint: (matcherName: string, received?: string, expected?: string, options?: MatcherHintOptions) => string;
|
||||
printReceived: (object: unknown) => string;
|
||||
printExpected: (value: unknown) => string;
|
||||
};
|
||||
declare function addCustomEqualityTesters(newTesters: Array<Tester>): void;
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
type ChaiPlugin = Chai.ChaiPlugin;
|
||||
type Tester = (this: TesterContext, a: any, b: any, customTesters: Array<Tester>) => boolean | undefined;
|
||||
interface TesterContext {
|
||||
equals: (a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean) => boolean;
|
||||
}
|
||||
|
||||
interface MatcherHintOptions {
|
||||
comment?: string;
|
||||
expectedColor?: Formatter;
|
||||
isDirectExpectCall?: boolean;
|
||||
isNot?: boolean;
|
||||
promise?: string;
|
||||
receivedColor?: Formatter;
|
||||
secondArgument?: string;
|
||||
secondArgumentColor?: Formatter;
|
||||
}
|
||||
interface MatcherState {
|
||||
customTesters: Array<Tester>;
|
||||
assertionCalls: number;
|
||||
currentTestName?: string;
|
||||
dontThrow?: () => void;
|
||||
error?: Error;
|
||||
equals: (a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean) => boolean;
|
||||
expand?: boolean;
|
||||
expectedAssertionsNumber?: number | null;
|
||||
expectedAssertionsNumberErrorGen?: (() => Error) | null;
|
||||
isExpectingAssertions?: boolean;
|
||||
isExpectingAssertionsError?: Error | null;
|
||||
isNot: boolean;
|
||||
promise: string;
|
||||
suppressedErrors: Array<Error>;
|
||||
testPath?: string;
|
||||
utils: ReturnType<typeof getMatcherUtils> & {
|
||||
diff: typeof diff;
|
||||
stringify: typeof stringify;
|
||||
iterableEquality: Tester;
|
||||
subsetEquality: Tester;
|
||||
};
|
||||
soft?: boolean;
|
||||
}
|
||||
interface SyncExpectationResult {
|
||||
pass: boolean;
|
||||
message: () => string;
|
||||
actual?: any;
|
||||
expected?: any;
|
||||
}
|
||||
type AsyncExpectationResult = Promise<SyncExpectationResult>;
|
||||
type ExpectationResult = SyncExpectationResult | AsyncExpectationResult;
|
||||
interface RawMatcherFn<T extends MatcherState = MatcherState> {
|
||||
(this: T, received: any, expected: any, options?: any): ExpectationResult;
|
||||
}
|
||||
type MatchersObject<T extends MatcherState = MatcherState> = Record<string, RawMatcherFn<T>>;
|
||||
interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersContaining {
|
||||
<T>(actual: T, message?: string): Assertion<T>;
|
||||
unreachable: (message?: string) => never;
|
||||
soft: <T>(actual: T, message?: string) => Assertion<T>;
|
||||
extend: (expects: MatchersObject) => void;
|
||||
addEqualityTesters: (testers: Array<Tester>) => void;
|
||||
assertions: (expected: number) => void;
|
||||
hasAssertions: () => void;
|
||||
anything: () => any;
|
||||
any: (constructor: unknown) => any;
|
||||
getState: () => MatcherState;
|
||||
setState: (state: Partial<MatcherState>) => void;
|
||||
not: AsymmetricMatchersContaining;
|
||||
}
|
||||
interface AsymmetricMatchersContaining {
|
||||
stringContaining: (expected: string) => any;
|
||||
objectContaining: <T = any>(expected: T) => any;
|
||||
arrayContaining: <T = unknown>(expected: Array<T>) => any;
|
||||
stringMatching: (expected: string | RegExp) => any;
|
||||
closeTo: (expected: number, precision?: number) => any;
|
||||
}
|
||||
interface JestAssertion<T = any> extends jest.Matchers<void, T> {
|
||||
toEqual: <E>(expected: E) => void;
|
||||
toStrictEqual: <E>(expected: E) => void;
|
||||
toBe: <E>(expected: E) => void;
|
||||
toMatch: (expected: string | RegExp) => void;
|
||||
toMatchObject: <E extends {} | any[]>(expected: E) => void;
|
||||
toContain: <E>(item: E) => void;
|
||||
toContainEqual: <E>(item: E) => void;
|
||||
toBeTruthy: () => void;
|
||||
toBeFalsy: () => void;
|
||||
toBeGreaterThan: (num: number | bigint) => void;
|
||||
toBeGreaterThanOrEqual: (num: number | bigint) => void;
|
||||
toBeLessThan: (num: number | bigint) => void;
|
||||
toBeLessThanOrEqual: (num: number | bigint) => void;
|
||||
toBeNaN: () => void;
|
||||
toBeUndefined: () => void;
|
||||
toBeNull: () => void;
|
||||
toBeDefined: () => void;
|
||||
toBeInstanceOf: <E>(expected: E) => void;
|
||||
toBeCalledTimes: (times: number) => void;
|
||||
toHaveLength: (length: number) => void;
|
||||
toHaveProperty: <E>(property: string | (string | number)[], value?: E) => void;
|
||||
toBeCloseTo: (number: number, numDigits?: number) => void;
|
||||
toHaveBeenCalledTimes: (times: number) => void;
|
||||
toHaveBeenCalled: () => void;
|
||||
toBeCalled: () => void;
|
||||
toHaveBeenCalledWith: <E extends any[]>(...args: E) => void;
|
||||
toBeCalledWith: <E extends any[]>(...args: E) => void;
|
||||
toHaveBeenNthCalledWith: <E extends any[]>(n: number, ...args: E) => void;
|
||||
nthCalledWith: <E extends any[]>(nthCall: number, ...args: E) => void;
|
||||
toHaveBeenLastCalledWith: <E extends any[]>(...args: E) => void;
|
||||
lastCalledWith: <E extends any[]>(...args: E) => void;
|
||||
toThrow: (expected?: string | Constructable | RegExp | Error) => void;
|
||||
toThrowError: (expected?: string | Constructable | RegExp | Error) => void;
|
||||
toReturn: () => void;
|
||||
toHaveReturned: () => void;
|
||||
toReturnTimes: (times: number) => void;
|
||||
toHaveReturnedTimes: (times: number) => void;
|
||||
toReturnWith: <E>(value: E) => void;
|
||||
toHaveReturnedWith: <E>(value: E) => void;
|
||||
toHaveLastReturnedWith: <E>(value: E) => void;
|
||||
lastReturnedWith: <E>(value: E) => void;
|
||||
toHaveNthReturnedWith: <E>(nthCall: number, value: E) => void;
|
||||
nthReturnedWith: <E>(nthCall: number, value: E) => void;
|
||||
}
|
||||
type VitestAssertion<A, T> = {
|
||||
[K in keyof A]: A[K] extends Chai.Assertion ? Assertion<T> : A[K] extends (...args: any[]) => any ? A[K] : VitestAssertion<A[K], T>;
|
||||
} & ((type: string, message?: string) => Assertion);
|
||||
type Promisify<O> = {
|
||||
[K in keyof O]: O[K] extends (...args: infer A) => infer R ? O extends R ? Promisify<O[K]> : (...args: A) => Promise<R> : O[K];
|
||||
};
|
||||
interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAssertion<T> {
|
||||
toBeTypeOf: (expected: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => void;
|
||||
toHaveBeenCalledOnce: () => void;
|
||||
toSatisfy: <E>(matcher: (value: E) => boolean, message?: string) => void;
|
||||
resolves: Promisify<Assertion<T>>;
|
||||
rejects: Promisify<Assertion<T>>;
|
||||
}
|
||||
declare global {
|
||||
namespace jest {
|
||||
interface Matchers<R, T = {}> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AsymmetricMatcherInterface {
|
||||
asymmetricMatch: (other: unknown) => boolean;
|
||||
toString: () => string;
|
||||
getExpectedType?: () => string;
|
||||
toAsymmetricMatcher?: () => string;
|
||||
}
|
||||
declare abstract class AsymmetricMatcher<T, State extends MatcherState = MatcherState> implements AsymmetricMatcherInterface {
|
||||
protected sample: T;
|
||||
protected inverse: boolean;
|
||||
$$typeof: symbol;
|
||||
constructor(sample: T, inverse?: boolean);
|
||||
protected getMatcherContext(expect?: Chai.ExpectStatic): State;
|
||||
abstract asymmetricMatch(other: unknown): boolean;
|
||||
abstract toString(): string;
|
||||
getExpectedType?(): string;
|
||||
toAsymmetricMatcher?(): string;
|
||||
}
|
||||
declare class StringContaining extends AsymmetricMatcher<string> {
|
||||
constructor(sample: string, inverse?: boolean);
|
||||
asymmetricMatch(other: string): boolean;
|
||||
toString(): string;
|
||||
getExpectedType(): string;
|
||||
}
|
||||
declare class Anything extends AsymmetricMatcher<void> {
|
||||
asymmetricMatch(other: unknown): boolean;
|
||||
toString(): string;
|
||||
toAsymmetricMatcher(): string;
|
||||
}
|
||||
declare class ObjectContaining extends AsymmetricMatcher<Record<string, unknown>> {
|
||||
constructor(sample: Record<string, unknown>, inverse?: boolean);
|
||||
getPrototype(obj: object): any;
|
||||
hasProperty(obj: object | null, property: string): boolean;
|
||||
asymmetricMatch(other: any): boolean;
|
||||
toString(): string;
|
||||
getExpectedType(): string;
|
||||
}
|
||||
declare class ArrayContaining<T = unknown> extends AsymmetricMatcher<Array<T>> {
|
||||
constructor(sample: Array<T>, inverse?: boolean);
|
||||
asymmetricMatch(other: Array<T>): boolean;
|
||||
toString(): string;
|
||||
getExpectedType(): string;
|
||||
}
|
||||
declare class Any extends AsymmetricMatcher<any> {
|
||||
constructor(sample: unknown);
|
||||
fnNameFor(func: Function): string;
|
||||
asymmetricMatch(other: unknown): boolean;
|
||||
toString(): string;
|
||||
getExpectedType(): string;
|
||||
toAsymmetricMatcher(): string;
|
||||
}
|
||||
declare class StringMatching extends AsymmetricMatcher<RegExp> {
|
||||
constructor(sample: string | RegExp, inverse?: boolean);
|
||||
asymmetricMatch(other: string): boolean;
|
||||
toString(): string;
|
||||
getExpectedType(): string;
|
||||
}
|
||||
declare const JestAsymmetricMatchers: ChaiPlugin;
|
||||
|
||||
declare function equals(a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean): boolean;
|
||||
declare function isAsymmetric(obj: any): boolean;
|
||||
declare function hasAsymmetric(obj: any, seen?: Set<unknown>): boolean;
|
||||
declare function isA(typeName: string, value: unknown): boolean;
|
||||
declare function fnNameFor(func: Function): string;
|
||||
declare function hasProperty(obj: object | null, property: string): boolean;
|
||||
declare function isImmutableUnorderedKeyed(maybeKeyed: any): boolean;
|
||||
declare function isImmutableUnorderedSet(maybeSet: any): boolean;
|
||||
declare function iterableEquality(a: any, b: any, customTesters?: Array<Tester>, aStack?: Array<any>, bStack?: Array<any>): boolean | undefined;
|
||||
declare function subsetEquality(object: unknown, subset: unknown, customTesters?: Array<Tester>): boolean | undefined;
|
||||
declare function typeEquality(a: any, b: any): boolean | undefined;
|
||||
declare function arrayBufferEquality(a: unknown, b: unknown): boolean | undefined;
|
||||
declare function sparseArrayEquality(a: unknown, b: unknown, customTesters?: Array<Tester>): boolean | undefined;
|
||||
declare function generateToBeMessage(deepEqualityName: string, expected?: string, actual?: string): string;
|
||||
declare function pluralize(word: string, count: number): string;
|
||||
declare function getObjectKeys(object: object): Array<string | symbol>;
|
||||
declare function getObjectSubset(object: any, subset: any, customTesters?: Array<Tester>): {
|
||||
subset: any;
|
||||
stripped: number;
|
||||
};
|
||||
|
||||
declare const MATCHERS_OBJECT: unique symbol;
|
||||
declare const JEST_MATCHERS_OBJECT: unique symbol;
|
||||
declare const GLOBAL_EXPECT: unique symbol;
|
||||
declare const ASYMMETRIC_MATCHERS_OBJECT: unique symbol;
|
||||
|
||||
declare function getState<State extends MatcherState = MatcherState>(expect: ExpectStatic): State;
|
||||
declare function setState<State extends MatcherState = MatcherState>(state: Partial<State>, expect: ExpectStatic): void;
|
||||
|
||||
declare const JestChaiExpect: ChaiPlugin;
|
||||
|
||||
declare const JestExtend: ChaiPlugin;
|
||||
|
||||
export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, type Assertion, AsymmetricMatcher, type AsymmetricMatcherInterface, type AsymmetricMatchersContaining, type AsyncExpectationResult, type ChaiPlugin, type ExpectStatic, type ExpectationResult, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, type JestAssertion, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, type MatcherHintOptions, type MatcherState, type MatchersObject, ObjectContaining, type RawMatcherFn, StringContaining, StringMatching, type SyncExpectationResult, type Tester, type TesterContext, addCustomEqualityTesters, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
|
||||
+1639
@@ -0,0 +1,1639 @@
|
||||
import { getType, getColors, stringify, isObject, assertTypes } from '@vitest/utils';
|
||||
export { setupColors } from '@vitest/utils';
|
||||
import { diff } from '@vitest/utils/diff';
|
||||
import { isMockFunction } from '@vitest/spy';
|
||||
import { processError } from '@vitest/utils/error';
|
||||
import { util } from 'chai';
|
||||
|
||||
const MATCHERS_OBJECT = Symbol.for("matchers-object");
|
||||
const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object");
|
||||
const GLOBAL_EXPECT = Symbol.for("expect-global");
|
||||
const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object");
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
|
||||
const globalState = /* @__PURE__ */ new WeakMap();
|
||||
const matchers = /* @__PURE__ */ Object.create(null);
|
||||
const customEqualityTesters = [];
|
||||
const assymetricMatchers = /* @__PURE__ */ Object.create(null);
|
||||
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
|
||||
get: () => globalState
|
||||
});
|
||||
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
|
||||
configurable: true,
|
||||
get: () => ({
|
||||
state: globalState.get(globalThis[GLOBAL_EXPECT]),
|
||||
matchers,
|
||||
customEqualityTesters
|
||||
})
|
||||
});
|
||||
Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
|
||||
get: () => assymetricMatchers
|
||||
});
|
||||
}
|
||||
function getState(expect) {
|
||||
return globalThis[MATCHERS_OBJECT].get(expect);
|
||||
}
|
||||
function setState(state, expect) {
|
||||
const map = globalThis[MATCHERS_OBJECT];
|
||||
const current = map.get(expect) || {};
|
||||
Object.assign(current, state);
|
||||
map.set(expect, current);
|
||||
}
|
||||
|
||||
function getMatcherUtils() {
|
||||
const c = () => getColors();
|
||||
const EXPECTED_COLOR = c().green;
|
||||
const RECEIVED_COLOR = c().red;
|
||||
const INVERTED_COLOR = c().inverse;
|
||||
const BOLD_WEIGHT = c().bold;
|
||||
const DIM_COLOR = c().dim;
|
||||
function matcherHint(matcherName, received = "received", expected = "expected", options = {}) {
|
||||
const {
|
||||
comment = "",
|
||||
isDirectExpectCall = false,
|
||||
// seems redundant with received === ''
|
||||
isNot = false,
|
||||
promise = "",
|
||||
secondArgument = "",
|
||||
expectedColor = EXPECTED_COLOR,
|
||||
receivedColor = RECEIVED_COLOR,
|
||||
secondArgumentColor = EXPECTED_COLOR
|
||||
} = options;
|
||||
let hint = "";
|
||||
let dimString = "expect";
|
||||
if (!isDirectExpectCall && received !== "") {
|
||||
hint += DIM_COLOR(`${dimString}(`) + receivedColor(received);
|
||||
dimString = ")";
|
||||
}
|
||||
if (promise !== "") {
|
||||
hint += DIM_COLOR(`${dimString}.`) + promise;
|
||||
dimString = "";
|
||||
}
|
||||
if (isNot) {
|
||||
hint += `${DIM_COLOR(`${dimString}.`)}not`;
|
||||
dimString = "";
|
||||
}
|
||||
if (matcherName.includes(".")) {
|
||||
dimString += matcherName;
|
||||
} else {
|
||||
hint += DIM_COLOR(`${dimString}.`) + matcherName;
|
||||
dimString = "";
|
||||
}
|
||||
if (expected === "") {
|
||||
dimString += "()";
|
||||
} else {
|
||||
hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected);
|
||||
if (secondArgument)
|
||||
hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument);
|
||||
dimString = ")";
|
||||
}
|
||||
if (comment !== "")
|
||||
dimString += ` // ${comment}`;
|
||||
if (dimString !== "")
|
||||
hint += DIM_COLOR(dimString);
|
||||
return hint;
|
||||
}
|
||||
const SPACE_SYMBOL = "\xB7";
|
||||
const replaceTrailingSpaces = (text) => text.replace(/\s+$/gm, (spaces) => SPACE_SYMBOL.repeat(spaces.length));
|
||||
const printReceived = (object) => RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)));
|
||||
const printExpected = (value) => EXPECTED_COLOR(replaceTrailingSpaces(stringify(value)));
|
||||
return {
|
||||
EXPECTED_COLOR,
|
||||
RECEIVED_COLOR,
|
||||
INVERTED_COLOR,
|
||||
BOLD_WEIGHT,
|
||||
DIM_COLOR,
|
||||
matcherHint,
|
||||
printReceived,
|
||||
printExpected
|
||||
};
|
||||
}
|
||||
function addCustomEqualityTesters(newTesters) {
|
||||
if (!Array.isArray(newTesters)) {
|
||||
throw new TypeError(
|
||||
`expect.customEqualityTesters: Must be set to an array of Testers. Was given "${getType(
|
||||
newTesters
|
||||
)}"`
|
||||
);
|
||||
}
|
||||
globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters.push(
|
||||
...newTesters
|
||||
);
|
||||
}
|
||||
function getCustomEqualityTesters() {
|
||||
return globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters;
|
||||
}
|
||||
|
||||
function equals(a, b, customTesters, strictCheck) {
|
||||
customTesters = customTesters || [];
|
||||
return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey);
|
||||
}
|
||||
const functionToString = Function.prototype.toString;
|
||||
function isAsymmetric(obj) {
|
||||
return !!obj && typeof obj === "object" && "asymmetricMatch" in obj && isA("Function", obj.asymmetricMatch);
|
||||
}
|
||||
function hasAsymmetric(obj, seen = /* @__PURE__ */ new Set()) {
|
||||
if (seen.has(obj))
|
||||
return false;
|
||||
seen.add(obj);
|
||||
if (isAsymmetric(obj))
|
||||
return true;
|
||||
if (Array.isArray(obj))
|
||||
return obj.some((i) => hasAsymmetric(i, seen));
|
||||
if (obj instanceof Set)
|
||||
return Array.from(obj).some((i) => hasAsymmetric(i, seen));
|
||||
if (isObject(obj))
|
||||
return Object.values(obj).some((v) => hasAsymmetric(v, seen));
|
||||
return false;
|
||||
}
|
||||
function asymmetricMatch(a, b) {
|
||||
const asymmetricA = isAsymmetric(a);
|
||||
const asymmetricB = isAsymmetric(b);
|
||||
if (asymmetricA && asymmetricB)
|
||||
return void 0;
|
||||
if (asymmetricA)
|
||||
return a.asymmetricMatch(b);
|
||||
if (asymmetricB)
|
||||
return b.asymmetricMatch(a);
|
||||
}
|
||||
function eq(a, b, aStack, bStack, customTesters, hasKey2) {
|
||||
let result = true;
|
||||
const asymmetricResult = asymmetricMatch(a, b);
|
||||
if (asymmetricResult !== void 0)
|
||||
return asymmetricResult;
|
||||
const testerContext = { equals };
|
||||
for (let i = 0; i < customTesters.length; i++) {
|
||||
const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters);
|
||||
if (customTesterResult !== void 0)
|
||||
return customTesterResult;
|
||||
}
|
||||
if (a instanceof Error && b instanceof Error)
|
||||
return a.message === b.message;
|
||||
if (typeof URL === "function" && a instanceof URL && b instanceof URL)
|
||||
return a.href === b.href;
|
||||
if (Object.is(a, b))
|
||||
return true;
|
||||
if (a === null || b === null)
|
||||
return a === b;
|
||||
const className = Object.prototype.toString.call(a);
|
||||
if (className !== Object.prototype.toString.call(b))
|
||||
return false;
|
||||
switch (className) {
|
||||
case "[object Boolean]":
|
||||
case "[object String]":
|
||||
case "[object Number]":
|
||||
if (typeof a !== typeof b) {
|
||||
return false;
|
||||
} else if (typeof a !== "object" && typeof b !== "object") {
|
||||
return Object.is(a, b);
|
||||
} else {
|
||||
return Object.is(a.valueOf(), b.valueOf());
|
||||
}
|
||||
case "[object Date]": {
|
||||
const numA = +a;
|
||||
const numB = +b;
|
||||
return numA === numB || Number.isNaN(numA) && Number.isNaN(numB);
|
||||
}
|
||||
case "[object RegExp]":
|
||||
return a.source === b.source && a.flags === b.flags;
|
||||
}
|
||||
if (typeof a !== "object" || typeof b !== "object")
|
||||
return false;
|
||||
if (isDomNode(a) && isDomNode(b))
|
||||
return a.isEqualNode(b);
|
||||
let length = aStack.length;
|
||||
while (length--) {
|
||||
if (aStack[length] === a)
|
||||
return bStack[length] === b;
|
||||
else if (bStack[length] === b)
|
||||
return false;
|
||||
}
|
||||
aStack.push(a);
|
||||
bStack.push(b);
|
||||
if (className === "[object Array]" && a.length !== b.length)
|
||||
return false;
|
||||
const aKeys = keys(a, hasKey2);
|
||||
let key;
|
||||
let size = aKeys.length;
|
||||
if (keys(b, hasKey2).length !== size)
|
||||
return false;
|
||||
while (size--) {
|
||||
key = aKeys[size];
|
||||
result = hasKey2(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey2);
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
aStack.pop();
|
||||
bStack.pop();
|
||||
return result;
|
||||
}
|
||||
function keys(obj, hasKey2) {
|
||||
const keys2 = [];
|
||||
for (const key in obj) {
|
||||
if (hasKey2(obj, key))
|
||||
keys2.push(key);
|
||||
}
|
||||
return keys2.concat(
|
||||
Object.getOwnPropertySymbols(obj).filter(
|
||||
(symbol) => Object.getOwnPropertyDescriptor(obj, symbol).enumerable
|
||||
)
|
||||
);
|
||||
}
|
||||
function hasDefinedKey(obj, key) {
|
||||
return hasKey(obj, key) && obj[key] !== void 0;
|
||||
}
|
||||
function hasKey(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
function isA(typeName, value) {
|
||||
return Object.prototype.toString.apply(value) === `[object ${typeName}]`;
|
||||
}
|
||||
function isDomNode(obj) {
|
||||
return obj !== null && typeof obj === "object" && "nodeType" in obj && typeof obj.nodeType === "number" && "nodeName" in obj && typeof obj.nodeName === "string" && "isEqualNode" in obj && typeof obj.isEqualNode === "function";
|
||||
}
|
||||
function fnNameFor(func) {
|
||||
if (func.name)
|
||||
return func.name;
|
||||
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
|
||||
return matches ? matches[1] : "<anonymous>";
|
||||
}
|
||||
function getPrototype(obj) {
|
||||
if (Object.getPrototypeOf)
|
||||
return Object.getPrototypeOf(obj);
|
||||
if (obj.constructor.prototype === obj)
|
||||
return null;
|
||||
return obj.constructor.prototype;
|
||||
}
|
||||
function hasProperty(obj, property) {
|
||||
if (!obj)
|
||||
return false;
|
||||
if (Object.prototype.hasOwnProperty.call(obj, property))
|
||||
return true;
|
||||
return hasProperty(getPrototype(obj), property);
|
||||
}
|
||||
const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@";
|
||||
const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@";
|
||||
const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@";
|
||||
function isImmutableUnorderedKeyed(maybeKeyed) {
|
||||
return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL] && !maybeKeyed[IS_ORDERED_SENTINEL]);
|
||||
}
|
||||
function isImmutableUnorderedSet(maybeSet) {
|
||||
return !!(maybeSet && maybeSet[IS_SET_SENTINEL] && !maybeSet[IS_ORDERED_SENTINEL]);
|
||||
}
|
||||
const IteratorSymbol = Symbol.iterator;
|
||||
function hasIterator(object) {
|
||||
return !!(object != null && object[IteratorSymbol]);
|
||||
}
|
||||
function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) {
|
||||
if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b))
|
||||
return void 0;
|
||||
if (a.constructor !== b.constructor)
|
||||
return false;
|
||||
let length = aStack.length;
|
||||
while (length--) {
|
||||
if (aStack[length] === a)
|
||||
return bStack[length] === b;
|
||||
}
|
||||
aStack.push(a);
|
||||
bStack.push(b);
|
||||
const filteredCustomTesters = [
|
||||
...customTesters.filter((t) => t !== iterableEquality),
|
||||
iterableEqualityWithStack
|
||||
];
|
||||
function iterableEqualityWithStack(a2, b2) {
|
||||
return iterableEquality(
|
||||
a2,
|
||||
b2,
|
||||
[...customTesters],
|
||||
[...aStack],
|
||||
[...bStack]
|
||||
);
|
||||
}
|
||||
if (a.size !== void 0) {
|
||||
if (a.size !== b.size) {
|
||||
return false;
|
||||
} else if (isA("Set", a) || isImmutableUnorderedSet(a)) {
|
||||
let allFound = true;
|
||||
for (const aValue of a) {
|
||||
if (!b.has(aValue)) {
|
||||
let has = false;
|
||||
for (const bValue of b) {
|
||||
const isEqual = equals(aValue, bValue, filteredCustomTesters);
|
||||
if (isEqual === true)
|
||||
has = true;
|
||||
}
|
||||
if (has === false) {
|
||||
allFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
aStack.pop();
|
||||
bStack.pop();
|
||||
return allFound;
|
||||
} else if (isA("Map", a) || isImmutableUnorderedKeyed(a)) {
|
||||
let allFound = true;
|
||||
for (const aEntry of a) {
|
||||
if (!b.has(aEntry[0]) || !equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)) {
|
||||
let has = false;
|
||||
for (const bEntry of b) {
|
||||
const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters);
|
||||
let matchedValue = false;
|
||||
if (matchedKey === true)
|
||||
matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters);
|
||||
if (matchedValue === true)
|
||||
has = true;
|
||||
}
|
||||
if (has === false) {
|
||||
allFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
aStack.pop();
|
||||
bStack.pop();
|
||||
return allFound;
|
||||
}
|
||||
}
|
||||
const bIterator = b[IteratorSymbol]();
|
||||
for (const aValue of a) {
|
||||
const nextB = bIterator.next();
|
||||
if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters))
|
||||
return false;
|
||||
}
|
||||
if (!bIterator.next().done)
|
||||
return false;
|
||||
const aEntries = Object.entries(a);
|
||||
const bEntries = Object.entries(b);
|
||||
if (!equals(aEntries, bEntries))
|
||||
return false;
|
||||
aStack.pop();
|
||||
bStack.pop();
|
||||
return true;
|
||||
}
|
||||
function hasPropertyInObject(object, key) {
|
||||
const shouldTerminate = !object || typeof object !== "object" || object === Object.prototype;
|
||||
if (shouldTerminate)
|
||||
return false;
|
||||
return Object.prototype.hasOwnProperty.call(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key);
|
||||
}
|
||||
function isObjectWithKeys(a) {
|
||||
return isObject(a) && !(a instanceof Error) && !Array.isArray(a) && !(a instanceof Date);
|
||||
}
|
||||
function subsetEquality(object, subset, customTesters = []) {
|
||||
const filteredCustomTesters = customTesters.filter((t) => t !== subsetEquality);
|
||||
const subsetEqualityWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object2, subset2) => {
|
||||
if (!isObjectWithKeys(subset2))
|
||||
return void 0;
|
||||
return Object.keys(subset2).every((key) => {
|
||||
if (subset2[key] != null && typeof subset2[key] === "object") {
|
||||
if (seenReferences.has(subset2[key]))
|
||||
return equals(object2[key], subset2[key], filteredCustomTesters);
|
||||
seenReferences.set(subset2[key], true);
|
||||
}
|
||||
const result = object2 != null && hasPropertyInObject(object2, key) && equals(object2[key], subset2[key], [
|
||||
...filteredCustomTesters,
|
||||
subsetEqualityWithContext(seenReferences)
|
||||
]);
|
||||
seenReferences.delete(subset2[key]);
|
||||
return result;
|
||||
});
|
||||
};
|
||||
return subsetEqualityWithContext()(object, subset);
|
||||
}
|
||||
function typeEquality(a, b) {
|
||||
if (a == null || b == null || a.constructor === b.constructor)
|
||||
return void 0;
|
||||
return false;
|
||||
}
|
||||
function arrayBufferEquality(a, b) {
|
||||
let dataViewA = a;
|
||||
let dataViewB = b;
|
||||
if (!(a instanceof DataView && b instanceof DataView)) {
|
||||
if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer))
|
||||
return void 0;
|
||||
try {
|
||||
dataViewA = new DataView(a);
|
||||
dataViewB = new DataView(b);
|
||||
} catch {
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
if (dataViewA.byteLength !== dataViewB.byteLength)
|
||||
return false;
|
||||
for (let i = 0; i < dataViewA.byteLength; i++) {
|
||||
if (dataViewA.getUint8(i) !== dataViewB.getUint8(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function sparseArrayEquality(a, b, customTesters = []) {
|
||||
if (!Array.isArray(a) || !Array.isArray(b))
|
||||
return void 0;
|
||||
const aKeys = Object.keys(a);
|
||||
const bKeys = Object.keys(b);
|
||||
const filteredCustomTesters = customTesters.filter((t) => t !== sparseArrayEquality);
|
||||
return equals(a, b, filteredCustomTesters, true) && equals(aKeys, bKeys);
|
||||
}
|
||||
function generateToBeMessage(deepEqualityName, expected = "#{this}", actual = "#{exp}") {
|
||||
const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`;
|
||||
if (["toStrictEqual", "toEqual"].includes(deepEqualityName))
|
||||
return `${toBeMessage}
|
||||
|
||||
If it should pass with deep equality, replace "toBe" with "${deepEqualityName}"
|
||||
|
||||
Expected: ${expected}
|
||||
Received: serializes to the same string
|
||||
`;
|
||||
return toBeMessage;
|
||||
}
|
||||
function pluralize(word, count) {
|
||||
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
||||
}
|
||||
function getObjectKeys(object) {
|
||||
return [
|
||||
...Object.keys(object),
|
||||
...Object.getOwnPropertySymbols(object).filter(
|
||||
(s) => {
|
||||
var _a;
|
||||
return (_a = Object.getOwnPropertyDescriptor(object, s)) == null ? void 0 : _a.enumerable;
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
function getObjectSubset(object, subset, customTesters = []) {
|
||||
let stripped = 0;
|
||||
const getObjectSubsetWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object2, subset2) => {
|
||||
if (Array.isArray(object2)) {
|
||||
if (Array.isArray(subset2) && subset2.length === object2.length) {
|
||||
return subset2.map(
|
||||
(sub, i) => getObjectSubsetWithContext(seenReferences)(object2[i], sub)
|
||||
);
|
||||
}
|
||||
} else if (object2 instanceof Date) {
|
||||
return object2;
|
||||
} else if (isObject(object2) && isObject(subset2)) {
|
||||
if (equals(object2, subset2, [
|
||||
...customTesters,
|
||||
iterableEquality,
|
||||
subsetEquality
|
||||
])) {
|
||||
return subset2;
|
||||
}
|
||||
const trimmed = {};
|
||||
seenReferences.set(object2, trimmed);
|
||||
for (const key of getObjectKeys(object2)) {
|
||||
if (hasPropertyInObject(subset2, key)) {
|
||||
trimmed[key] = seenReferences.has(object2[key]) ? seenReferences.get(object2[key]) : getObjectSubsetWithContext(seenReferences)(object2[key], subset2[key]);
|
||||
} else {
|
||||
if (!seenReferences.has(object2[key])) {
|
||||
stripped += 1;
|
||||
if (isObject(object2[key]))
|
||||
stripped += getObjectKeys(object2[key]).length;
|
||||
getObjectSubsetWithContext(seenReferences)(object2[key], subset2[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getObjectKeys(trimmed).length > 0)
|
||||
return trimmed;
|
||||
}
|
||||
return object2;
|
||||
};
|
||||
return { subset: getObjectSubsetWithContext()(object, subset), stripped };
|
||||
}
|
||||
|
||||
class AsymmetricMatcher {
|
||||
constructor(sample, inverse = false) {
|
||||
this.sample = sample;
|
||||
this.inverse = inverse;
|
||||
}
|
||||
// should have "jest" to be compatible with its ecosystem
|
||||
$$typeof = Symbol.for("jest.asymmetricMatcher");
|
||||
getMatcherContext(expect) {
|
||||
return {
|
||||
...getState(expect || globalThis[GLOBAL_EXPECT]),
|
||||
equals,
|
||||
isNot: this.inverse,
|
||||
customTesters: getCustomEqualityTesters(),
|
||||
utils: {
|
||||
...getMatcherUtils(),
|
||||
diff,
|
||||
stringify,
|
||||
iterableEquality,
|
||||
subsetEquality
|
||||
}
|
||||
};
|
||||
}
|
||||
// implement custom chai/loupe inspect for better AssertionError.message formatting
|
||||
// https://github.com/chaijs/loupe/blob/9b8a6deabcd50adc056a64fb705896194710c5c6/src/index.ts#L29
|
||||
[Symbol.for("chai/inspect")](options) {
|
||||
const result = stringify(this, options.depth, { min: true });
|
||||
if (result.length <= options.truncate)
|
||||
return result;
|
||||
return `${this.toString()}{\u2026}`;
|
||||
}
|
||||
}
|
||||
class StringContaining extends AsymmetricMatcher {
|
||||
constructor(sample, inverse = false) {
|
||||
if (!isA("String", sample))
|
||||
throw new Error("Expected is not a string");
|
||||
super(sample, inverse);
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
const result = isA("String", other) && other.includes(this.sample);
|
||||
return this.inverse ? !result : result;
|
||||
}
|
||||
toString() {
|
||||
return `String${this.inverse ? "Not" : ""}Containing`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
class Anything extends AsymmetricMatcher {
|
||||
asymmetricMatch(other) {
|
||||
return other != null;
|
||||
}
|
||||
toString() {
|
||||
return "Anything";
|
||||
}
|
||||
toAsymmetricMatcher() {
|
||||
return "Anything";
|
||||
}
|
||||
}
|
||||
class ObjectContaining extends AsymmetricMatcher {
|
||||
constructor(sample, inverse = false) {
|
||||
super(sample, inverse);
|
||||
}
|
||||
getPrototype(obj) {
|
||||
if (Object.getPrototypeOf)
|
||||
return Object.getPrototypeOf(obj);
|
||||
if (obj.constructor.prototype === obj)
|
||||
return null;
|
||||
return obj.constructor.prototype;
|
||||
}
|
||||
hasProperty(obj, property) {
|
||||
if (!obj)
|
||||
return false;
|
||||
if (Object.prototype.hasOwnProperty.call(obj, property))
|
||||
return true;
|
||||
return this.hasProperty(this.getPrototype(obj), property);
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
if (typeof this.sample !== "object") {
|
||||
throw new TypeError(
|
||||
`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`
|
||||
);
|
||||
}
|
||||
let result = true;
|
||||
const matcherContext = this.getMatcherContext();
|
||||
for (const property in this.sample) {
|
||||
if (!this.hasProperty(other, property) || !equals(this.sample[property], other[property], matcherContext.customTesters)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this.inverse ? !result : result;
|
||||
}
|
||||
toString() {
|
||||
return `Object${this.inverse ? "Not" : ""}Containing`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "object";
|
||||
}
|
||||
}
|
||||
class ArrayContaining extends AsymmetricMatcher {
|
||||
constructor(sample, inverse = false) {
|
||||
super(sample, inverse);
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
if (!Array.isArray(this.sample)) {
|
||||
throw new TypeError(
|
||||
`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`
|
||||
);
|
||||
}
|
||||
const matcherContext = this.getMatcherContext();
|
||||
const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every(
|
||||
(item) => other.some((another) => equals(item, another, matcherContext.customTesters))
|
||||
);
|
||||
return this.inverse ? !result : result;
|
||||
}
|
||||
toString() {
|
||||
return `Array${this.inverse ? "Not" : ""}Containing`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "array";
|
||||
}
|
||||
}
|
||||
class Any extends AsymmetricMatcher {
|
||||
constructor(sample) {
|
||||
if (typeof sample === "undefined") {
|
||||
throw new TypeError(
|
||||
"any() expects to be passed a constructor function. Please pass one or use anything() to match any object."
|
||||
);
|
||||
}
|
||||
super(sample);
|
||||
}
|
||||
fnNameFor(func) {
|
||||
if (func.name)
|
||||
return func.name;
|
||||
const functionToString = Function.prototype.toString;
|
||||
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
|
||||
return matches ? matches[1] : "<anonymous>";
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
if (this.sample === String)
|
||||
return typeof other == "string" || other instanceof String;
|
||||
if (this.sample === Number)
|
||||
return typeof other == "number" || other instanceof Number;
|
||||
if (this.sample === Function)
|
||||
return typeof other == "function" || other instanceof Function;
|
||||
if (this.sample === Boolean)
|
||||
return typeof other == "boolean" || other instanceof Boolean;
|
||||
if (this.sample === BigInt)
|
||||
return typeof other == "bigint" || other instanceof BigInt;
|
||||
if (this.sample === Symbol)
|
||||
return typeof other == "symbol" || other instanceof Symbol;
|
||||
if (this.sample === Object)
|
||||
return typeof other == "object";
|
||||
return other instanceof this.sample;
|
||||
}
|
||||
toString() {
|
||||
return "Any";
|
||||
}
|
||||
getExpectedType() {
|
||||
if (this.sample === String)
|
||||
return "string";
|
||||
if (this.sample === Number)
|
||||
return "number";
|
||||
if (this.sample === Function)
|
||||
return "function";
|
||||
if (this.sample === Object)
|
||||
return "object";
|
||||
if (this.sample === Boolean)
|
||||
return "boolean";
|
||||
return this.fnNameFor(this.sample);
|
||||
}
|
||||
toAsymmetricMatcher() {
|
||||
return `Any<${this.fnNameFor(this.sample)}>`;
|
||||
}
|
||||
}
|
||||
class StringMatching extends AsymmetricMatcher {
|
||||
constructor(sample, inverse = false) {
|
||||
if (!isA("String", sample) && !isA("RegExp", sample))
|
||||
throw new Error("Expected is not a String or a RegExp");
|
||||
super(new RegExp(sample), inverse);
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
const result = isA("String", other) && this.sample.test(other);
|
||||
return this.inverse ? !result : result;
|
||||
}
|
||||
toString() {
|
||||
return `String${this.inverse ? "Not" : ""}Matching`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
class CloseTo extends AsymmetricMatcher {
|
||||
precision;
|
||||
constructor(sample, precision = 2, inverse = false) {
|
||||
if (!isA("Number", sample))
|
||||
throw new Error("Expected is not a Number");
|
||||
if (!isA("Number", precision))
|
||||
throw new Error("Precision is not a Number");
|
||||
super(sample);
|
||||
this.inverse = inverse;
|
||||
this.precision = precision;
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
if (!isA("Number", other))
|
||||
return false;
|
||||
let result = false;
|
||||
if (other === Number.POSITIVE_INFINITY && this.sample === Number.POSITIVE_INFINITY) {
|
||||
result = true;
|
||||
} else if (other === Number.NEGATIVE_INFINITY && this.sample === Number.NEGATIVE_INFINITY) {
|
||||
result = true;
|
||||
} else {
|
||||
result = Math.abs(this.sample - other) < 10 ** -this.precision / 2;
|
||||
}
|
||||
return this.inverse ? !result : result;
|
||||
}
|
||||
toString() {
|
||||
return `Number${this.inverse ? "Not" : ""}CloseTo`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "number";
|
||||
}
|
||||
toAsymmetricMatcher() {
|
||||
return [
|
||||
this.toString(),
|
||||
this.sample,
|
||||
`(${pluralize("digit", this.precision)})`
|
||||
].join(" ");
|
||||
}
|
||||
}
|
||||
const JestAsymmetricMatchers = (chai, utils) => {
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"anything",
|
||||
() => new Anything()
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"any",
|
||||
(expected) => new Any(expected)
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"stringContaining",
|
||||
(expected) => new StringContaining(expected)
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"objectContaining",
|
||||
(expected) => new ObjectContaining(expected)
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"arrayContaining",
|
||||
(expected) => new ArrayContaining(expected)
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"stringMatching",
|
||||
(expected) => new StringMatching(expected)
|
||||
);
|
||||
utils.addMethod(
|
||||
chai.expect,
|
||||
"closeTo",
|
||||
(expected, precision) => new CloseTo(expected, precision)
|
||||
);
|
||||
chai.expect.not = {
|
||||
stringContaining: (expected) => new StringContaining(expected, true),
|
||||
objectContaining: (expected) => new ObjectContaining(expected, true),
|
||||
arrayContaining: (expected) => new ArrayContaining(expected, true),
|
||||
stringMatching: (expected) => new StringMatching(expected, true),
|
||||
closeTo: (expected, precision) => new CloseTo(expected, precision, true)
|
||||
};
|
||||
};
|
||||
|
||||
function recordAsyncExpect(test, promise) {
|
||||
if (test && promise instanceof Promise) {
|
||||
promise = promise.finally(() => {
|
||||
const index = test.promises.indexOf(promise);
|
||||
if (index !== -1)
|
||||
test.promises.splice(index, 1);
|
||||
});
|
||||
if (!test.promises)
|
||||
test.promises = [];
|
||||
test.promises.push(promise);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
function wrapSoft(utils, fn) {
|
||||
return function(...args) {
|
||||
var _a;
|
||||
const test = utils.flag(this, "vitest-test");
|
||||
const state = (test == null ? void 0 : test.context._local) ? test.context.expect.getState() : getState(globalThis[GLOBAL_EXPECT]);
|
||||
if (!state.soft)
|
||||
return fn.apply(this, args);
|
||||
if (!test)
|
||||
throw new Error("expect.soft() can only be used inside a test");
|
||||
try {
|
||||
return fn.apply(this, args);
|
||||
} catch (err) {
|
||||
test.result || (test.result = { state: "fail" });
|
||||
test.result.state = "fail";
|
||||
(_a = test.result).errors || (_a.errors = []);
|
||||
test.result.errors.push(processError(err));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const JestChaiExpect = (chai, utils) => {
|
||||
const { AssertionError } = chai;
|
||||
const c = () => getColors();
|
||||
const customTesters = getCustomEqualityTesters();
|
||||
function def(name, fn) {
|
||||
const addMethod = (n) => {
|
||||
const softWrapper = wrapSoft(utils, fn);
|
||||
utils.addMethod(chai.Assertion.prototype, n, softWrapper);
|
||||
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, softWrapper);
|
||||
};
|
||||
if (Array.isArray(name))
|
||||
name.forEach((n) => addMethod(n));
|
||||
else
|
||||
addMethod(name);
|
||||
}
|
||||
["throw", "throws", "Throw"].forEach((m) => {
|
||||
utils.overwriteMethod(chai.Assertion.prototype, m, (_super) => {
|
||||
return function(...args) {
|
||||
const promise = utils.flag(this, "promise");
|
||||
const object = utils.flag(this, "object");
|
||||
const isNot = utils.flag(this, "negate");
|
||||
if (promise === "rejects") {
|
||||
utils.flag(this, "object", () => {
|
||||
throw object;
|
||||
});
|
||||
} else if (promise === "resolves" && typeof object !== "function") {
|
||||
if (!isNot) {
|
||||
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
||||
const error = {
|
||||
showDiff: false
|
||||
};
|
||||
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_super.apply(this, args);
|
||||
};
|
||||
});
|
||||
});
|
||||
def("withTest", function(test) {
|
||||
utils.flag(this, "vitest-test", test);
|
||||
return this;
|
||||
});
|
||||
def("toEqual", function(expected) {
|
||||
const actual = utils.flag(this, "object");
|
||||
const equal = equals(
|
||||
actual,
|
||||
expected,
|
||||
[...customTesters, iterableEquality]
|
||||
);
|
||||
return this.assert(
|
||||
equal,
|
||||
"expected #{this} to deeply equal #{exp}",
|
||||
"expected #{this} to not deeply equal #{exp}",
|
||||
expected,
|
||||
actual
|
||||
);
|
||||
});
|
||||
def("toStrictEqual", function(expected) {
|
||||
const obj = utils.flag(this, "object");
|
||||
const equal = equals(
|
||||
obj,
|
||||
expected,
|
||||
[
|
||||
...customTesters,
|
||||
iterableEquality,
|
||||
typeEquality,
|
||||
sparseArrayEquality,
|
||||
arrayBufferEquality
|
||||
],
|
||||
true
|
||||
);
|
||||
return this.assert(
|
||||
equal,
|
||||
"expected #{this} to strictly equal #{exp}",
|
||||
"expected #{this} to not strictly equal #{exp}",
|
||||
expected,
|
||||
obj
|
||||
);
|
||||
});
|
||||
def("toBe", function(expected) {
|
||||
const actual = this._obj;
|
||||
const pass = Object.is(actual, expected);
|
||||
let deepEqualityName = "";
|
||||
if (!pass) {
|
||||
const toStrictEqualPass = equals(
|
||||
actual,
|
||||
expected,
|
||||
[
|
||||
...customTesters,
|
||||
iterableEquality,
|
||||
typeEquality,
|
||||
sparseArrayEquality,
|
||||
arrayBufferEquality
|
||||
],
|
||||
true
|
||||
);
|
||||
if (toStrictEqualPass) {
|
||||
deepEqualityName = "toStrictEqual";
|
||||
} else {
|
||||
const toEqualPass = equals(
|
||||
actual,
|
||||
expected,
|
||||
[...customTesters, iterableEquality]
|
||||
);
|
||||
if (toEqualPass)
|
||||
deepEqualityName = "toEqual";
|
||||
}
|
||||
}
|
||||
return this.assert(
|
||||
pass,
|
||||
generateToBeMessage(deepEqualityName),
|
||||
"expected #{this} not to be #{exp} // Object.is equality",
|
||||
expected,
|
||||
actual
|
||||
);
|
||||
});
|
||||
def("toMatchObject", function(expected) {
|
||||
const actual = this._obj;
|
||||
const pass = equals(actual, expected, [...customTesters, iterableEquality, subsetEquality]);
|
||||
const isNot = utils.flag(this, "negate");
|
||||
const { subset: actualSubset, stripped } = getObjectSubset(actual, expected);
|
||||
if (pass && isNot || !pass && !isNot) {
|
||||
const msg = utils.getMessage(
|
||||
this,
|
||||
[
|
||||
pass,
|
||||
"expected #{this} to match object #{exp}",
|
||||
"expected #{this} to not match object #{exp}",
|
||||
expected,
|
||||
actualSubset,
|
||||
false
|
||||
]
|
||||
);
|
||||
const message = stripped === 0 ? msg : `${msg}
|
||||
(${stripped} matching ${stripped === 1 ? "property" : "properties"} omitted from actual)`;
|
||||
throw new AssertionError(message, { showDiff: true, expected, actual: actualSubset });
|
||||
}
|
||||
});
|
||||
def("toMatch", function(expected) {
|
||||
const actual = this._obj;
|
||||
if (typeof actual !== "string")
|
||||
throw new TypeError(`.toMatch() expects to receive a string, but got ${typeof actual}`);
|
||||
return this.assert(
|
||||
typeof expected === "string" ? actual.includes(expected) : actual.match(expected),
|
||||
`expected #{this} to match #{exp}`,
|
||||
`expected #{this} not to match #{exp}`,
|
||||
expected,
|
||||
actual
|
||||
);
|
||||
});
|
||||
def("toContain", function(item) {
|
||||
const actual = this._obj;
|
||||
if (typeof Node !== "undefined" && actual instanceof Node) {
|
||||
if (!(item instanceof Node))
|
||||
throw new TypeError(`toContain() expected a DOM node as the argument, but got ${typeof item}`);
|
||||
return this.assert(
|
||||
actual.contains(item),
|
||||
"expected #{this} to contain element #{exp}",
|
||||
"expected #{this} not to contain element #{exp}",
|
||||
item,
|
||||
actual
|
||||
);
|
||||
}
|
||||
if (typeof DOMTokenList !== "undefined" && actual instanceof DOMTokenList) {
|
||||
assertTypes(item, "class name", ["string"]);
|
||||
const isNot = utils.flag(this, "negate");
|
||||
const expectedClassList = isNot ? actual.value.replace(item, "").trim() : `${actual.value} ${item}`;
|
||||
return this.assert(
|
||||
actual.contains(item),
|
||||
`expected "${actual.value}" to contain "${item}"`,
|
||||
`expected "${actual.value}" not to contain "${item}"`,
|
||||
expectedClassList,
|
||||
actual.value
|
||||
);
|
||||
}
|
||||
if (typeof actual === "string" && typeof item === "string") {
|
||||
return this.assert(
|
||||
actual.includes(item),
|
||||
`expected #{this} to contain #{exp}`,
|
||||
`expected #{this} not to contain #{exp}`,
|
||||
item,
|
||||
actual
|
||||
);
|
||||
}
|
||||
if (actual != null && typeof actual !== "string")
|
||||
utils.flag(this, "object", Array.from(actual));
|
||||
return this.contain(item);
|
||||
});
|
||||
def("toContainEqual", function(expected) {
|
||||
const obj = utils.flag(this, "object");
|
||||
const index = Array.from(obj).findIndex((item) => {
|
||||
return equals(item, expected, customTesters);
|
||||
});
|
||||
this.assert(
|
||||
index !== -1,
|
||||
"expected #{this} to deep equally contain #{exp}",
|
||||
"expected #{this} to not deep equally contain #{exp}",
|
||||
expected
|
||||
);
|
||||
});
|
||||
def("toBeTruthy", function() {
|
||||
const obj = utils.flag(this, "object");
|
||||
this.assert(
|
||||
Boolean(obj),
|
||||
"expected #{this} to be truthy",
|
||||
"expected #{this} to not be truthy",
|
||||
obj,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeFalsy", function() {
|
||||
const obj = utils.flag(this, "object");
|
||||
this.assert(
|
||||
!obj,
|
||||
"expected #{this} to be falsy",
|
||||
"expected #{this} to not be falsy",
|
||||
obj,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeGreaterThan", function(expected) {
|
||||
const actual = this._obj;
|
||||
assertTypes(actual, "actual", ["number", "bigint"]);
|
||||
assertTypes(expected, "expected", ["number", "bigint"]);
|
||||
return this.assert(
|
||||
actual > expected,
|
||||
`expected ${actual} to be greater than ${expected}`,
|
||||
`expected ${actual} to be not greater than ${expected}`,
|
||||
actual,
|
||||
expected,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeGreaterThanOrEqual", function(expected) {
|
||||
const actual = this._obj;
|
||||
assertTypes(actual, "actual", ["number", "bigint"]);
|
||||
assertTypes(expected, "expected", ["number", "bigint"]);
|
||||
return this.assert(
|
||||
actual >= expected,
|
||||
`expected ${actual} to be greater than or equal to ${expected}`,
|
||||
`expected ${actual} to be not greater than or equal to ${expected}`,
|
||||
actual,
|
||||
expected,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeLessThan", function(expected) {
|
||||
const actual = this._obj;
|
||||
assertTypes(actual, "actual", ["number", "bigint"]);
|
||||
assertTypes(expected, "expected", ["number", "bigint"]);
|
||||
return this.assert(
|
||||
actual < expected,
|
||||
`expected ${actual} to be less than ${expected}`,
|
||||
`expected ${actual} to be not less than ${expected}`,
|
||||
actual,
|
||||
expected,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeLessThanOrEqual", function(expected) {
|
||||
const actual = this._obj;
|
||||
assertTypes(actual, "actual", ["number", "bigint"]);
|
||||
assertTypes(expected, "expected", ["number", "bigint"]);
|
||||
return this.assert(
|
||||
actual <= expected,
|
||||
`expected ${actual} to be less than or equal to ${expected}`,
|
||||
`expected ${actual} to be not less than or equal to ${expected}`,
|
||||
actual,
|
||||
expected,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toBeNaN", function() {
|
||||
return this.be.NaN;
|
||||
});
|
||||
def("toBeUndefined", function() {
|
||||
return this.be.undefined;
|
||||
});
|
||||
def("toBeNull", function() {
|
||||
return this.be.null;
|
||||
});
|
||||
def("toBeDefined", function() {
|
||||
const negate = utils.flag(this, "negate");
|
||||
utils.flag(this, "negate", false);
|
||||
if (negate)
|
||||
return this.be.undefined;
|
||||
return this.not.be.undefined;
|
||||
});
|
||||
def("toBeTypeOf", function(expected) {
|
||||
const actual = typeof this._obj;
|
||||
const equal = expected === actual;
|
||||
return this.assert(
|
||||
equal,
|
||||
"expected #{this} to be type of #{exp}",
|
||||
"expected #{this} not to be type of #{exp}",
|
||||
expected,
|
||||
actual
|
||||
);
|
||||
});
|
||||
def("toBeInstanceOf", function(obj) {
|
||||
return this.instanceOf(obj);
|
||||
});
|
||||
def("toHaveLength", function(length) {
|
||||
return this.have.length(length);
|
||||
});
|
||||
def("toHaveProperty", function(...args) {
|
||||
if (Array.isArray(args[0]))
|
||||
args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join(".");
|
||||
const actual = this._obj;
|
||||
const [propertyName, expected] = args;
|
||||
const getValue = () => {
|
||||
const hasOwn = Object.prototype.hasOwnProperty.call(actual, propertyName);
|
||||
if (hasOwn)
|
||||
return { value: actual[propertyName], exists: true };
|
||||
return utils.getPathInfo(actual, propertyName);
|
||||
};
|
||||
const { value, exists } = getValue();
|
||||
const pass = exists && (args.length === 1 || equals(expected, value, customTesters));
|
||||
const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`;
|
||||
return this.assert(
|
||||
pass,
|
||||
`expected #{this} to have property "${propertyName}"${valueString}`,
|
||||
`expected #{this} to not have property "${propertyName}"${valueString}`,
|
||||
expected,
|
||||
exists ? value : void 0
|
||||
);
|
||||
});
|
||||
def("toBeCloseTo", function(received, precision = 2) {
|
||||
const expected = this._obj;
|
||||
let pass = false;
|
||||
let expectedDiff = 0;
|
||||
let receivedDiff = 0;
|
||||
if (received === Number.POSITIVE_INFINITY && expected === Number.POSITIVE_INFINITY) {
|
||||
pass = true;
|
||||
} else if (received === Number.NEGATIVE_INFINITY && expected === Number.NEGATIVE_INFINITY) {
|
||||
pass = true;
|
||||
} else {
|
||||
expectedDiff = 10 ** -precision / 2;
|
||||
receivedDiff = Math.abs(expected - received);
|
||||
pass = receivedDiff < expectedDiff;
|
||||
}
|
||||
return this.assert(
|
||||
pass,
|
||||
`expected #{this} to be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`,
|
||||
`expected #{this} to not be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`,
|
||||
received,
|
||||
expected,
|
||||
false
|
||||
);
|
||||
});
|
||||
const assertIsMock = (assertion) => {
|
||||
if (!isMockFunction(assertion._obj))
|
||||
throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`);
|
||||
};
|
||||
const getSpy = (assertion) => {
|
||||
assertIsMock(assertion);
|
||||
return assertion._obj;
|
||||
};
|
||||
const ordinalOf = (i) => {
|
||||
const j = i % 10;
|
||||
const k = i % 100;
|
||||
if (j === 1 && k !== 11)
|
||||
return `${i}st`;
|
||||
if (j === 2 && k !== 12)
|
||||
return `${i}nd`;
|
||||
if (j === 3 && k !== 13)
|
||||
return `${i}rd`;
|
||||
return `${i}th`;
|
||||
};
|
||||
const formatCalls = (spy, msg, actualCall) => {
|
||||
if (spy.mock.calls) {
|
||||
msg += c().gray(`
|
||||
|
||||
Received:
|
||||
|
||||
${spy.mock.calls.map((callArg, i) => {
|
||||
let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:
|
||||
|
||||
`);
|
||||
if (actualCall)
|
||||
methodCall += diff(actualCall, callArg, { omitAnnotationLines: true });
|
||||
else
|
||||
methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
|
||||
methodCall += "\n";
|
||||
return methodCall;
|
||||
}).join("\n")}`);
|
||||
}
|
||||
msg += c().gray(`
|
||||
|
||||
Number of calls: ${c().bold(spy.mock.calls.length)}
|
||||
`);
|
||||
return msg;
|
||||
};
|
||||
const formatReturns = (spy, msg, actualReturn) => {
|
||||
msg += c().gray(`
|
||||
|
||||
Received:
|
||||
|
||||
${spy.mock.results.map((callReturn, i) => {
|
||||
let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:
|
||||
|
||||
`);
|
||||
if (actualReturn)
|
||||
methodCall += diff(actualReturn, callReturn.value, { omitAnnotationLines: true });
|
||||
else
|
||||
methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
|
||||
methodCall += "\n";
|
||||
return methodCall;
|
||||
}).join("\n")}`);
|
||||
msg += c().gray(`
|
||||
|
||||
Number of calls: ${c().bold(spy.mock.calls.length)}
|
||||
`);
|
||||
return msg;
|
||||
};
|
||||
def(["toHaveBeenCalledTimes", "toBeCalledTimes"], function(number) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const callCount = spy.mock.calls.length;
|
||||
return this.assert(
|
||||
callCount === number,
|
||||
`expected "${spyName}" to be called #{exp} times, but got ${callCount} times`,
|
||||
`expected "${spyName}" to not be called #{exp} times`,
|
||||
number,
|
||||
callCount,
|
||||
false
|
||||
);
|
||||
});
|
||||
def("toHaveBeenCalledOnce", function() {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const callCount = spy.mock.calls.length;
|
||||
return this.assert(
|
||||
callCount === 1,
|
||||
`expected "${spyName}" to be called once, but got ${callCount} times`,
|
||||
`expected "${spyName}" to not be called once`,
|
||||
1,
|
||||
callCount,
|
||||
false
|
||||
);
|
||||
});
|
||||
def(["toHaveBeenCalled", "toBeCalled"], function() {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const callCount = spy.mock.calls.length;
|
||||
const called = callCount > 0;
|
||||
const isNot = utils.flag(this, "negate");
|
||||
let msg = utils.getMessage(
|
||||
this,
|
||||
[
|
||||
called,
|
||||
`expected "${spyName}" to be called at least once`,
|
||||
`expected "${spyName}" to not be called at all, but actually been called ${callCount} times`,
|
||||
true,
|
||||
called
|
||||
]
|
||||
);
|
||||
if (called && isNot)
|
||||
msg = formatCalls(spy, msg);
|
||||
if (called && isNot || !called && !isNot)
|
||||
throw new AssertionError(msg);
|
||||
});
|
||||
def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const pass = spy.mock.calls.some((callArg) => equals(callArg, args, [...customTesters, iterableEquality]));
|
||||
const isNot = utils.flag(this, "negate");
|
||||
const msg = utils.getMessage(
|
||||
this,
|
||||
[
|
||||
pass,
|
||||
`expected "${spyName}" to be called with arguments: #{exp}`,
|
||||
`expected "${spyName}" to not be called with arguments: #{exp}`,
|
||||
args
|
||||
]
|
||||
);
|
||||
if (pass && isNot || !pass && !isNot)
|
||||
throw new AssertionError(formatCalls(spy, msg, args));
|
||||
});
|
||||
def(["toHaveBeenNthCalledWith", "nthCalledWith"], function(times, ...args) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const nthCall = spy.mock.calls[times - 1];
|
||||
const callCount = spy.mock.calls.length;
|
||||
const isCalled = times <= callCount;
|
||||
this.assert(
|
||||
equals(nthCall, args, [...customTesters, iterableEquality]),
|
||||
`expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`,
|
||||
`expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`,
|
||||
args,
|
||||
nthCall,
|
||||
isCalled
|
||||
);
|
||||
});
|
||||
def(["toHaveBeenLastCalledWith", "lastCalledWith"], function(...args) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const lastCall = spy.mock.calls[spy.mock.calls.length - 1];
|
||||
this.assert(
|
||||
equals(lastCall, args, [...customTesters, iterableEquality]),
|
||||
`expected last "${spyName}" call to have been called with #{exp}`,
|
||||
`expected last "${spyName}" call to not have been called with #{exp}`,
|
||||
args,
|
||||
lastCall
|
||||
);
|
||||
});
|
||||
def(["toThrow", "toThrowError"], function(expected) {
|
||||
if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp)
|
||||
return this.throws(expected);
|
||||
const obj = this._obj;
|
||||
const promise = utils.flag(this, "promise");
|
||||
const isNot = utils.flag(this, "negate");
|
||||
let thrown = null;
|
||||
if (promise === "rejects") {
|
||||
thrown = obj;
|
||||
} else if (promise === "resolves" && typeof obj !== "function") {
|
||||
if (!isNot) {
|
||||
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
||||
const error = {
|
||||
showDiff: false
|
||||
};
|
||||
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let isThrow = false;
|
||||
try {
|
||||
obj();
|
||||
} catch (err) {
|
||||
isThrow = true;
|
||||
thrown = err;
|
||||
}
|
||||
if (!isThrow && !isNot) {
|
||||
const message = utils.flag(this, "message") || "expected function to throw an error, but it didn't";
|
||||
const error = {
|
||||
showDiff: false
|
||||
};
|
||||
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
||||
}
|
||||
}
|
||||
if (typeof expected === "function") {
|
||||
const name = expected.name || expected.prototype.constructor.name;
|
||||
return this.assert(
|
||||
thrown && thrown instanceof expected,
|
||||
`expected error to be instance of ${name}`,
|
||||
`expected error not to be instance of ${name}`,
|
||||
expected,
|
||||
thrown
|
||||
);
|
||||
}
|
||||
if (expected instanceof Error) {
|
||||
return this.assert(
|
||||
thrown && expected.message === thrown.message,
|
||||
`expected error to have message: ${expected.message}`,
|
||||
`expected error not to have message: ${expected.message}`,
|
||||
expected.message,
|
||||
thrown && thrown.message
|
||||
);
|
||||
}
|
||||
if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") {
|
||||
const matcher = expected;
|
||||
return this.assert(
|
||||
thrown && matcher.asymmetricMatch(thrown),
|
||||
"expected error to match asymmetric matcher",
|
||||
"expected error not to match asymmetric matcher",
|
||||
matcher,
|
||||
thrown
|
||||
);
|
||||
}
|
||||
throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`);
|
||||
});
|
||||
def(["toHaveReturned", "toReturn"], function() {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const calledAndNotThrew = spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw");
|
||||
this.assert(
|
||||
calledAndNotThrew,
|
||||
`expected "${spyName}" to be successfully called at least once`,
|
||||
`expected "${spyName}" to not be successfully called`,
|
||||
calledAndNotThrew,
|
||||
!calledAndNotThrew,
|
||||
false
|
||||
);
|
||||
});
|
||||
def(["toHaveReturnedTimes", "toReturnTimes"], function(times) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const successfulReturns = spy.mock.results.reduce((success, { type }) => type === "throw" ? success : ++success, 0);
|
||||
this.assert(
|
||||
successfulReturns === times,
|
||||
`expected "${spyName}" to be successfully called ${times} times`,
|
||||
`expected "${spyName}" to not be successfully called ${times} times`,
|
||||
`expected number of returns: ${times}`,
|
||||
`received number of returns: ${successfulReturns}`,
|
||||
false
|
||||
);
|
||||
});
|
||||
def(["toHaveReturnedWith", "toReturnWith"], function(value) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const pass = spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result));
|
||||
const isNot = utils.flag(this, "negate");
|
||||
const msg = utils.getMessage(
|
||||
this,
|
||||
[
|
||||
pass,
|
||||
`expected "${spyName}" to return with: #{exp} at least once`,
|
||||
`expected "${spyName}" to not return with: #{exp}`,
|
||||
value
|
||||
]
|
||||
);
|
||||
if (pass && isNot || !pass && !isNot)
|
||||
throw new AssertionError(formatReturns(spy, msg, value));
|
||||
});
|
||||
def(["toHaveLastReturnedWith", "lastReturnedWith"], function(value) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const { value: lastResult } = spy.mock.results[spy.mock.results.length - 1];
|
||||
const pass = equals(lastResult, value);
|
||||
this.assert(
|
||||
pass,
|
||||
`expected last "${spyName}" call to return #{exp}`,
|
||||
`expected last "${spyName}" call to not return #{exp}`,
|
||||
value,
|
||||
lastResult
|
||||
);
|
||||
});
|
||||
def(["toHaveNthReturnedWith", "nthReturnedWith"], function(nthCall, value) {
|
||||
const spy = getSpy(this);
|
||||
const spyName = spy.getMockName();
|
||||
const isNot = utils.flag(this, "negate");
|
||||
const { type: callType, value: callResult } = spy.mock.results[nthCall - 1];
|
||||
const ordinalCall = `${ordinalOf(nthCall)} call`;
|
||||
if (!isNot && callType === "throw")
|
||||
chai.assert.fail(`expected ${ordinalCall} to return #{exp}, but instead it threw an error`);
|
||||
const nthCallReturn = equals(callResult, value);
|
||||
this.assert(
|
||||
nthCallReturn,
|
||||
`expected ${ordinalCall} "${spyName}" call to return #{exp}`,
|
||||
`expected ${ordinalCall} "${spyName}" call to not return #{exp}`,
|
||||
value,
|
||||
callResult
|
||||
);
|
||||
});
|
||||
def("toSatisfy", function(matcher, message) {
|
||||
return this.be.satisfy(matcher, message);
|
||||
});
|
||||
utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
|
||||
const error = new Error("resolves");
|
||||
utils.flag(this, "promise", "resolves");
|
||||
utils.flag(this, "error", error);
|
||||
const test = utils.flag(this, "vitest-test");
|
||||
const obj = utils.flag(this, "object");
|
||||
if (typeof (obj == null ? void 0 : obj.then) !== "function")
|
||||
throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
|
||||
const proxy = new Proxy(this, {
|
||||
get: (target, key, receiver) => {
|
||||
const result = Reflect.get(target, key, receiver);
|
||||
if (typeof result !== "function")
|
||||
return result instanceof chai.Assertion ? proxy : result;
|
||||
return async (...args) => {
|
||||
const promise = obj.then(
|
||||
(value) => {
|
||||
utils.flag(this, "object", value);
|
||||
return result.call(this, ...args);
|
||||
},
|
||||
(err) => {
|
||||
const _error = new AssertionError(
|
||||
`promise rejected "${utils.inspect(err)}" instead of resolving`,
|
||||
{ showDiff: false }
|
||||
);
|
||||
_error.cause = err;
|
||||
_error.stack = error.stack.replace(error.message, _error.message);
|
||||
throw _error;
|
||||
}
|
||||
);
|
||||
return recordAsyncExpect(test, promise);
|
||||
};
|
||||
}
|
||||
});
|
||||
return proxy;
|
||||
});
|
||||
utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() {
|
||||
const error = new Error("rejects");
|
||||
utils.flag(this, "promise", "rejects");
|
||||
utils.flag(this, "error", error);
|
||||
const test = utils.flag(this, "vitest-test");
|
||||
const obj = utils.flag(this, "object");
|
||||
const wrapper = typeof obj === "function" ? obj() : obj;
|
||||
if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function")
|
||||
throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
|
||||
const proxy = new Proxy(this, {
|
||||
get: (target, key, receiver) => {
|
||||
const result = Reflect.get(target, key, receiver);
|
||||
if (typeof result !== "function")
|
||||
return result instanceof chai.Assertion ? proxy : result;
|
||||
return async (...args) => {
|
||||
const promise = wrapper.then(
|
||||
(value) => {
|
||||
const _error = new AssertionError(
|
||||
`promise resolved "${utils.inspect(value)}" instead of rejecting`,
|
||||
{ showDiff: true, expected: new Error("rejected promise"), actual: value }
|
||||
);
|
||||
_error.stack = error.stack.replace(error.message, _error.message);
|
||||
throw _error;
|
||||
},
|
||||
(err) => {
|
||||
utils.flag(this, "object", err);
|
||||
return result.call(this, ...args);
|
||||
}
|
||||
);
|
||||
return recordAsyncExpect(test, promise);
|
||||
};
|
||||
}
|
||||
});
|
||||
return proxy;
|
||||
});
|
||||
};
|
||||
|
||||
function getMatcherState(assertion, expect) {
|
||||
const obj = assertion._obj;
|
||||
const isNot = util.flag(assertion, "negate");
|
||||
const promise = util.flag(assertion, "promise") || "";
|
||||
const jestUtils = {
|
||||
...getMatcherUtils(),
|
||||
diff,
|
||||
stringify,
|
||||
iterableEquality,
|
||||
subsetEquality
|
||||
};
|
||||
const matcherState = {
|
||||
...getState(expect),
|
||||
customTesters: getCustomEqualityTesters(),
|
||||
isNot,
|
||||
utils: jestUtils,
|
||||
promise,
|
||||
equals,
|
||||
// needed for built-in jest-snapshots, but we don't use it
|
||||
suppressedErrors: []
|
||||
};
|
||||
return {
|
||||
state: matcherState,
|
||||
isNot,
|
||||
obj
|
||||
};
|
||||
}
|
||||
class JestExtendError extends Error {
|
||||
constructor(message, actual, expected) {
|
||||
super(message);
|
||||
this.actual = actual;
|
||||
this.expected = expected;
|
||||
}
|
||||
}
|
||||
function JestExtendPlugin(expect, matchers) {
|
||||
return (c, utils) => {
|
||||
Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => {
|
||||
function expectWrapper(...args) {
|
||||
const { state, isNot, obj } = getMatcherState(this, expect);
|
||||
const result = expectAssertion.call(state, obj, ...args);
|
||||
if (result && typeof result === "object" && result instanceof Promise) {
|
||||
return result.then(({ pass: pass2, message: message2, actual: actual2, expected: expected2 }) => {
|
||||
if (pass2 && isNot || !pass2 && !isNot)
|
||||
throw new JestExtendError(message2(), actual2, expected2);
|
||||
});
|
||||
}
|
||||
const { pass, message, actual, expected } = result;
|
||||
if (pass && isNot || !pass && !isNot)
|
||||
throw new JestExtendError(message(), actual, expected);
|
||||
}
|
||||
const softWrapper = wrapSoft(utils, expectWrapper);
|
||||
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, softWrapper);
|
||||
utils.addMethod(c.Assertion.prototype, expectAssertionName, softWrapper);
|
||||
class CustomMatcher extends AsymmetricMatcher {
|
||||
constructor(inverse = false, ...sample) {
|
||||
super(sample, inverse);
|
||||
}
|
||||
asymmetricMatch(other) {
|
||||
const { pass } = expectAssertion.call(
|
||||
this.getMatcherContext(expect),
|
||||
other,
|
||||
...this.sample
|
||||
);
|
||||
return this.inverse ? !pass : pass;
|
||||
}
|
||||
toString() {
|
||||
return `${this.inverse ? "not." : ""}${expectAssertionName}`;
|
||||
}
|
||||
getExpectedType() {
|
||||
return "any";
|
||||
}
|
||||
toAsymmetricMatcher() {
|
||||
return `${this.toString()}<${this.sample.map(String).join(", ")}>`;
|
||||
}
|
||||
}
|
||||
const customMatcher = (...sample) => new CustomMatcher(false, ...sample);
|
||||
Object.defineProperty(expect, expectAssertionName, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: customMatcher,
|
||||
writable: true
|
||||
});
|
||||
Object.defineProperty(expect.not, expectAssertionName, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: (...sample) => new CustomMatcher(true, ...sample),
|
||||
writable: true
|
||||
});
|
||||
Object.defineProperty(globalThis[ASYMMETRIC_MATCHERS_OBJECT], expectAssertionName, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: customMatcher,
|
||||
writable: true
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
const JestExtend = (chai, utils) => {
|
||||
utils.addMethod(chai.expect, "extend", (expect, expects) => {
|
||||
chai.use(JestExtendPlugin(expect, expects));
|
||||
});
|
||||
};
|
||||
|
||||
export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import './dist/chai.cjs'
|
||||
|
||||
export * from './dist/index.js'
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@vitest/expect",
|
||||
"type": "module",
|
||||
"version": "1.6.1",
|
||||
"description": "Jest's expect matchers as a Chai plugin",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/expect#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/expect"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./index.d.ts",
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"chai": "^4.3.10",
|
||||
"@vitest/utils": "1.6.1",
|
||||
"@vitest/spy": "1.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "4.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"@vitest/runner": "1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c",
|
||||
"dev": "rollup -c --watch"
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-Present Vitest Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
# @vitest/runner
|
||||
|
||||
Vitest mechanism to collect and run tasks.
|
||||
|
||||
[GitHub](https://github.com/vitest-dev/vitest) | [Documentation](https://vitest.dev/advanced/runner)
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
import { processError } from '@vitest/utils/error';
|
||||
import { toArray } from '@vitest/utils';
|
||||
|
||||
function partitionSuiteChildren(suite) {
|
||||
let tasksGroup = [];
|
||||
const tasksGroups = [];
|
||||
for (const c of suite.tasks) {
|
||||
if (tasksGroup.length === 0 || c.concurrent === tasksGroup[0].concurrent) {
|
||||
tasksGroup.push(c);
|
||||
} else {
|
||||
tasksGroups.push(tasksGroup);
|
||||
tasksGroup = [c];
|
||||
}
|
||||
}
|
||||
if (tasksGroup.length > 0)
|
||||
tasksGroups.push(tasksGroup);
|
||||
return tasksGroups;
|
||||
}
|
||||
|
||||
function interpretTaskModes(suite, namePattern, onlyMode, parentIsOnly, allowOnly) {
|
||||
const suiteIsOnly = parentIsOnly || suite.mode === "only";
|
||||
suite.tasks.forEach((t) => {
|
||||
const includeTask = suiteIsOnly || t.mode === "only";
|
||||
if (onlyMode) {
|
||||
if (t.type === "suite" && (includeTask || someTasksAreOnly(t))) {
|
||||
if (t.mode === "only") {
|
||||
checkAllowOnly(t, allowOnly);
|
||||
t.mode = "run";
|
||||
}
|
||||
} else if (t.mode === "run" && !includeTask) {
|
||||
t.mode = "skip";
|
||||
} else if (t.mode === "only") {
|
||||
checkAllowOnly(t, allowOnly);
|
||||
t.mode = "run";
|
||||
}
|
||||
}
|
||||
if (t.type === "test") {
|
||||
if (namePattern && !getTaskFullName(t).match(namePattern))
|
||||
t.mode = "skip";
|
||||
} else if (t.type === "suite") {
|
||||
if (t.mode === "skip")
|
||||
skipAllTasks(t);
|
||||
else
|
||||
interpretTaskModes(t, namePattern, onlyMode, includeTask, allowOnly);
|
||||
}
|
||||
});
|
||||
if (suite.mode === "run") {
|
||||
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run"))
|
||||
suite.mode = "skip";
|
||||
}
|
||||
}
|
||||
function getTaskFullName(task) {
|
||||
return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
|
||||
}
|
||||
function someTasksAreOnly(suite) {
|
||||
return suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
|
||||
}
|
||||
function skipAllTasks(suite) {
|
||||
suite.tasks.forEach((t) => {
|
||||
if (t.mode === "run") {
|
||||
t.mode = "skip";
|
||||
if (t.type === "suite")
|
||||
skipAllTasks(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkAllowOnly(task, allowOnly) {
|
||||
if (allowOnly)
|
||||
return;
|
||||
const error = processError(new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"));
|
||||
task.result = {
|
||||
state: "fail",
|
||||
errors: [error]
|
||||
};
|
||||
}
|
||||
function generateHash(str) {
|
||||
let hash = 0;
|
||||
if (str.length === 0)
|
||||
return `${hash}`;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash;
|
||||
}
|
||||
return `${hash}`;
|
||||
}
|
||||
function calculateSuiteHash(parent) {
|
||||
parent.tasks.forEach((t, idx) => {
|
||||
t.id = `${parent.id}_${idx}`;
|
||||
if (t.type === "suite")
|
||||
calculateSuiteHash(t);
|
||||
});
|
||||
}
|
||||
|
||||
function createChainable(keys, fn) {
|
||||
function create(context) {
|
||||
const chain2 = function(...args) {
|
||||
return fn.apply(context, args);
|
||||
};
|
||||
Object.assign(chain2, fn);
|
||||
chain2.withContext = () => chain2.bind(context);
|
||||
chain2.setContext = (key, value) => {
|
||||
context[key] = value;
|
||||
};
|
||||
chain2.mergeContext = (ctx) => {
|
||||
Object.assign(context, ctx);
|
||||
};
|
||||
for (const key of keys) {
|
||||
Object.defineProperty(chain2, key, {
|
||||
get() {
|
||||
return create({ ...context, [key]: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
return chain2;
|
||||
}
|
||||
const chain = create({});
|
||||
chain.fn = fn;
|
||||
return chain;
|
||||
}
|
||||
|
||||
function isAtomTest(s) {
|
||||
return s.type === "test" || s.type === "custom";
|
||||
}
|
||||
function getTests(suite) {
|
||||
const tests = [];
|
||||
const arraySuites = toArray(suite);
|
||||
for (const s of arraySuites) {
|
||||
if (isAtomTest(s)) {
|
||||
tests.push(s);
|
||||
} else {
|
||||
for (const task of s.tasks) {
|
||||
if (isAtomTest(task)) {
|
||||
tests.push(task);
|
||||
} else {
|
||||
const taskTests = getTests(task);
|
||||
for (const test of taskTests)
|
||||
tests.push(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
function getTasks(tasks = []) {
|
||||
return toArray(tasks).flatMap((s) => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]);
|
||||
}
|
||||
function getSuites(suite) {
|
||||
return toArray(suite).flatMap((s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []);
|
||||
}
|
||||
function hasTests(suite) {
|
||||
return toArray(suite).some((s) => s.tasks.some((c) => isAtomTest(c) || hasTests(c)));
|
||||
}
|
||||
function hasFailed(suite) {
|
||||
return toArray(suite).some((s) => {
|
||||
var _a;
|
||||
return ((_a = s.result) == null ? void 0 : _a.state) === "fail" || s.type === "suite" && hasFailed(s.tasks);
|
||||
});
|
||||
}
|
||||
function getNames(task) {
|
||||
const names = [task.name];
|
||||
let current = task;
|
||||
while ((current == null ? void 0 : current.suite) || (current == null ? void 0 : current.file)) {
|
||||
current = current.suite || current.file;
|
||||
if (current == null ? void 0 : current.name)
|
||||
names.unshift(current.name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
export { getTests as a, getTasks as b, calculateSuiteHash as c, getSuites as d, hasFailed as e, getNames as f, generateHash as g, hasTests as h, interpretTaskModes as i, createChainable as j, partitionSuiteChildren as p, someTasksAreOnly as s };
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { VitestRunner } from './types.js';
|
||||
export { CancelReason, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource } from './types.js';
|
||||
import { T as Task, F as File, d as SuiteAPI, e as TestAPI, f as SuiteCollector, g as CustomAPI, h as SuiteHooks, O as OnTestFailedHandler, i as OnTestFinishedHandler, a as Test, C as Custom, S as Suite } from './tasks-K5XERDtv.js';
|
||||
export { D as DoneCallback, E as ExtendedContext, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, R as RunMode, y as RuntimeContext, B as SequenceHooks, G as SequenceSetupFiles, x as SuiteFactory, k as TaskBase, A as TaskContext, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, o as TaskResultPack, j as TaskState, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-K5XERDtv.js';
|
||||
import { Awaitable } from '@vitest/utils';
|
||||
export { processError } from '@vitest/utils/error';
|
||||
import '@vitest/utils/diff';
|
||||
|
||||
declare function updateTask(task: Task, runner: VitestRunner): void;
|
||||
declare function startTests(paths: string[], runner: VitestRunner): Promise<File[]>;
|
||||
|
||||
declare const suite: SuiteAPI;
|
||||
declare const test: TestAPI;
|
||||
declare const describe: SuiteAPI;
|
||||
declare const it: TestAPI;
|
||||
declare function getCurrentSuite<ExtraContext = {}>(): SuiteCollector<ExtraContext>;
|
||||
declare function createTaskCollector(fn: (...args: any[]) => any, context?: Record<string, unknown>): CustomAPI;
|
||||
|
||||
declare function beforeAll(fn: SuiteHooks['beforeAll'][0], timeout?: number): void;
|
||||
declare function afterAll(fn: SuiteHooks['afterAll'][0], timeout?: number): void;
|
||||
declare function beforeEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['beforeEach'][0], timeout?: number): void;
|
||||
declare function afterEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['afterEach'][0], timeout?: number): void;
|
||||
declare const onTestFailed: (fn: OnTestFailedHandler) => void;
|
||||
declare const onTestFinished: (fn: OnTestFinishedHandler) => void;
|
||||
|
||||
declare function setFn(key: Test | Custom, fn: (() => Awaitable<void>)): void;
|
||||
declare function getFn<Task = Test | Custom>(key: Task): (() => Awaitable<void>);
|
||||
declare function setHooks(key: Suite, hooks: SuiteHooks): void;
|
||||
declare function getHooks(key: Suite): SuiteHooks;
|
||||
|
||||
declare function getCurrentTest<T extends Test | Custom | undefined>(): T;
|
||||
|
||||
export { Custom, CustomAPI, File, OnTestFailedHandler, OnTestFinishedHandler, Suite, SuiteAPI, SuiteCollector, SuiteHooks, Task, Test, TestAPI, VitestRunner, afterAll, afterEach, beforeAll, beforeEach, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
|
||||
+1005
@@ -0,0 +1,1005 @@
|
||||
import limit from 'p-limit';
|
||||
import { getSafeTimers, isObject, createDefer, format, objDisplay, objectAttr, toArray, shuffle } from '@vitest/utils';
|
||||
import { processError } from '@vitest/utils/error';
|
||||
export { processError } from '@vitest/utils/error';
|
||||
import { j as createChainable, g as generateHash, c as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, p as partitionSuiteChildren, h as hasTests, e as hasFailed } from './chunk-tasks.js';
|
||||
import { relative } from 'pathe';
|
||||
import { parseSingleStack } from '@vitest/utils/source-map';
|
||||
|
||||
const fnMap = /* @__PURE__ */ new WeakMap();
|
||||
const fixtureMap = /* @__PURE__ */ new WeakMap();
|
||||
const hooksMap = /* @__PURE__ */ new WeakMap();
|
||||
function setFn(key, fn) {
|
||||
fnMap.set(key, fn);
|
||||
}
|
||||
function getFn(key) {
|
||||
return fnMap.get(key);
|
||||
}
|
||||
function setFixture(key, fixture) {
|
||||
fixtureMap.set(key, fixture);
|
||||
}
|
||||
function getFixture(key) {
|
||||
return fixtureMap.get(key);
|
||||
}
|
||||
function setHooks(key, hooks) {
|
||||
hooksMap.set(key, hooks);
|
||||
}
|
||||
function getHooks(key) {
|
||||
return hooksMap.get(key);
|
||||
}
|
||||
|
||||
class PendingError extends Error {
|
||||
constructor(message, task) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.taskId = task.id;
|
||||
}
|
||||
code = "VITEST_PENDING";
|
||||
taskId;
|
||||
}
|
||||
|
||||
const collectorContext = {
|
||||
tasks: [],
|
||||
currentSuite: null
|
||||
};
|
||||
function collectTask(task) {
|
||||
var _a;
|
||||
(_a = collectorContext.currentSuite) == null ? void 0 : _a.tasks.push(task);
|
||||
}
|
||||
async function runWithSuite(suite, fn) {
|
||||
const prev = collectorContext.currentSuite;
|
||||
collectorContext.currentSuite = suite;
|
||||
await fn();
|
||||
collectorContext.currentSuite = prev;
|
||||
}
|
||||
function withTimeout(fn, timeout, isHook = false) {
|
||||
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY)
|
||||
return fn;
|
||||
const { setTimeout, clearTimeout } = getSafeTimers();
|
||||
return (...args) => {
|
||||
return Promise.race([fn(...args), new Promise((resolve, reject) => {
|
||||
var _a;
|
||||
const timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
reject(new Error(makeTimeoutMsg(isHook, timeout)));
|
||||
}, timeout);
|
||||
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
||||
})]);
|
||||
};
|
||||
}
|
||||
function createTestContext(test, runner) {
|
||||
var _a;
|
||||
const context = function() {
|
||||
throw new Error("done() callback is deprecated, use promise instead");
|
||||
};
|
||||
context.task = test;
|
||||
context.skip = () => {
|
||||
test.pending = true;
|
||||
throw new PendingError("test is skipped; abort execution", test);
|
||||
};
|
||||
context.onTestFailed = (fn) => {
|
||||
test.onFailed || (test.onFailed = []);
|
||||
test.onFailed.push(fn);
|
||||
};
|
||||
context.onTestFinished = (fn) => {
|
||||
test.onFinished || (test.onFinished = []);
|
||||
test.onFinished.push(fn);
|
||||
};
|
||||
return ((_a = runner.extendTaskContext) == null ? void 0 : _a.call(runner, context)) || context;
|
||||
}
|
||||
function makeTimeoutMsg(isHook, timeout) {
|
||||
return `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.
|
||||
If this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
|
||||
}
|
||||
|
||||
function mergeContextFixtures(fixtures, context = {}) {
|
||||
const fixtureOptionKeys = ["auto"];
|
||||
const fixtureArray = Object.entries(fixtures).map(([prop, value]) => {
|
||||
const fixtureItem = { value };
|
||||
if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
|
||||
Object.assign(fixtureItem, value[1]);
|
||||
fixtureItem.value = value[0];
|
||||
}
|
||||
fixtureItem.prop = prop;
|
||||
fixtureItem.isFn = typeof fixtureItem.value === "function";
|
||||
return fixtureItem;
|
||||
});
|
||||
if (Array.isArray(context.fixtures))
|
||||
context.fixtures = context.fixtures.concat(fixtureArray);
|
||||
else
|
||||
context.fixtures = fixtureArray;
|
||||
fixtureArray.forEach((fixture) => {
|
||||
if (fixture.isFn) {
|
||||
const usedProps = getUsedProps(fixture.value);
|
||||
if (usedProps.length)
|
||||
fixture.deps = context.fixtures.filter(({ prop }) => prop !== fixture.prop && usedProps.includes(prop));
|
||||
}
|
||||
});
|
||||
return context;
|
||||
}
|
||||
const fixtureValueMaps = /* @__PURE__ */ new Map();
|
||||
const cleanupFnArrayMap = /* @__PURE__ */ new Map();
|
||||
async function callFixtureCleanup(context) {
|
||||
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
|
||||
for (const cleanup of cleanupFnArray.reverse())
|
||||
await cleanup();
|
||||
cleanupFnArrayMap.delete(context);
|
||||
}
|
||||
function withFixtures(fn, testContext) {
|
||||
return (hookContext) => {
|
||||
const context = hookContext || testContext;
|
||||
if (!context)
|
||||
return fn({});
|
||||
const fixtures = getFixture(context);
|
||||
if (!(fixtures == null ? void 0 : fixtures.length))
|
||||
return fn(context);
|
||||
const usedProps = getUsedProps(fn);
|
||||
const hasAutoFixture = fixtures.some(({ auto }) => auto);
|
||||
if (!usedProps.length && !hasAutoFixture)
|
||||
return fn(context);
|
||||
if (!fixtureValueMaps.get(context))
|
||||
fixtureValueMaps.set(context, /* @__PURE__ */ new Map());
|
||||
const fixtureValueMap = fixtureValueMaps.get(context);
|
||||
if (!cleanupFnArrayMap.has(context))
|
||||
cleanupFnArrayMap.set(context, []);
|
||||
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
||||
const usedFixtures = fixtures.filter(({ prop, auto }) => auto || usedProps.includes(prop));
|
||||
const pendingFixtures = resolveDeps(usedFixtures);
|
||||
if (!pendingFixtures.length)
|
||||
return fn(context);
|
||||
async function resolveFixtures() {
|
||||
for (const fixture of pendingFixtures) {
|
||||
if (fixtureValueMap.has(fixture))
|
||||
continue;
|
||||
const resolvedValue = fixture.isFn ? await resolveFixtureFunction(fixture.value, context, cleanupFnArray) : fixture.value;
|
||||
context[fixture.prop] = resolvedValue;
|
||||
fixtureValueMap.set(fixture, resolvedValue);
|
||||
cleanupFnArray.unshift(() => {
|
||||
fixtureValueMap.delete(fixture);
|
||||
});
|
||||
}
|
||||
}
|
||||
return resolveFixtures().then(() => fn(context));
|
||||
};
|
||||
}
|
||||
async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
||||
const useFnArgPromise = createDefer();
|
||||
let isUseFnArgResolved = false;
|
||||
const fixtureReturn = fixtureFn(context, async (useFnArg) => {
|
||||
isUseFnArgResolved = true;
|
||||
useFnArgPromise.resolve(useFnArg);
|
||||
const useReturnPromise = createDefer();
|
||||
cleanupFnArray.push(async () => {
|
||||
useReturnPromise.resolve();
|
||||
await fixtureReturn;
|
||||
});
|
||||
await useReturnPromise;
|
||||
}).catch((e) => {
|
||||
if (!isUseFnArgResolved) {
|
||||
useFnArgPromise.reject(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
return useFnArgPromise;
|
||||
}
|
||||
function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtures = []) {
|
||||
fixtures.forEach((fixture) => {
|
||||
if (pendingFixtures.includes(fixture))
|
||||
return;
|
||||
if (!fixture.isFn || !fixture.deps) {
|
||||
pendingFixtures.push(fixture);
|
||||
return;
|
||||
}
|
||||
if (depSet.has(fixture))
|
||||
throw new Error(`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`);
|
||||
depSet.add(fixture);
|
||||
resolveDeps(fixture.deps, depSet, pendingFixtures);
|
||||
pendingFixtures.push(fixture);
|
||||
depSet.clear();
|
||||
});
|
||||
return pendingFixtures;
|
||||
}
|
||||
function getUsedProps(fn) {
|
||||
const match = fn.toString().match(/[^(]*\(([^)]*)/);
|
||||
if (!match)
|
||||
return [];
|
||||
const args = splitByComma(match[1]);
|
||||
if (!args.length)
|
||||
return [];
|
||||
const first = args[0];
|
||||
if (!(first.startsWith("{") && first.endsWith("}")))
|
||||
throw new Error(`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`);
|
||||
const _first = first.slice(1, -1).replace(/\s/g, "");
|
||||
const props = splitByComma(_first).map((prop) => {
|
||||
return prop.replace(/\:.*|\=.*/g, "");
|
||||
});
|
||||
const last = props.at(-1);
|
||||
if (last && last.startsWith("..."))
|
||||
throw new Error(`Rest parameters are not supported in fixtures, received "${last}".`);
|
||||
return props;
|
||||
}
|
||||
function splitByComma(s) {
|
||||
const result = [];
|
||||
const stack = [];
|
||||
let start = 0;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
if (s[i] === "{" || s[i] === "[") {
|
||||
stack.push(s[i] === "{" ? "}" : "]");
|
||||
} else if (s[i] === stack[stack.length - 1]) {
|
||||
stack.pop();
|
||||
} else if (!stack.length && s[i] === ",") {
|
||||
const token = s.substring(start, i).trim();
|
||||
if (token)
|
||||
result.push(token);
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
const lastToken = s.substring(start).trim();
|
||||
if (lastToken)
|
||||
result.push(lastToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
let _test;
|
||||
function setCurrentTest(test) {
|
||||
_test = test;
|
||||
}
|
||||
function getCurrentTest() {
|
||||
return _test;
|
||||
}
|
||||
|
||||
const suite = createSuite();
|
||||
const test = createTest(
|
||||
function(name, optionsOrFn, optionsOrTest) {
|
||||
if (getCurrentTest())
|
||||
throw new Error('Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.');
|
||||
getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
|
||||
}
|
||||
);
|
||||
const describe = suite;
|
||||
const it = test;
|
||||
let runner;
|
||||
let defaultSuite;
|
||||
let currentTestFilepath;
|
||||
function getDefaultSuite() {
|
||||
return defaultSuite;
|
||||
}
|
||||
function getTestFilepath() {
|
||||
return currentTestFilepath;
|
||||
}
|
||||
function getRunner() {
|
||||
return runner;
|
||||
}
|
||||
function clearCollectorContext(filepath, currentRunner) {
|
||||
if (!defaultSuite)
|
||||
defaultSuite = currentRunner.config.sequence.shuffle ? suite.shuffle("") : currentRunner.config.sequence.concurrent ? suite.concurrent("") : suite("");
|
||||
runner = currentRunner;
|
||||
currentTestFilepath = filepath;
|
||||
collectorContext.tasks.length = 0;
|
||||
defaultSuite.clear();
|
||||
collectorContext.currentSuite = defaultSuite;
|
||||
}
|
||||
function getCurrentSuite() {
|
||||
return collectorContext.currentSuite || defaultSuite;
|
||||
}
|
||||
function createSuiteHooks() {
|
||||
return {
|
||||
beforeAll: [],
|
||||
afterAll: [],
|
||||
beforeEach: [],
|
||||
afterEach: []
|
||||
};
|
||||
}
|
||||
function parseArguments(optionsOrFn, optionsOrTest) {
|
||||
let options = {};
|
||||
let fn = () => {
|
||||
};
|
||||
if (typeof optionsOrTest === "object") {
|
||||
if (typeof optionsOrFn === "object")
|
||||
throw new TypeError("Cannot use two objects as arguments. Please provide options and a function callback in that order.");
|
||||
options = optionsOrTest;
|
||||
} else if (typeof optionsOrTest === "number") {
|
||||
options = { timeout: optionsOrTest };
|
||||
} else if (typeof optionsOrFn === "object") {
|
||||
options = optionsOrFn;
|
||||
}
|
||||
if (typeof optionsOrFn === "function") {
|
||||
if (typeof optionsOrTest === "function")
|
||||
throw new TypeError("Cannot use two functions as arguments. Please use the second argument for options.");
|
||||
fn = optionsOrFn;
|
||||
} else if (typeof optionsOrTest === "function") {
|
||||
fn = optionsOrTest;
|
||||
}
|
||||
return {
|
||||
options,
|
||||
handler: fn
|
||||
};
|
||||
}
|
||||
function createSuiteCollector(name, factory = () => {
|
||||
}, mode, shuffle, each, suiteOptions) {
|
||||
const tasks = [];
|
||||
const factoryQueue = [];
|
||||
let suite2;
|
||||
initSuite(true);
|
||||
const task = function(name2 = "", options = {}) {
|
||||
const task2 = {
|
||||
id: "",
|
||||
name: name2,
|
||||
suite: void 0,
|
||||
each: options.each,
|
||||
fails: options.fails,
|
||||
context: void 0,
|
||||
type: "custom",
|
||||
retry: options.retry ?? runner.config.retry,
|
||||
repeats: options.repeats,
|
||||
mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
|
||||
meta: options.meta ?? /* @__PURE__ */ Object.create(null)
|
||||
};
|
||||
const handler = options.handler;
|
||||
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent)
|
||||
task2.concurrent = true;
|
||||
if (shuffle)
|
||||
task2.shuffle = true;
|
||||
const context = createTestContext(task2, runner);
|
||||
Object.defineProperty(task2, "context", {
|
||||
value: context,
|
||||
enumerable: false
|
||||
});
|
||||
setFixture(context, options.fixtures);
|
||||
if (handler) {
|
||||
setFn(task2, withTimeout(
|
||||
withFixtures(handler, context),
|
||||
(options == null ? void 0 : options.timeout) ?? runner.config.testTimeout
|
||||
));
|
||||
}
|
||||
if (runner.config.includeTaskLocation) {
|
||||
const limit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 15;
|
||||
const error = new Error("stacktrace").stack;
|
||||
Error.stackTraceLimit = limit;
|
||||
const stack = findTestFileStackTrace(error, task2.each ?? false);
|
||||
if (stack)
|
||||
task2.location = stack;
|
||||
}
|
||||
tasks.push(task2);
|
||||
return task2;
|
||||
};
|
||||
const test2 = createTest(function(name2, optionsOrFn, optionsOrTest) {
|
||||
let { options, handler } = parseArguments(
|
||||
optionsOrFn,
|
||||
optionsOrTest
|
||||
);
|
||||
if (typeof suiteOptions === "object")
|
||||
options = Object.assign({}, suiteOptions, options);
|
||||
options.concurrent = this.concurrent || !this.sequential && (options == null ? void 0 : options.concurrent);
|
||||
options.sequential = this.sequential || !this.concurrent && (options == null ? void 0 : options.sequential);
|
||||
const test3 = task(
|
||||
formatName(name2),
|
||||
{ ...this, ...options, handler }
|
||||
);
|
||||
test3.type = "test";
|
||||
});
|
||||
const collector = {
|
||||
type: "collector",
|
||||
name,
|
||||
mode,
|
||||
options: suiteOptions,
|
||||
test: test2,
|
||||
tasks,
|
||||
collect,
|
||||
task,
|
||||
clear,
|
||||
on: addHook
|
||||
};
|
||||
function addHook(name2, ...fn) {
|
||||
getHooks(suite2)[name2].push(...fn);
|
||||
}
|
||||
function initSuite(includeLocation) {
|
||||
if (typeof suiteOptions === "number")
|
||||
suiteOptions = { timeout: suiteOptions };
|
||||
suite2 = {
|
||||
id: "",
|
||||
type: "suite",
|
||||
name,
|
||||
mode,
|
||||
each,
|
||||
shuffle,
|
||||
tasks: [],
|
||||
meta: /* @__PURE__ */ Object.create(null),
|
||||
projectName: ""
|
||||
};
|
||||
if (runner && includeLocation && runner.config.includeTaskLocation) {
|
||||
const limit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 15;
|
||||
const error = new Error("stacktrace").stack;
|
||||
Error.stackTraceLimit = limit;
|
||||
const stack = findTestFileStackTrace(error, suite2.each ?? false);
|
||||
if (stack)
|
||||
suite2.location = stack;
|
||||
}
|
||||
setHooks(suite2, createSuiteHooks());
|
||||
}
|
||||
function clear() {
|
||||
tasks.length = 0;
|
||||
factoryQueue.length = 0;
|
||||
initSuite(false);
|
||||
}
|
||||
async function collect(file) {
|
||||
factoryQueue.length = 0;
|
||||
if (factory)
|
||||
await runWithSuite(collector, () => factory(test2));
|
||||
const allChildren = [];
|
||||
for (const i of [...factoryQueue, ...tasks])
|
||||
allChildren.push(i.type === "collector" ? await i.collect(file) : i);
|
||||
suite2.file = file;
|
||||
suite2.tasks = allChildren;
|
||||
allChildren.forEach((task2) => {
|
||||
task2.suite = suite2;
|
||||
if (file)
|
||||
task2.file = file;
|
||||
});
|
||||
return suite2;
|
||||
}
|
||||
collectTask(collector);
|
||||
return collector;
|
||||
}
|
||||
function createSuite() {
|
||||
function suiteFn(name, factoryOrOptions, optionsOrFactory = {}) {
|
||||
const mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
|
||||
const currentSuite = getCurrentSuite();
|
||||
let { options, handler: factory } = parseArguments(
|
||||
factoryOrOptions,
|
||||
optionsOrFactory
|
||||
);
|
||||
if (currentSuite == null ? void 0 : currentSuite.options)
|
||||
options = { ...currentSuite.options, ...options };
|
||||
options.concurrent = this.concurrent || !this.sequential && (options == null ? void 0 : options.concurrent);
|
||||
options.sequential = this.sequential || !this.concurrent && (options == null ? void 0 : options.sequential);
|
||||
return createSuiteCollector(formatName(name), factory, mode, this.shuffle, this.each, options);
|
||||
}
|
||||
suiteFn.each = function(cases, ...args) {
|
||||
const suite2 = this.withContext();
|
||||
this.setContext("each", true);
|
||||
if (Array.isArray(cases) && args.length)
|
||||
cases = formatTemplateString(cases, args);
|
||||
return (name, optionsOrFn, fnOrOptions) => {
|
||||
const _name = formatName(name);
|
||||
const arrayOnlyCases = cases.every(Array.isArray);
|
||||
const { options, handler } = parseArguments(
|
||||
optionsOrFn,
|
||||
fnOrOptions
|
||||
);
|
||||
const fnFirst = typeof optionsOrFn === "function";
|
||||
cases.forEach((i, idx) => {
|
||||
const items = Array.isArray(i) ? i : [i];
|
||||
if (fnFirst) {
|
||||
arrayOnlyCases ? suite2(formatTitle(_name, items, idx), () => handler(...items), options) : suite2(formatTitle(_name, items, idx), () => handler(i), options);
|
||||
} else {
|
||||
arrayOnlyCases ? suite2(formatTitle(_name, items, idx), options, () => handler(...items)) : suite2(formatTitle(_name, items, idx), options, () => handler(i));
|
||||
}
|
||||
});
|
||||
this.setContext("each", void 0);
|
||||
};
|
||||
};
|
||||
suiteFn.skipIf = (condition) => condition ? suite.skip : suite;
|
||||
suiteFn.runIf = (condition) => condition ? suite : suite.skip;
|
||||
return createChainable(
|
||||
["concurrent", "sequential", "shuffle", "skip", "only", "todo"],
|
||||
suiteFn
|
||||
);
|
||||
}
|
||||
function createTaskCollector(fn, context) {
|
||||
const taskFn = fn;
|
||||
taskFn.each = function(cases, ...args) {
|
||||
const test2 = this.withContext();
|
||||
this.setContext("each", true);
|
||||
if (Array.isArray(cases) && args.length)
|
||||
cases = formatTemplateString(cases, args);
|
||||
return (name, optionsOrFn, fnOrOptions) => {
|
||||
const _name = formatName(name);
|
||||
const arrayOnlyCases = cases.every(Array.isArray);
|
||||
const { options, handler } = parseArguments(
|
||||
optionsOrFn,
|
||||
fnOrOptions
|
||||
);
|
||||
const fnFirst = typeof optionsOrFn === "function";
|
||||
cases.forEach((i, idx) => {
|
||||
const items = Array.isArray(i) ? i : [i];
|
||||
if (fnFirst) {
|
||||
arrayOnlyCases ? test2(formatTitle(_name, items, idx), () => handler(...items), options) : test2(formatTitle(_name, items, idx), () => handler(i), options);
|
||||
} else {
|
||||
arrayOnlyCases ? test2(formatTitle(_name, items, idx), options, () => handler(...items)) : test2(formatTitle(_name, items, idx), options, () => handler(i));
|
||||
}
|
||||
});
|
||||
this.setContext("each", void 0);
|
||||
};
|
||||
};
|
||||
taskFn.skipIf = function(condition) {
|
||||
return condition ? this.skip : this;
|
||||
};
|
||||
taskFn.runIf = function(condition) {
|
||||
return condition ? this : this.skip;
|
||||
};
|
||||
taskFn.extend = function(fixtures) {
|
||||
const _context = mergeContextFixtures(fixtures, context);
|
||||
return createTest(function fn2(name, optionsOrFn, optionsOrTest) {
|
||||
getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
|
||||
}, _context);
|
||||
};
|
||||
const _test = createChainable(
|
||||
["concurrent", "sequential", "skip", "only", "todo", "fails"],
|
||||
taskFn
|
||||
);
|
||||
if (context)
|
||||
_test.mergeContext(context);
|
||||
return _test;
|
||||
}
|
||||
function createTest(fn, context) {
|
||||
return createTaskCollector(fn, context);
|
||||
}
|
||||
function formatName(name) {
|
||||
return typeof name === "string" ? name : name instanceof Function ? name.name || "<anonymous>" : String(name);
|
||||
}
|
||||
function formatTitle(template, items, idx) {
|
||||
if (template.includes("%#")) {
|
||||
template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/__vitest_escaped_%__/g, "%%");
|
||||
}
|
||||
const count = template.split("%").length - 1;
|
||||
let formatted = format(template, ...items.slice(0, count));
|
||||
if (isObject(items[0])) {
|
||||
formatted = formatted.replace(
|
||||
/\$([$\w_.]+)/g,
|
||||
// https://github.com/chaijs/chai/pull/1490
|
||||
(_, key) => {
|
||||
var _a, _b;
|
||||
return objDisplay(objectAttr(items[0], key), { truncate: (_b = (_a = runner == null ? void 0 : runner.config) == null ? void 0 : _a.chaiConfig) == null ? void 0 : _b.truncateThreshold });
|
||||
}
|
||||
);
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
function formatTemplateString(cases, args) {
|
||||
const header = cases.join("").trim().replace(/ /g, "").split("\n").map((i) => i.split("|"))[0];
|
||||
const res = [];
|
||||
for (let i = 0; i < Math.floor(args.length / header.length); i++) {
|
||||
const oneCase = {};
|
||||
for (let j = 0; j < header.length; j++)
|
||||
oneCase[header[j]] = args[i * header.length + j];
|
||||
res.push(oneCase);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function findTestFileStackTrace(error, each) {
|
||||
const lines = error.split("\n").slice(1);
|
||||
for (const line of lines) {
|
||||
const stack = parseSingleStack(line);
|
||||
if (stack && stack.file === getTestFilepath()) {
|
||||
return {
|
||||
line: stack.line,
|
||||
/**
|
||||
* test.each([1, 2])('name')
|
||||
* ^ leads here, but should
|
||||
* ^ lead here
|
||||
* in source maps it's the same boundary, so it just points to the start of it
|
||||
*/
|
||||
column: each ? stack.column + 1 : stack.column
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runSetupFiles(config, runner) {
|
||||
const files = toArray(config.setupFiles);
|
||||
if (config.sequence.setupFiles === "parallel") {
|
||||
await Promise.all(
|
||||
files.map(async (fsPath) => {
|
||||
await runner.importFile(fsPath, "setup");
|
||||
})
|
||||
);
|
||||
} else {
|
||||
for (const fsPath of files)
|
||||
await runner.importFile(fsPath, "setup");
|
||||
}
|
||||
}
|
||||
|
||||
const now$1 = Date.now;
|
||||
async function collectTests(paths, runner) {
|
||||
const files = [];
|
||||
const config = runner.config;
|
||||
for (const filepath of paths) {
|
||||
const path = relative(config.root, filepath);
|
||||
const file = {
|
||||
id: generateHash(`${path}${config.name || ""}`),
|
||||
name: path,
|
||||
type: "suite",
|
||||
mode: "run",
|
||||
filepath,
|
||||
tasks: [],
|
||||
meta: /* @__PURE__ */ Object.create(null),
|
||||
projectName: config.name
|
||||
};
|
||||
clearCollectorContext(filepath, runner);
|
||||
try {
|
||||
const setupStart = now$1();
|
||||
await runSetupFiles(config, runner);
|
||||
const collectStart = now$1();
|
||||
file.setupDuration = collectStart - setupStart;
|
||||
await runner.importFile(filepath, "collect");
|
||||
const defaultTasks = await getDefaultSuite().collect(file);
|
||||
setHooks(file, getHooks(defaultTasks));
|
||||
for (const c of [...defaultTasks.tasks, ...collectorContext.tasks]) {
|
||||
if (c.type === "test") {
|
||||
file.tasks.push(c);
|
||||
} else if (c.type === "custom") {
|
||||
file.tasks.push(c);
|
||||
} else if (c.type === "suite") {
|
||||
file.tasks.push(c);
|
||||
} else if (c.type === "collector") {
|
||||
const suite = await c.collect(file);
|
||||
if (suite.name || suite.tasks.length)
|
||||
file.tasks.push(suite);
|
||||
}
|
||||
}
|
||||
file.collectDuration = now$1() - collectStart;
|
||||
} catch (e) {
|
||||
const error = processError(e);
|
||||
file.result = {
|
||||
state: "fail",
|
||||
errors: [error]
|
||||
};
|
||||
}
|
||||
calculateSuiteHash(file);
|
||||
const hasOnlyTasks = someTasksAreOnly(file);
|
||||
interpretTaskModes(file, config.testNamePattern, hasOnlyTasks, false, config.allowOnly);
|
||||
files.push(file);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
const now = Date.now;
|
||||
function updateSuiteHookState(suite, name, state, runner) {
|
||||
var _a;
|
||||
if (!suite.result)
|
||||
suite.result = { state: "run" };
|
||||
if (!((_a = suite.result) == null ? void 0 : _a.hooks))
|
||||
suite.result.hooks = {};
|
||||
const suiteHooks = suite.result.hooks;
|
||||
if (suiteHooks) {
|
||||
suiteHooks[name] = state;
|
||||
updateTask(suite, runner);
|
||||
}
|
||||
}
|
||||
function getSuiteHooks(suite, name, sequence) {
|
||||
const hooks = getHooks(suite)[name];
|
||||
if (sequence === "stack" && (name === "afterAll" || name === "afterEach"))
|
||||
return hooks.slice().reverse();
|
||||
return hooks;
|
||||
}
|
||||
async function callTaskHooks(task, hooks, sequence) {
|
||||
if (sequence === "stack")
|
||||
hooks = hooks.slice().reverse();
|
||||
if (sequence === "parallel") {
|
||||
await Promise.all(hooks.map((fn) => fn(task.result)));
|
||||
} else {
|
||||
for (const fn of hooks)
|
||||
await fn(task.result);
|
||||
}
|
||||
}
|
||||
async function callSuiteHook(suite, currentTask, name, runner, args) {
|
||||
const sequence = runner.config.sequence.hooks;
|
||||
const callbacks = [];
|
||||
if (name === "beforeEach" && suite.suite) {
|
||||
callbacks.push(
|
||||
...await callSuiteHook(suite.suite, currentTask, name, runner, args)
|
||||
);
|
||||
}
|
||||
updateSuiteHookState(currentTask, name, "run", runner);
|
||||
const hooks = getSuiteHooks(suite, name, sequence);
|
||||
if (sequence === "parallel") {
|
||||
callbacks.push(...await Promise.all(hooks.map((fn) => fn(...args))));
|
||||
} else {
|
||||
for (const hook of hooks)
|
||||
callbacks.push(await hook(...args));
|
||||
}
|
||||
updateSuiteHookState(currentTask, name, "pass", runner);
|
||||
if (name === "afterEach" && suite.suite) {
|
||||
callbacks.push(
|
||||
...await callSuiteHook(suite.suite, currentTask, name, runner, args)
|
||||
);
|
||||
}
|
||||
return callbacks;
|
||||
}
|
||||
const packs = /* @__PURE__ */ new Map();
|
||||
let updateTimer;
|
||||
let previousUpdate;
|
||||
function updateTask(task, runner) {
|
||||
packs.set(task.id, [task.result, task.meta]);
|
||||
const { clearTimeout, setTimeout } = getSafeTimers();
|
||||
clearTimeout(updateTimer);
|
||||
updateTimer = setTimeout(() => {
|
||||
previousUpdate = sendTasksUpdate(runner);
|
||||
}, 10);
|
||||
}
|
||||
async function sendTasksUpdate(runner) {
|
||||
var _a;
|
||||
const { clearTimeout } = getSafeTimers();
|
||||
clearTimeout(updateTimer);
|
||||
await previousUpdate;
|
||||
if (packs.size) {
|
||||
const taskPacks = Array.from(packs).map(([id, task]) => {
|
||||
return [
|
||||
id,
|
||||
task[0],
|
||||
task[1]
|
||||
];
|
||||
});
|
||||
const p = (_a = runner.onTaskUpdate) == null ? void 0 : _a.call(runner, taskPacks);
|
||||
packs.clear();
|
||||
return p;
|
||||
}
|
||||
}
|
||||
async function callCleanupHooks(cleanups) {
|
||||
await Promise.all(cleanups.map(async (fn) => {
|
||||
if (typeof fn !== "function")
|
||||
return;
|
||||
await fn();
|
||||
}));
|
||||
}
|
||||
async function runTest(test, runner) {
|
||||
var _a, _b, _c, _d, _e, _f;
|
||||
await ((_a = runner.onBeforeRunTask) == null ? void 0 : _a.call(runner, test));
|
||||
if (test.mode !== "run")
|
||||
return;
|
||||
if (((_b = test.result) == null ? void 0 : _b.state) === "fail") {
|
||||
updateTask(test, runner);
|
||||
return;
|
||||
}
|
||||
const start = now();
|
||||
test.result = {
|
||||
state: "run",
|
||||
startTime: start,
|
||||
retryCount: 0
|
||||
};
|
||||
updateTask(test, runner);
|
||||
setCurrentTest(test);
|
||||
const repeats = test.repeats ?? 0;
|
||||
for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) {
|
||||
const retry = test.retry ?? 0;
|
||||
for (let retryCount = 0; retryCount <= retry; retryCount++) {
|
||||
let beforeEachCleanups = [];
|
||||
try {
|
||||
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, { retry: retryCount, repeats: repeatCount }));
|
||||
test.result.repeatCount = repeatCount;
|
||||
beforeEachCleanups = await callSuiteHook(test.suite, test, "beforeEach", runner, [test.context, test.suite]);
|
||||
if (runner.runTask) {
|
||||
await runner.runTask(test);
|
||||
} else {
|
||||
const fn = getFn(test);
|
||||
if (!fn)
|
||||
throw new Error("Test function is not found. Did you add it using `setFn`?");
|
||||
await fn();
|
||||
}
|
||||
if (test.promises) {
|
||||
const result = await Promise.allSettled(test.promises);
|
||||
const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
|
||||
if (errors.length)
|
||||
throw errors;
|
||||
}
|
||||
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, { retry: retryCount, repeats: repeatCount }));
|
||||
if (test.result.state !== "fail") {
|
||||
if (!test.repeats)
|
||||
test.result.state = "pass";
|
||||
else if (test.repeats && retry === retryCount)
|
||||
test.result.state = "pass";
|
||||
}
|
||||
} catch (e) {
|
||||
failTask(test.result, e, runner.config.diffOptions);
|
||||
}
|
||||
if (test.pending || ((_e = test.result) == null ? void 0 : _e.state) === "skip") {
|
||||
test.mode = "skip";
|
||||
test.result = { state: "skip" };
|
||||
updateTask(test, runner);
|
||||
setCurrentTest(void 0);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await callSuiteHook(test.suite, test, "afterEach", runner, [test.context, test.suite]);
|
||||
await callCleanupHooks(beforeEachCleanups);
|
||||
await callFixtureCleanup(test.context);
|
||||
} catch (e) {
|
||||
failTask(test.result, e, runner.config.diffOptions);
|
||||
}
|
||||
if (test.result.state === "pass")
|
||||
break;
|
||||
if (retryCount < retry) {
|
||||
test.result.state = "run";
|
||||
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
|
||||
}
|
||||
updateTask(test, runner);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await callTaskHooks(test, test.onFinished || [], "stack");
|
||||
} catch (e) {
|
||||
failTask(test.result, e, runner.config.diffOptions);
|
||||
}
|
||||
if (test.result.state === "fail") {
|
||||
try {
|
||||
await callTaskHooks(test, test.onFailed || [], runner.config.sequence.hooks);
|
||||
} catch (e) {
|
||||
failTask(test.result, e, runner.config.diffOptions);
|
||||
}
|
||||
}
|
||||
if (test.fails) {
|
||||
if (test.result.state === "pass") {
|
||||
const error = processError(new Error("Expect test to fail"));
|
||||
test.result.state = "fail";
|
||||
test.result.errors = [error];
|
||||
} else {
|
||||
test.result.state = "pass";
|
||||
test.result.errors = void 0;
|
||||
}
|
||||
}
|
||||
setCurrentTest(void 0);
|
||||
test.result.duration = now() - start;
|
||||
await ((_f = runner.onAfterRunTask) == null ? void 0 : _f.call(runner, test));
|
||||
updateTask(test, runner);
|
||||
}
|
||||
function failTask(result, err, diffOptions) {
|
||||
if (err instanceof PendingError) {
|
||||
result.state = "skip";
|
||||
return;
|
||||
}
|
||||
result.state = "fail";
|
||||
const errors = Array.isArray(err) ? err : [err];
|
||||
for (const e of errors) {
|
||||
const error = processError(e, diffOptions);
|
||||
result.errors ?? (result.errors = []);
|
||||
result.errors.push(error);
|
||||
}
|
||||
}
|
||||
function markTasksAsSkipped(suite, runner) {
|
||||
suite.tasks.forEach((t) => {
|
||||
t.mode = "skip";
|
||||
t.result = { ...t.result, state: "skip" };
|
||||
updateTask(t, runner);
|
||||
if (t.type === "suite")
|
||||
markTasksAsSkipped(t, runner);
|
||||
});
|
||||
}
|
||||
async function runSuite(suite, runner) {
|
||||
var _a, _b, _c, _d;
|
||||
await ((_a = runner.onBeforeRunSuite) == null ? void 0 : _a.call(runner, suite));
|
||||
if (((_b = suite.result) == null ? void 0 : _b.state) === "fail") {
|
||||
markTasksAsSkipped(suite, runner);
|
||||
updateTask(suite, runner);
|
||||
return;
|
||||
}
|
||||
const start = now();
|
||||
suite.result = {
|
||||
state: "run",
|
||||
startTime: start
|
||||
};
|
||||
updateTask(suite, runner);
|
||||
let beforeAllCleanups = [];
|
||||
if (suite.mode === "skip") {
|
||||
suite.result.state = "skip";
|
||||
} else if (suite.mode === "todo") {
|
||||
suite.result.state = "todo";
|
||||
} else {
|
||||
try {
|
||||
beforeAllCleanups = await callSuiteHook(suite, suite, "beforeAll", runner, [suite]);
|
||||
if (runner.runSuite) {
|
||||
await runner.runSuite(suite);
|
||||
} else {
|
||||
for (let tasksGroup of partitionSuiteChildren(suite)) {
|
||||
if (tasksGroup[0].concurrent === true) {
|
||||
const mutex = limit(runner.config.maxConcurrency);
|
||||
await Promise.all(tasksGroup.map((c) => mutex(() => runSuiteChild(c, runner))));
|
||||
} else {
|
||||
const { sequence } = runner.config;
|
||||
if (sequence.shuffle || suite.shuffle) {
|
||||
const suites = tasksGroup.filter((group) => group.type === "suite");
|
||||
const tests = tasksGroup.filter((group) => group.type === "test");
|
||||
const groups = shuffle([suites, tests], sequence.seed);
|
||||
tasksGroup = groups.flatMap((group) => shuffle(group, sequence.seed));
|
||||
}
|
||||
for (const c of tasksGroup)
|
||||
await runSuiteChild(c, runner);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
failTask(suite.result, e, runner.config.diffOptions);
|
||||
}
|
||||
try {
|
||||
await callSuiteHook(suite, suite, "afterAll", runner, [suite]);
|
||||
await callCleanupHooks(beforeAllCleanups);
|
||||
} catch (e) {
|
||||
failTask(suite.result, e, runner.config.diffOptions);
|
||||
}
|
||||
if (suite.mode === "run") {
|
||||
if (!runner.config.passWithNoTests && !hasTests(suite)) {
|
||||
suite.result.state = "fail";
|
||||
if (!((_c = suite.result.errors) == null ? void 0 : _c.length)) {
|
||||
const error = processError(new Error(`No test found in suite ${suite.name}`));
|
||||
suite.result.errors = [error];
|
||||
}
|
||||
} else if (hasFailed(suite)) {
|
||||
suite.result.state = "fail";
|
||||
} else {
|
||||
suite.result.state = "pass";
|
||||
}
|
||||
}
|
||||
updateTask(suite, runner);
|
||||
suite.result.duration = now() - start;
|
||||
await ((_d = runner.onAfterRunSuite) == null ? void 0 : _d.call(runner, suite));
|
||||
}
|
||||
}
|
||||
async function runSuiteChild(c, runner) {
|
||||
if (c.type === "test" || c.type === "custom")
|
||||
return runTest(c, runner);
|
||||
else if (c.type === "suite")
|
||||
return runSuite(c, runner);
|
||||
}
|
||||
async function runFiles(files, runner) {
|
||||
var _a, _b;
|
||||
for (const file of files) {
|
||||
if (!file.tasks.length && !runner.config.passWithNoTests) {
|
||||
if (!((_b = (_a = file.result) == null ? void 0 : _a.errors) == null ? void 0 : _b.length)) {
|
||||
const error = processError(new Error(`No test suite found in file ${file.filepath}`));
|
||||
file.result = {
|
||||
state: "fail",
|
||||
errors: [error]
|
||||
};
|
||||
}
|
||||
}
|
||||
await runSuite(file, runner);
|
||||
}
|
||||
}
|
||||
async function startTests(paths, runner) {
|
||||
var _a, _b, _c, _d;
|
||||
await ((_a = runner.onBeforeCollect) == null ? void 0 : _a.call(runner, paths));
|
||||
const files = await collectTests(paths, runner);
|
||||
await ((_b = runner.onCollected) == null ? void 0 : _b.call(runner, files));
|
||||
await ((_c = runner.onBeforeRunFiles) == null ? void 0 : _c.call(runner, files));
|
||||
await runFiles(files, runner);
|
||||
await ((_d = runner.onAfterRunFiles) == null ? void 0 : _d.call(runner, files));
|
||||
await sendTasksUpdate(runner);
|
||||
return files;
|
||||
}
|
||||
|
||||
function getDefaultHookTimeout() {
|
||||
return getRunner().config.hookTimeout;
|
||||
}
|
||||
function beforeAll(fn, timeout) {
|
||||
return getCurrentSuite().on("beforeAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true));
|
||||
}
|
||||
function afterAll(fn, timeout) {
|
||||
return getCurrentSuite().on("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true));
|
||||
}
|
||||
function beforeEach(fn, timeout) {
|
||||
return getCurrentSuite().on("beforeEach", withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true));
|
||||
}
|
||||
function afterEach(fn, timeout) {
|
||||
return getCurrentSuite().on("afterEach", withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true));
|
||||
}
|
||||
const onTestFailed = createTestHook("onTestFailed", (test, handler) => {
|
||||
test.onFailed || (test.onFailed = []);
|
||||
test.onFailed.push(handler);
|
||||
});
|
||||
const onTestFinished = createTestHook("onTestFinished", (test, handler) => {
|
||||
test.onFinished || (test.onFinished = []);
|
||||
test.onFinished.push(handler);
|
||||
});
|
||||
function createTestHook(name, handler) {
|
||||
return (fn) => {
|
||||
const current = getCurrentTest();
|
||||
if (!current)
|
||||
throw new Error(`Hook ${name}() can only be called inside a test`);
|
||||
return handler(current, fn);
|
||||
};
|
||||
}
|
||||
|
||||
export { afterAll, afterEach, beforeAll, beforeEach, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
import { ErrorWithDiff, Awaitable } from '@vitest/utils';
|
||||
|
||||
type ChainableFunction<T extends string, F extends (...args: any) => any, C = {}> = F & {
|
||||
[x in T]: ChainableFunction<T, F, C>;
|
||||
} & {
|
||||
fn: (this: Record<T, any>, ...args: Parameters<F>) => ReturnType<F>;
|
||||
} & C;
|
||||
declare function createChainable<T extends string, Args extends any[], R = any>(keys: T[], fn: (this: Record<T, any>, ...args: Args) => R): ChainableFunction<T, (...args: Args) => R>;
|
||||
|
||||
interface FixtureItem extends FixtureOptions {
|
||||
prop: string;
|
||||
value: any;
|
||||
/**
|
||||
* Indicates whether the fixture is a function
|
||||
*/
|
||||
isFn: boolean;
|
||||
/**
|
||||
* The dependencies(fixtures) of current fixture function.
|
||||
*/
|
||||
deps?: FixtureItem[];
|
||||
}
|
||||
|
||||
type RunMode = 'run' | 'skip' | 'only' | 'todo';
|
||||
type TaskState = RunMode | 'pass' | 'fail';
|
||||
interface TaskBase {
|
||||
id: string;
|
||||
name: string;
|
||||
mode: RunMode;
|
||||
meta: TaskMeta;
|
||||
each?: boolean;
|
||||
concurrent?: boolean;
|
||||
shuffle?: boolean;
|
||||
suite?: Suite;
|
||||
file?: File;
|
||||
result?: TaskResult;
|
||||
retry?: number;
|
||||
repeats?: number;
|
||||
location?: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
}
|
||||
interface TaskPopulated extends TaskBase {
|
||||
suite: Suite;
|
||||
pending?: boolean;
|
||||
result?: TaskResult;
|
||||
fails?: boolean;
|
||||
onFailed?: OnTestFailedHandler[];
|
||||
onFinished?: OnTestFinishedHandler[];
|
||||
/**
|
||||
* Store promises (from async expects) to wait for them before finishing the test
|
||||
*/
|
||||
promises?: Promise<any>[];
|
||||
}
|
||||
interface TaskMeta {
|
||||
}
|
||||
interface TaskResult {
|
||||
state: TaskState;
|
||||
duration?: number;
|
||||
startTime?: number;
|
||||
heap?: number;
|
||||
errors?: ErrorWithDiff[];
|
||||
htmlError?: string;
|
||||
hooks?: Partial<Record<keyof SuiteHooks, TaskState>>;
|
||||
retryCount?: number;
|
||||
repeatCount?: number;
|
||||
}
|
||||
type TaskResultPack = [id: string, result: TaskResult | undefined, meta: TaskMeta];
|
||||
interface Suite extends TaskBase {
|
||||
type: 'suite';
|
||||
tasks: Task[];
|
||||
filepath?: string;
|
||||
projectName: string;
|
||||
}
|
||||
interface File extends Suite {
|
||||
filepath: string;
|
||||
collectDuration?: number;
|
||||
setupDuration?: number;
|
||||
}
|
||||
interface Test<ExtraContext = {}> extends TaskPopulated {
|
||||
type: 'test';
|
||||
context: TaskContext<Test> & ExtraContext & TestContext;
|
||||
}
|
||||
interface Custom<ExtraContext = {}> extends TaskPopulated {
|
||||
type: 'custom';
|
||||
context: TaskContext<Custom> & ExtraContext & TestContext;
|
||||
}
|
||||
type Task = Test | Suite | Custom | File;
|
||||
type DoneCallback = (error?: any) => void;
|
||||
type TestFunction<ExtraContext = {}> = (context: ExtendedContext<Test> & ExtraContext) => Awaitable<any> | void;
|
||||
type ExtractEachCallbackArgs<T extends ReadonlyArray<any>> = {
|
||||
1: [T[0]];
|
||||
2: [T[0], T[1]];
|
||||
3: [T[0], T[1], T[2]];
|
||||
4: [T[0], T[1], T[2], T[3]];
|
||||
5: [T[0], T[1], T[2], T[3], T[4]];
|
||||
6: [T[0], T[1], T[2], T[3], T[4], T[5]];
|
||||
7: [T[0], T[1], T[2], T[3], T[4], T[5], T[6]];
|
||||
8: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7]];
|
||||
9: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]];
|
||||
10: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]];
|
||||
fallback: Array<T extends ReadonlyArray<infer U> ? U : any>;
|
||||
}[T extends Readonly<[any]> ? 1 : T extends Readonly<[any, any]> ? 2 : T extends Readonly<[any, any, any]> ? 3 : T extends Readonly<[any, any, any, any]> ? 4 : T extends Readonly<[any, any, any, any, any]> ? 5 : T extends Readonly<[any, any, any, any, any, any]> ? 6 : T extends Readonly<[any, any, any, any, any, any, any]> ? 7 : T extends Readonly<[any, any, any, any, any, any, any, any]> ? 8 : T extends Readonly<[any, any, any, any, any, any, any, any, any]> ? 9 : T extends Readonly<[any, any, any, any, any, any, any, any, any, any]> ? 10 : 'fallback'];
|
||||
interface EachFunctionReturn<T extends any[]> {
|
||||
/**
|
||||
* @deprecated Use options as the second argument instead
|
||||
*/
|
||||
(name: string | Function, fn: (...args: T) => Awaitable<void>, options: TestOptions): void;
|
||||
(name: string | Function, fn: (...args: T) => Awaitable<void>, options?: number | TestOptions): void;
|
||||
(name: string | Function, options: TestOptions, fn: (...args: T) => Awaitable<void>): void;
|
||||
}
|
||||
interface TestEachFunction {
|
||||
<T extends any[] | [any]>(cases: ReadonlyArray<T>): EachFunctionReturn<T>;
|
||||
<T extends ReadonlyArray<any>>(cases: ReadonlyArray<T>): EachFunctionReturn<ExtractEachCallbackArgs<T>>;
|
||||
<T>(cases: ReadonlyArray<T>): EachFunctionReturn<T[]>;
|
||||
(...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>;
|
||||
}
|
||||
interface TestCollectorCallable<C = {}> {
|
||||
/**
|
||||
* @deprecated Use options as the second argument instead
|
||||
*/
|
||||
<ExtraContext extends C>(name: string | Function, fn: TestFunction<ExtraContext>, options: TestOptions): void;
|
||||
<ExtraContext extends C>(name: string | Function, fn?: TestFunction<ExtraContext>, options?: number | TestOptions): void;
|
||||
<ExtraContext extends C>(name: string | Function, options?: TestOptions, fn?: TestFunction<ExtraContext>): void;
|
||||
}
|
||||
type ChainableTestAPI<ExtraContext = {}> = ChainableFunction<'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'fails', TestCollectorCallable<ExtraContext>, {
|
||||
each: TestEachFunction;
|
||||
}>;
|
||||
interface TestOptions {
|
||||
/**
|
||||
* Test timeout.
|
||||
*/
|
||||
timeout?: number;
|
||||
/**
|
||||
* Times to retry the test if fails. Useful for making flaky tests more stable.
|
||||
* When retries is up, the last test error will be thrown.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
retry?: number;
|
||||
/**
|
||||
* How many times the test will run.
|
||||
* Only inner tests will repeat if set on `describe()`, nested `describe()` will inherit parent's repeat by default.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
repeats?: number;
|
||||
/**
|
||||
* Whether tests run concurrently.
|
||||
* Tests inherit `concurrent` from `describe()` and nested `describe()` will inherit from parent's `concurrent`.
|
||||
*/
|
||||
concurrent?: boolean;
|
||||
/**
|
||||
* Whether tests run sequentially.
|
||||
* Tests inherit `sequential` from `describe()` and nested `describe()` will inherit from parent's `sequential`.
|
||||
*/
|
||||
sequential?: boolean;
|
||||
/**
|
||||
* Whether the test should be skipped.
|
||||
*/
|
||||
skip?: boolean;
|
||||
/**
|
||||
* Should this test be the only one running in a suite.
|
||||
*/
|
||||
only?: boolean;
|
||||
/**
|
||||
* Whether the test should be skipped and marked as a todo.
|
||||
*/
|
||||
todo?: boolean;
|
||||
/**
|
||||
* Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail.
|
||||
*/
|
||||
fails?: boolean;
|
||||
}
|
||||
interface ExtendedAPI<ExtraContext> {
|
||||
skipIf: (condition: any) => ChainableTestAPI<ExtraContext>;
|
||||
runIf: (condition: any) => ChainableTestAPI<ExtraContext>;
|
||||
}
|
||||
type CustomAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & ExtendedAPI<ExtraContext> & {
|
||||
extend: <T extends Record<string, any> = {}>(fixtures: Fixtures<T, ExtraContext>) => CustomAPI<{
|
||||
[K in keyof T | keyof ExtraContext]: K extends keyof T ? T[K] : K extends keyof ExtraContext ? ExtraContext[K] : never;
|
||||
}>;
|
||||
};
|
||||
type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & ExtendedAPI<ExtraContext> & {
|
||||
extend: <T extends Record<string, any> = {}>(fixtures: Fixtures<T, ExtraContext>) => TestAPI<{
|
||||
[K in keyof T | keyof ExtraContext]: K extends keyof T ? T[K] : K extends keyof ExtraContext ? ExtraContext[K] : never;
|
||||
}>;
|
||||
};
|
||||
interface FixtureOptions {
|
||||
/**
|
||||
* Whether to automatically set up current fixture, even though it's not being used in tests.
|
||||
*/
|
||||
auto?: boolean;
|
||||
}
|
||||
type Use<T> = (value: T) => Promise<void>;
|
||||
type FixtureFn<T, K extends keyof T, ExtraContext> = (context: Omit<T, K> & ExtraContext, use: Use<T[K]>) => Promise<void>;
|
||||
type Fixture<T, K extends keyof T, ExtraContext = {}> = ((...args: any) => any) extends T[K] ? (T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never) : T[K] | (T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never);
|
||||
type Fixtures<T extends Record<string, any>, ExtraContext = {}> = {
|
||||
[K in keyof T]: Fixture<T, K, ExtraContext & ExtendedContext<Test>> | [Fixture<T, K, ExtraContext & ExtendedContext<Test>>, FixtureOptions?];
|
||||
};
|
||||
type InferFixturesTypes<T> = T extends TestAPI<infer C> ? C : T;
|
||||
interface SuiteCollectorCallable<ExtraContext = {}> {
|
||||
/**
|
||||
* @deprecated Use options as the second argument instead
|
||||
*/
|
||||
<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn: SuiteFactory<OverrideExtraContext>, options: TestOptions): SuiteCollector<OverrideExtraContext>;
|
||||
<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn?: SuiteFactory<OverrideExtraContext>, options?: number | TestOptions): SuiteCollector<OverrideExtraContext>;
|
||||
<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, options: TestOptions, fn?: SuiteFactory<OverrideExtraContext>): SuiteCollector<OverrideExtraContext>;
|
||||
}
|
||||
type ChainableSuiteAPI<ExtraContext = {}> = ChainableFunction<'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'shuffle', SuiteCollectorCallable<ExtraContext>, {
|
||||
each: TestEachFunction;
|
||||
}>;
|
||||
type SuiteAPI<ExtraContext = {}> = ChainableSuiteAPI<ExtraContext> & {
|
||||
skipIf: (condition: any) => ChainableSuiteAPI<ExtraContext>;
|
||||
runIf: (condition: any) => ChainableSuiteAPI<ExtraContext>;
|
||||
};
|
||||
type HookListener<T extends any[], Return = void> = (...args: T) => Awaitable<Return>;
|
||||
type HookCleanupCallback = (() => Awaitable<unknown>) | void;
|
||||
interface SuiteHooks<ExtraContext = {}> {
|
||||
beforeAll: HookListener<[Readonly<Suite | File>], HookCleanupCallback>[];
|
||||
afterAll: HookListener<[Readonly<Suite | File>]>[];
|
||||
beforeEach: HookListener<[ExtendedContext<Test | Custom> & ExtraContext, Readonly<Suite>], HookCleanupCallback>[];
|
||||
afterEach: HookListener<[ExtendedContext<Test | Custom> & ExtraContext, Readonly<Suite>]>[];
|
||||
}
|
||||
interface TaskCustomOptions extends TestOptions {
|
||||
concurrent?: boolean;
|
||||
sequential?: boolean;
|
||||
skip?: boolean;
|
||||
only?: boolean;
|
||||
todo?: boolean;
|
||||
fails?: boolean;
|
||||
each?: boolean;
|
||||
meta?: Record<string, unknown>;
|
||||
fixtures?: FixtureItem[];
|
||||
handler?: (context: TaskContext<Custom>) => Awaitable<void>;
|
||||
}
|
||||
interface SuiteCollector<ExtraContext = {}> {
|
||||
readonly name: string;
|
||||
readonly mode: RunMode;
|
||||
options?: TestOptions;
|
||||
type: 'collector';
|
||||
test: TestAPI<ExtraContext>;
|
||||
tasks: (Suite | Custom<ExtraContext> | Test<ExtraContext> | SuiteCollector<ExtraContext>)[];
|
||||
task: (name: string, options?: TaskCustomOptions) => Custom<ExtraContext>;
|
||||
collect: (file?: File) => Promise<Suite>;
|
||||
clear: () => void;
|
||||
on: <T extends keyof SuiteHooks<ExtraContext>>(name: T, ...fn: SuiteHooks<ExtraContext>[T]) => void;
|
||||
}
|
||||
type SuiteFactory<ExtraContext = {}> = (test: TestAPI<ExtraContext>) => Awaitable<void>;
|
||||
interface RuntimeContext {
|
||||
tasks: (SuiteCollector | Test)[];
|
||||
currentSuite: SuiteCollector | null;
|
||||
}
|
||||
interface TestContext {
|
||||
}
|
||||
interface TaskContext<Task extends Custom | Test = Custom | Test> {
|
||||
/**
|
||||
* Metadata of the current test
|
||||
*/
|
||||
task: Readonly<Task>;
|
||||
/**
|
||||
* Extract hooks on test failed
|
||||
*/
|
||||
onTestFailed: (fn: OnTestFailedHandler) => void;
|
||||
/**
|
||||
* Extract hooks on test failed
|
||||
*/
|
||||
onTestFinished: (fn: OnTestFinishedHandler) => void;
|
||||
/**
|
||||
* Mark tests as skipped. All execution after this call will be skipped.
|
||||
*/
|
||||
skip: () => void;
|
||||
}
|
||||
type ExtendedContext<T extends Custom | Test> = TaskContext<T> & TestContext;
|
||||
type OnTestFailedHandler = (result: TaskResult) => Awaitable<void>;
|
||||
type OnTestFinishedHandler = (result: TaskResult) => Awaitable<void>;
|
||||
type SequenceHooks = 'stack' | 'list' | 'parallel';
|
||||
type SequenceSetupFiles = 'list' | 'parallel';
|
||||
|
||||
export { type TaskContext as A, type SequenceHooks as B, type Custom as C, type DoneCallback as D, type ExtendedContext as E, type File as F, type SequenceSetupFiles as G, type HookListener as H, type InferFixturesTypes as I, type OnTestFailedHandler as O, type RunMode as R, type Suite as S, type Task as T, type Use as U, type Test as a, type ChainableFunction as b, createChainable as c, type SuiteAPI as d, type TestAPI as e, type SuiteCollector as f, type CustomAPI as g, type SuiteHooks as h, type OnTestFinishedHandler as i, type TaskState as j, type TaskBase as k, type TaskPopulated as l, type TaskMeta as m, type TaskResult as n, type TaskResultPack as o, type TestFunction as p, type TestOptions as q, type FixtureOptions as r, type FixtureFn as s, type Fixture as t, type Fixtures as u, type HookCleanupCallback as v, type TaskCustomOptions as w, type SuiteFactory as x, type RuntimeContext as y, type TestContext as z };
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
import { B as SequenceHooks, G as SequenceSetupFiles, F as File, T as Task, S as Suite, o as TaskResultPack, a as Test, C as Custom, A as TaskContext, E as ExtendedContext } from './tasks-K5XERDtv.js';
|
||||
export { g as CustomAPI, D as DoneCallback, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, O as OnTestFailedHandler, i as OnTestFinishedHandler, R as RunMode, y as RuntimeContext, d as SuiteAPI, f as SuiteCollector, x as SuiteFactory, h as SuiteHooks, k as TaskBase, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, j as TaskState, e as TestAPI, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-K5XERDtv.js';
|
||||
import { DiffOptions } from '@vitest/utils/diff';
|
||||
import '@vitest/utils';
|
||||
|
||||
interface VitestRunnerConfig {
|
||||
root: string;
|
||||
setupFiles: string[] | string;
|
||||
name: string;
|
||||
passWithNoTests: boolean;
|
||||
testNamePattern?: RegExp;
|
||||
allowOnly?: boolean;
|
||||
sequence: {
|
||||
shuffle?: boolean;
|
||||
concurrent?: boolean;
|
||||
seed: number;
|
||||
hooks: SequenceHooks;
|
||||
setupFiles: SequenceSetupFiles;
|
||||
};
|
||||
chaiConfig?: {
|
||||
truncateThreshold?: number;
|
||||
};
|
||||
maxConcurrency: number;
|
||||
testTimeout: number;
|
||||
hookTimeout: number;
|
||||
retry: number;
|
||||
includeTaskLocation?: boolean;
|
||||
diffOptions?: DiffOptions;
|
||||
}
|
||||
type VitestRunnerImportSource = 'collect' | 'setup';
|
||||
interface VitestRunnerConstructor {
|
||||
new (config: VitestRunnerConfig): VitestRunner;
|
||||
}
|
||||
type CancelReason = 'keyboard-input' | 'test-failure' | string & Record<string, never>;
|
||||
interface VitestRunner {
|
||||
/**
|
||||
* First thing that's getting called before actually collecting and running tests.
|
||||
*/
|
||||
onBeforeCollect?: (paths: string[]) => unknown;
|
||||
/**
|
||||
* Called after collecting tests and before "onBeforeRun".
|
||||
*/
|
||||
onCollected?: (files: File[]) => unknown;
|
||||
/**
|
||||
* Called when test runner should cancel next test runs.
|
||||
* Runner should listen for this method and mark tests and suites as skipped in
|
||||
* "onBeforeRunSuite" and "onBeforeRunTask" when called.
|
||||
*/
|
||||
onCancel?: (reason: CancelReason) => unknown;
|
||||
/**
|
||||
* Called before running a single test. Doesn't have "result" yet.
|
||||
*/
|
||||
onBeforeRunTask?: (test: Task) => unknown;
|
||||
/**
|
||||
* Called before actually running the test function. Already has "result" with "state" and "startTime".
|
||||
*/
|
||||
onBeforeTryTask?: (test: Task, options: {
|
||||
retry: number;
|
||||
repeats: number;
|
||||
}) => unknown;
|
||||
/**
|
||||
* Called after result and state are set.
|
||||
*/
|
||||
onAfterRunTask?: (test: Task) => unknown;
|
||||
/**
|
||||
* Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws.
|
||||
*/
|
||||
onAfterTryTask?: (test: Task, options: {
|
||||
retry: number;
|
||||
repeats: number;
|
||||
}) => unknown;
|
||||
/**
|
||||
* Called before running a single suite. Doesn't have "result" yet.
|
||||
*/
|
||||
onBeforeRunSuite?: (suite: Suite) => unknown;
|
||||
/**
|
||||
* Called after running a single suite. Has state and result.
|
||||
*/
|
||||
onAfterRunSuite?: (suite: Suite) => unknown;
|
||||
/**
|
||||
* If defined, will be called instead of usual Vitest suite partition and handling.
|
||||
* "before" and "after" hooks will not be ignored.
|
||||
*/
|
||||
runSuite?: (suite: Suite) => Promise<void>;
|
||||
/**
|
||||
* If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function.
|
||||
* "before" and "after" hooks will not be ignored.
|
||||
*/
|
||||
runTask?: (test: Task) => Promise<void>;
|
||||
/**
|
||||
* Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests.
|
||||
*/
|
||||
onTaskUpdate?: (task: TaskResultPack[]) => Promise<void>;
|
||||
/**
|
||||
* Called before running all tests in collected paths.
|
||||
*/
|
||||
onBeforeRunFiles?: (files: File[]) => unknown;
|
||||
/**
|
||||
* Called right after running all tests in collected paths.
|
||||
*/
|
||||
onAfterRunFiles?: (files: File[]) => unknown;
|
||||
/**
|
||||
* Called when new context for a test is defined. Useful, if you want to add custom properties to the context.
|
||||
* If you only want to define custom context, consider using "beforeAll" in "setupFiles" instead.
|
||||
*
|
||||
* This method is called for both "test" and "custom" handlers.
|
||||
*
|
||||
* @see https://vitest.dev/advanced/runner.html#your-task-function
|
||||
*/
|
||||
extendTaskContext?: <T extends Test | Custom>(context: TaskContext<T>) => ExtendedContext<T>;
|
||||
/**
|
||||
* Called, when files are imported. Can be called in two situations: when collecting tests and when importing setup files.
|
||||
*/
|
||||
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
|
||||
/**
|
||||
* Publicly available configuration.
|
||||
*/
|
||||
config: VitestRunnerConfig;
|
||||
}
|
||||
|
||||
export { type CancelReason, Custom, ExtendedContext, File, SequenceHooks, SequenceSetupFiles, Suite, Task, TaskContext, TaskResultPack, Test, type VitestRunner, type VitestRunnerConfig, type VitestRunnerConstructor, type VitestRunnerImportSource };
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { S as Suite, T as Task, a as Test, C as Custom } from './tasks-K5XERDtv.js';
|
||||
export { b as ChainableFunction, c as createChainable } from './tasks-K5XERDtv.js';
|
||||
import { Arrayable } from '@vitest/utils';
|
||||
|
||||
/**
|
||||
* If any tasks been marked as `only`, mark all other tasks as `skip`.
|
||||
*/
|
||||
declare function interpretTaskModes(suite: Suite, namePattern?: string | RegExp, onlyMode?: boolean, parentIsOnly?: boolean, allowOnly?: boolean): void;
|
||||
declare function someTasksAreOnly(suite: Suite): boolean;
|
||||
declare function generateHash(str: string): string;
|
||||
declare function calculateSuiteHash(parent: Suite): void;
|
||||
|
||||
/**
|
||||
* Partition in tasks groups by consecutive concurrent
|
||||
*/
|
||||
declare function partitionSuiteChildren(suite: Suite): Task[][];
|
||||
|
||||
declare function getTests(suite: Arrayable<Task>): (Test | Custom)[];
|
||||
declare function getTasks(tasks?: Arrayable<Task>): Task[];
|
||||
declare function getSuites(suite: Arrayable<Task>): Suite[];
|
||||
declare function hasTests(suite: Arrayable<Suite>): boolean;
|
||||
declare function hasFailed(suite: Arrayable<Task>): boolean;
|
||||
declare function getNames(task: Task): string[];
|
||||
|
||||
export { calculateSuiteHash, generateHash, getNames, getSuites, getTasks, getTests, hasFailed, hasTests, interpretTaskModes, partitionSuiteChildren, someTasksAreOnly };
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export { c as calculateSuiteHash, j as createChainable, g as generateHash, f as getNames, d as getSuites, b as getTasks, a as getTests, e as hasFailed, h as hasTests, i as interpretTaskModes, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
|
||||
import '@vitest/utils/error';
|
||||
import '@vitest/utils';
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
export const AsyncResource = {
|
||||
bind(fn, _type, thisArg) {
|
||||
return fn.bind(thisArg);
|
||||
},
|
||||
};
|
||||
|
||||
export class AsyncLocalStorage {
|
||||
getStore() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
run(_store, callback) {
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
export type LimitFunction = {
|
||||
/**
|
||||
The number of promises that are currently running.
|
||||
*/
|
||||
readonly activeCount: number;
|
||||
|
||||
/**
|
||||
The number of promises that are waiting to run (i.e. their internal `fn` was not called yet).
|
||||
*/
|
||||
readonly pendingCount: number;
|
||||
|
||||
/**
|
||||
Discard pending promises that are waiting to run.
|
||||
|
||||
This might be useful if you want to teardown the queue at the end of your program's lifecycle or discard any function calls referencing an intermediary state of your app.
|
||||
|
||||
Note: This does not cancel promises that are already running.
|
||||
*/
|
||||
clearQueue: () => void;
|
||||
|
||||
/**
|
||||
@param fn - Promise-returning/async function.
|
||||
@param arguments - Any arguments to pass through to `fn`. Support for passing arguments on to the `fn` is provided in order to be able to avoid creating unnecessary closures. You probably don't need this optimization unless you're pushing a lot of functions.
|
||||
@returns The promise returned by calling `fn(...arguments)`.
|
||||
*/
|
||||
<Arguments extends unknown[], ReturnType>(
|
||||
fn: (...arguments_: Arguments) => PromiseLike<ReturnType> | ReturnType,
|
||||
...arguments_: Arguments
|
||||
): Promise<ReturnType>;
|
||||
};
|
||||
|
||||
/**
|
||||
Run multiple promise-returning & async functions with limited concurrency.
|
||||
|
||||
@param concurrency - Concurrency limit. Minimum: `1`.
|
||||
@returns A `limit` function.
|
||||
*/
|
||||
export default function pLimit(concurrency: number): LimitFunction;
|
||||
Generated
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
import Queue from 'yocto-queue';
|
||||
import {AsyncResource} from '#async_hooks';
|
||||
|
||||
export default function pLimit(concurrency) {
|
||||
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
|
||||
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
|
||||
}
|
||||
|
||||
const queue = new Queue();
|
||||
let activeCount = 0;
|
||||
|
||||
const next = () => {
|
||||
activeCount--;
|
||||
|
||||
if (queue.size > 0) {
|
||||
queue.dequeue()();
|
||||
}
|
||||
};
|
||||
|
||||
const run = async (function_, resolve, arguments_) => {
|
||||
activeCount++;
|
||||
|
||||
const result = (async () => function_(...arguments_))();
|
||||
|
||||
resolve(result);
|
||||
|
||||
try {
|
||||
await result;
|
||||
} catch {}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
const enqueue = (function_, resolve, arguments_) => {
|
||||
queue.enqueue(
|
||||
AsyncResource.bind(run.bind(undefined, function_, resolve, arguments_)),
|
||||
);
|
||||
|
||||
(async () => {
|
||||
// This function needs to wait until the next microtask before comparing
|
||||
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
|
||||
// when the run function is dequeued and called. The comparison in the if-statement
|
||||
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
|
||||
await Promise.resolve();
|
||||
|
||||
if (activeCount < concurrency && queue.size > 0) {
|
||||
queue.dequeue()();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
const generator = (function_, ...arguments_) => new Promise(resolve => {
|
||||
enqueue(function_, resolve, arguments_);
|
||||
});
|
||||
|
||||
Object.defineProperties(generator, {
|
||||
activeCount: {
|
||||
get: () => activeCount,
|
||||
},
|
||||
pendingCount: {
|
||||
get: () => queue.size,
|
||||
},
|
||||
clearQueue: {
|
||||
value() {
|
||||
queue.clear();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return generator;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Generated
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "p-limit",
|
||||
"version": "5.0.0",
|
||||
"description": "Run multiple promise-returning & async functions with limited concurrency",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/p-limit",
|
||||
"funding": "https://github.com/sponsors/sindresorhus",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"imports": {
|
||||
"#async_hooks": {
|
||||
"node": "async_hooks",
|
||||
"default": "./async-hooks-stub.js"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"async-hooks-stub.js"
|
||||
],
|
||||
"keywords": [
|
||||
"promise",
|
||||
"limit",
|
||||
"limited",
|
||||
"concurrency",
|
||||
"throttle",
|
||||
"throat",
|
||||
"rate",
|
||||
"batch",
|
||||
"ratelimit",
|
||||
"task",
|
||||
"queue",
|
||||
"async",
|
||||
"await",
|
||||
"promises",
|
||||
"bluebird"
|
||||
],
|
||||
"dependencies": {
|
||||
"yocto-queue": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^5.3.1",
|
||||
"delay": "^6.0.0",
|
||||
"in-range": "^3.0.0",
|
||||
"random-int": "^3.0.0",
|
||||
"time-span": "^5.1.0",
|
||||
"tsd": "^0.29.0",
|
||||
"xo": "^0.56.0"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+88
@@ -0,0 +1,88 @@
|
||||
# p-limit
|
||||
|
||||
> Run multiple promise-returning & async functions with limited concurrency
|
||||
|
||||
*Works in Node.js and browsers.*
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install p-limit
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import pLimit from 'p-limit';
|
||||
|
||||
const limit = pLimit(1);
|
||||
|
||||
const input = [
|
||||
limit(() => fetchSomething('foo')),
|
||||
limit(() => fetchSomething('bar')),
|
||||
limit(() => doSomething())
|
||||
];
|
||||
|
||||
// Only one promise is run at once
|
||||
const result = await Promise.all(input);
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### pLimit(concurrency)
|
||||
|
||||
Returns a `limit` function.
|
||||
|
||||
#### concurrency
|
||||
|
||||
Type: `number`\
|
||||
Minimum: `1`\
|
||||
Default: `Infinity`
|
||||
|
||||
Concurrency limit.
|
||||
|
||||
### limit(fn, ...args)
|
||||
|
||||
Returns the promise returned by calling `fn(...args)`.
|
||||
|
||||
#### fn
|
||||
|
||||
Type: `Function`
|
||||
|
||||
Promise-returning/async function.
|
||||
|
||||
#### args
|
||||
|
||||
Any arguments to pass through to `fn`.
|
||||
|
||||
Support for passing arguments on to the `fn` is provided in order to be able to avoid creating unnecessary closures. You probably don't need this optimization unless you're pushing a *lot* of functions.
|
||||
|
||||
### limit.activeCount
|
||||
|
||||
The number of promises that are currently running.
|
||||
|
||||
### limit.pendingCount
|
||||
|
||||
The number of promises that are waiting to run (i.e. their internal `fn` was not called yet).
|
||||
|
||||
### limit.clearQueue()
|
||||
|
||||
Discard pending promises that are waiting to run.
|
||||
|
||||
This might be useful if you want to teardown the queue at the end of your program's lifecycle or discard any function calls referencing an intermediary state of your app.
|
||||
|
||||
Note: This does not cancel promises that are already running.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How is this different from the [`p-queue`](https://github.com/sindresorhus/p-queue) package?
|
||||
|
||||
This package is only about limiting the number of concurrent executions, while `p-queue` is a fully featured queue implementation with lots of different options, introspection, and ability to pause the queue.
|
||||
|
||||
## Related
|
||||
|
||||
- [p-throttle](https://github.com/sindresorhus/p-throttle) - Throttle promise-returning & async functions
|
||||
- [p-debounce](https://github.com/sindresorhus/p-debounce) - Debounce promise-returning & async functions
|
||||
- [p-all](https://github.com/sindresorhus/p-all) - Run promise-returning & async functions concurrently with optional limited concurrency
|
||||
- [More…](https://github.com/sindresorhus/promise-fun)
|
||||
Generated
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
export default class Queue<ValueType> implements Iterable<ValueType> {
|
||||
/**
|
||||
The size of the queue.
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
Tiny queue data structure.
|
||||
|
||||
The instance is an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols), which means you can iterate over the queue front to back with a “for…of” loop, or use spreading to convert the queue to an array. Don't do this unless you really need to though, since it's slow.
|
||||
|
||||
@example
|
||||
```
|
||||
import Queue from 'yocto-queue';
|
||||
|
||||
const queue = new Queue();
|
||||
|
||||
queue.enqueue('🦄');
|
||||
queue.enqueue('🌈');
|
||||
|
||||
console.log(queue.size);
|
||||
//=> 2
|
||||
|
||||
console.log(...queue);
|
||||
//=> '🦄 🌈'
|
||||
|
||||
console.log(queue.dequeue());
|
||||
//=> '🦄'
|
||||
|
||||
console.log(queue.dequeue());
|
||||
//=> '🌈'
|
||||
```
|
||||
*/
|
||||
constructor();
|
||||
|
||||
/**
|
||||
The instance is an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols), which means you can iterate over the queue front to back with a “for…of” loop. Using the iterator will not remove the items from the queue. If you want that, use `drain()` instead.
|
||||
|
||||
You can also use spreading to convert the queue to an array. Don't do this unless you really need to though, since it's slow.
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<ValueType>;
|
||||
|
||||
/**
|
||||
Returns an iterator that dequeues items as you consume it.
|
||||
|
||||
This allows you to empty the queue while processing its items.
|
||||
|
||||
If you want to not remove items as you consume it, use the `Queue` object as an iterator.
|
||||
*/
|
||||
drain(): IterableIterator<ValueType>;
|
||||
|
||||
/**
|
||||
Add a value to the queue.
|
||||
*/
|
||||
enqueue(value: ValueType): void;
|
||||
|
||||
/**
|
||||
Remove the next value in the queue.
|
||||
|
||||
@returns The removed value or `undefined` if the queue is empty.
|
||||
*/
|
||||
dequeue(): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Get the next value in the queue without removing it.
|
||||
|
||||
@returns The value or `undefined` if the queue is empty.
|
||||
*/
|
||||
peek(): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Clear the queue.
|
||||
*/
|
||||
clear(): void;
|
||||
}
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
How it works:
|
||||
`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
|
||||
*/
|
||||
|
||||
class Node {
|
||||
value;
|
||||
next;
|
||||
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Queue {
|
||||
#head;
|
||||
#tail;
|
||||
#size;
|
||||
|
||||
constructor() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
enqueue(value) {
|
||||
const node = new Node(value);
|
||||
|
||||
if (this.#head) {
|
||||
this.#tail.next = node;
|
||||
this.#tail = node;
|
||||
} else {
|
||||
this.#head = node;
|
||||
this.#tail = node;
|
||||
}
|
||||
|
||||
this.#size++;
|
||||
}
|
||||
|
||||
dequeue() {
|
||||
const current = this.#head;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#head = this.#head.next;
|
||||
this.#size--;
|
||||
|
||||
// Clean up tail reference when queue becomes empty
|
||||
if (!this.#head) {
|
||||
this.#tail = undefined;
|
||||
}
|
||||
|
||||
return current.value;
|
||||
}
|
||||
|
||||
peek() {
|
||||
if (!this.#head) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.#head.value;
|
||||
|
||||
// TODO: Node.js 18.
|
||||
// return this.#head?.value;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#head = undefined;
|
||||
this.#tail = undefined;
|
||||
this.#size = 0;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.#size;
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
let current = this.#head;
|
||||
|
||||
while (current) {
|
||||
yield current.value;
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
|
||||
* drain() {
|
||||
while (this.#head) {
|
||||
yield this.dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "yocto-queue",
|
||||
"version": "1.2.2",
|
||||
"description": "Tiny queue data structure",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/yocto-queue",
|
||||
"funding": "https://github.com/sponsors/sindresorhus",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"scripts": {
|
||||
"//test": "xo && ava && tsd",
|
||||
"test": "ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"queue",
|
||||
"data",
|
||||
"structure",
|
||||
"algorithm",
|
||||
"queues",
|
||||
"queuing",
|
||||
"list",
|
||||
"array",
|
||||
"linkedlist",
|
||||
"fifo",
|
||||
"enqueue",
|
||||
"dequeue",
|
||||
"data-structure"
|
||||
],
|
||||
"devDependencies": {
|
||||
"ava": "^3.15.0",
|
||||
"tsd": "^0.17.0",
|
||||
"xo": "^0.44.0"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
# yocto-queue [](https://bundlephobia.com/result?p=yocto-queue)
|
||||
|
||||
> Tiny queue data structure
|
||||
|
||||
You should use this package instead of an array if you do a lot of `Array#push()` and `Array#shift()` on large arrays, since `Array#shift()` has [linear time complexity](https://medium.com/@ariel.salem1989/an-easy-to-use-guide-to-big-o-time-complexity-5dcf4be8a444#:~:text=O(N)%E2%80%94Linear%20Time) *O(n)* while `Queue#dequeue()` has [constant time complexity](https://medium.com/@ariel.salem1989/an-easy-to-use-guide-to-big-o-time-complexity-5dcf4be8a444#:~:text=O(1)%20%E2%80%94%20Constant%20Time) *O(1)*. That makes a huge difference for large arrays.
|
||||
|
||||
> A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is an ordered list of elements where an element is inserted at the end of the queue and is removed from the front of the queue. A queue works based on the first-in, first-out ([FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))) principle.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install yocto-queue
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import Queue from 'yocto-queue';
|
||||
|
||||
const queue = new Queue();
|
||||
|
||||
queue.enqueue('🦄');
|
||||
queue.enqueue('🌈');
|
||||
|
||||
console.log(queue.size);
|
||||
//=> 2
|
||||
|
||||
console.log(...queue);
|
||||
//=> '🦄 🌈'
|
||||
|
||||
console.log(queue.dequeue());
|
||||
//=> '🦄'
|
||||
|
||||
console.log(queue.dequeue());
|
||||
//=> '🌈'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `queue = new Queue()`
|
||||
|
||||
The instance is an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols), which means you can iterate over the queue front to back with a “for…of” loop. Using the iterator will not remove the items from the queue. If you want that, use [`drain()`](#drain) instead.
|
||||
|
||||
You can also use spreading to convert the queue to an array. Don't do this unless you really need to though, since it's slow.
|
||||
|
||||
#### `.enqueue(value)`
|
||||
|
||||
Add a value to the queue.
|
||||
|
||||
#### `.dequeue()`
|
||||
|
||||
Remove the next value in the queue.
|
||||
|
||||
Returns the removed value or `undefined` if the queue is empty.
|
||||
|
||||
#### `.peek()`
|
||||
|
||||
Get the next value in the queue without removing it.
|
||||
|
||||
Returns the value or `undefined` if the queue is empty.
|
||||
|
||||
#### `.drain()`
|
||||
|
||||
Returns an iterator that dequeues items as you consume it.
|
||||
|
||||
This allows you to empty the queue while processing its items.
|
||||
|
||||
If you want to not remove items as you consume it, use the `Queue` object as an iterator.
|
||||
|
||||
#### `.clear()`
|
||||
|
||||
Clear the queue.
|
||||
|
||||
#### `.size`
|
||||
|
||||
The size of the queue.
|
||||
|
||||
## Related
|
||||
|
||||
- [quick-lru](https://github.com/sindresorhus/quick-lru) - Simple “Least Recently Used” (LRU) cache
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@vitest/runner",
|
||||
"type": "module",
|
||||
"version": "1.6.1",
|
||||
"description": "Vitest test runner",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/runner#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/runner"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./utils": {
|
||||
"types": "./dist/utils.d.ts",
|
||||
"default": "./dist/utils.js"
|
||||
},
|
||||
"./types": {
|
||||
"types": "./dist/types.d.ts",
|
||||
"default": "./dist/types.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"p-limit": "^5.0.0",
|
||||
"pathe": "^1.1.1",
|
||||
"@vitest/utils": "1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c",
|
||||
"dev": "rollup -c --watch"
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/types.js'
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/utils.js'
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-Present Vitest Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# @vitest/snapshot
|
||||
|
||||
Lightweight implementation of Jest's snapshots.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { SnapshotClient } from '@vitest/snapshot'
|
||||
import { NodeSnapshotEnvironment } from '@vitest/snapshot/environment'
|
||||
import { SnapshotManager } from '@vitest/snapshot/manager'
|
||||
|
||||
const client = new SnapshotClient({
|
||||
// you need to provide your own equality check implementation if you use it
|
||||
// this function is called when `.toMatchSnapshot({ property: 1 })` is called
|
||||
isEqual: (received, expected) => equals(received, expected, [iterableEquality, subsetEquality]),
|
||||
})
|
||||
|
||||
// class that implements snapshot saving and reading
|
||||
// by default uses fs module, but you can provide your own implementation depending on the environment
|
||||
const environment = new NodeSnapshotEnvironment()
|
||||
|
||||
// you need to implement this yourselves,
|
||||
// this depends on your runner
|
||||
function getCurrentFilepath() {
|
||||
return '/file.spec.js'
|
||||
}
|
||||
function getCurrentTestName() {
|
||||
return 'test1'
|
||||
}
|
||||
|
||||
// example for inline snapshots, nothing is required to support regular snapshots,
|
||||
// just call `assert` with `isInline: false`
|
||||
function wrapper(received) {
|
||||
function __INLINE_SNAPSHOT__(inlineSnapshot, message) {
|
||||
client.assert({
|
||||
received,
|
||||
message,
|
||||
isInline: true,
|
||||
inlineSnapshot,
|
||||
filepath: getCurrentFilepath(),
|
||||
name: getCurrentTestName(),
|
||||
})
|
||||
}
|
||||
return {
|
||||
// the name is hard-coded, it should be inside another function, so Vitest can find the actual test file where it was called (parses call stack trace + 2)
|
||||
// you can override this behaviour in SnapshotState's `_inferInlineSnapshotStack` method by providing your own SnapshotState to SnapshotClient constructor
|
||||
toMatchInlineSnapshot: (...args) => __INLINE_SNAPSHOT__(...args),
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
updateSnapshot: 'new',
|
||||
snapshotEnvironment: environment,
|
||||
}
|
||||
|
||||
await client.startCurrentRun(getCurrentFilepath(), getCurrentTestName(), options)
|
||||
|
||||
// this will save snapshot to a file which is returned by "snapshotEnvironment.resolvePath"
|
||||
client.assert({
|
||||
received: 'some text',
|
||||
isInline: false,
|
||||
})
|
||||
|
||||
// uses "pretty-format", so it requires quotes
|
||||
// also naming is hard-coded when parsing test files
|
||||
wrapper('text 1').toMatchInlineSnapshot()
|
||||
wrapper('text 2').toMatchInlineSnapshot('"text 2"')
|
||||
|
||||
const result = await client.finishCurrentRun() // this saves files and returns SnapshotResult
|
||||
|
||||
// you can use manager to manage several clients
|
||||
const manager = new SnapshotManager(options)
|
||||
manager.add(result)
|
||||
|
||||
// do something
|
||||
// and then read the summary
|
||||
|
||||
console.log(manager.summary)
|
||||
```
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
interface SnapshotEnvironment {
|
||||
getVersion: () => string;
|
||||
getHeader: () => string;
|
||||
resolvePath: (filepath: string) => Promise<string>;
|
||||
resolveRawPath: (testPath: string, rawPath: string) => Promise<string>;
|
||||
saveSnapshotFile: (filepath: string, snapshot: string) => Promise<void>;
|
||||
readSnapshotFile: (filepath: string) => Promise<string | null>;
|
||||
removeSnapshotFile: (filepath: string) => Promise<void>;
|
||||
}
|
||||
interface SnapshotEnvironmentOptions {
|
||||
snapshotsDirName?: string;
|
||||
}
|
||||
|
||||
export type { SnapshotEnvironment as S, SnapshotEnvironmentOptions as a };
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { S as SnapshotEnvironment, a as SnapshotEnvironmentOptions } from './environment-cMiGIVXz.js';
|
||||
|
||||
declare class NodeSnapshotEnvironment implements SnapshotEnvironment {
|
||||
private options;
|
||||
constructor(options?: SnapshotEnvironmentOptions);
|
||||
getVersion(): string;
|
||||
getHeader(): string;
|
||||
resolveRawPath(testPath: string, rawPath: string): Promise<string>;
|
||||
resolvePath(filepath: string): Promise<string>;
|
||||
prepareDirectory(dirPath: string): Promise<void>;
|
||||
saveSnapshotFile(filepath: string, snapshot: string): Promise<void>;
|
||||
readSnapshotFile(filepath: string): Promise<string | null>;
|
||||
removeSnapshotFile(filepath: string): Promise<void>;
|
||||
}
|
||||
|
||||
export { NodeSnapshotEnvironment, SnapshotEnvironment };
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { promises, existsSync } from 'node:fs';
|
||||
import { isAbsolute, resolve, dirname, join, basename } from 'pathe';
|
||||
|
||||
class NodeSnapshotEnvironment {
|
||||
constructor(options = {}) {
|
||||
this.options = options;
|
||||
}
|
||||
getVersion() {
|
||||
return "1";
|
||||
}
|
||||
getHeader() {
|
||||
return `// Snapshot v${this.getVersion()}`;
|
||||
}
|
||||
async resolveRawPath(testPath, rawPath) {
|
||||
return isAbsolute(rawPath) ? rawPath : resolve(dirname(testPath), rawPath);
|
||||
}
|
||||
async resolvePath(filepath) {
|
||||
return join(
|
||||
join(
|
||||
dirname(filepath),
|
||||
this.options.snapshotsDirName ?? "__snapshots__"
|
||||
),
|
||||
`${basename(filepath)}.snap`
|
||||
);
|
||||
}
|
||||
async prepareDirectory(dirPath) {
|
||||
await promises.mkdir(dirPath, { recursive: true });
|
||||
}
|
||||
async saveSnapshotFile(filepath, snapshot) {
|
||||
await promises.mkdir(dirname(filepath), { recursive: true });
|
||||
await promises.writeFile(filepath, snapshot, "utf-8");
|
||||
}
|
||||
async readSnapshotFile(filepath) {
|
||||
if (!existsSync(filepath))
|
||||
return null;
|
||||
return promises.readFile(filepath, "utf-8");
|
||||
}
|
||||
async removeSnapshotFile(filepath) {
|
||||
if (existsSync(filepath))
|
||||
await promises.unlink(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
export { NodeSnapshotEnvironment };
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { Plugin, OptionsReceived } from 'pretty-format';
|
||||
import { S as SnapshotEnvironment } from './environment-cMiGIVXz.js';
|
||||
|
||||
interface RawSnapshotInfo {
|
||||
file: string;
|
||||
readonly?: boolean;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
type SnapshotData = Record<string, string>;
|
||||
type SnapshotUpdateState = 'all' | 'new' | 'none';
|
||||
type SnapshotSerializer = Plugin;
|
||||
interface SnapshotStateOptions {
|
||||
updateSnapshot: SnapshotUpdateState;
|
||||
snapshotEnvironment: SnapshotEnvironment;
|
||||
expand?: boolean;
|
||||
snapshotFormat?: OptionsReceived;
|
||||
resolveSnapshotPath?: (path: string, extension: string) => string;
|
||||
}
|
||||
interface SnapshotMatchOptions {
|
||||
testName: string;
|
||||
received: unknown;
|
||||
key?: string;
|
||||
inlineSnapshot?: string;
|
||||
isInline: boolean;
|
||||
error?: Error;
|
||||
rawSnapshot?: RawSnapshotInfo;
|
||||
}
|
||||
interface SnapshotResult {
|
||||
filepath: string;
|
||||
added: number;
|
||||
fileDeleted: boolean;
|
||||
matched: number;
|
||||
unchecked: number;
|
||||
uncheckedKeys: Array<string>;
|
||||
unmatched: number;
|
||||
updated: number;
|
||||
}
|
||||
interface UncheckedSnapshot {
|
||||
filePath: string;
|
||||
keys: Array<string>;
|
||||
}
|
||||
interface SnapshotSummary {
|
||||
added: number;
|
||||
didUpdate: boolean;
|
||||
failure: boolean;
|
||||
filesAdded: number;
|
||||
filesRemoved: number;
|
||||
filesRemovedList: Array<string>;
|
||||
filesUnmatched: number;
|
||||
filesUpdated: number;
|
||||
matched: number;
|
||||
total: number;
|
||||
unchecked: number;
|
||||
uncheckedKeysByFile: Array<UncheckedSnapshot>;
|
||||
unmatched: number;
|
||||
updated: number;
|
||||
}
|
||||
|
||||
export type { RawSnapshotInfo as R, SnapshotStateOptions as S, UncheckedSnapshot as U, SnapshotMatchOptions as a, SnapshotResult as b, SnapshotData as c, SnapshotUpdateState as d, SnapshotSerializer as e, SnapshotSummary as f };
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import { S as SnapshotStateOptions, a as SnapshotMatchOptions, b as SnapshotResult, R as RawSnapshotInfo } from './index-S94ASl6q.js';
|
||||
export { c as SnapshotData, e as SnapshotSerializer, f as SnapshotSummary, d as SnapshotUpdateState, U as UncheckedSnapshot } from './index-S94ASl6q.js';
|
||||
import { S as SnapshotEnvironment } from './environment-cMiGIVXz.js';
|
||||
import { Plugin, Plugins } from 'pretty-format';
|
||||
|
||||
interface ParsedStack {
|
||||
method: string;
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
interface SnapshotReturnOptions {
|
||||
actual: string;
|
||||
count: number;
|
||||
expected?: string;
|
||||
key: string;
|
||||
pass: boolean;
|
||||
}
|
||||
interface SaveStatus {
|
||||
deleted: boolean;
|
||||
saved: boolean;
|
||||
}
|
||||
declare class SnapshotState {
|
||||
testFilePath: string;
|
||||
snapshotPath: string;
|
||||
private _counters;
|
||||
private _dirty;
|
||||
private _updateSnapshot;
|
||||
private _snapshotData;
|
||||
private _initialData;
|
||||
private _inlineSnapshots;
|
||||
private _rawSnapshots;
|
||||
private _uncheckedKeys;
|
||||
private _snapshotFormat;
|
||||
private _environment;
|
||||
private _fileExists;
|
||||
added: number;
|
||||
expand: boolean;
|
||||
matched: number;
|
||||
unmatched: number;
|
||||
updated: number;
|
||||
private constructor();
|
||||
static create(testFilePath: string, options: SnapshotStateOptions): Promise<SnapshotState>;
|
||||
get environment(): SnapshotEnvironment;
|
||||
markSnapshotsAsCheckedForTest(testName: string): void;
|
||||
protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null;
|
||||
private _addSnapshot;
|
||||
clear(): void;
|
||||
save(): Promise<SaveStatus>;
|
||||
getUncheckedCount(): number;
|
||||
getUncheckedKeys(): Array<string>;
|
||||
removeUncheckedKeys(): void;
|
||||
match({ testName, received, key, inlineSnapshot, isInline, error, rawSnapshot, }: SnapshotMatchOptions): SnapshotReturnOptions;
|
||||
pack(): Promise<SnapshotResult>;
|
||||
}
|
||||
|
||||
interface AssertOptions {
|
||||
received: unknown;
|
||||
filepath?: string;
|
||||
name?: string;
|
||||
message?: string;
|
||||
isInline?: boolean;
|
||||
properties?: object;
|
||||
inlineSnapshot?: string;
|
||||
error?: Error;
|
||||
errorMessage?: string;
|
||||
rawSnapshot?: RawSnapshotInfo;
|
||||
}
|
||||
interface SnapshotClientOptions {
|
||||
isEqual?: (received: unknown, expected: unknown) => boolean;
|
||||
}
|
||||
declare class SnapshotClient {
|
||||
private options;
|
||||
filepath?: string;
|
||||
name?: string;
|
||||
snapshotState: SnapshotState | undefined;
|
||||
snapshotStateMap: Map<string, SnapshotState>;
|
||||
constructor(options?: SnapshotClientOptions);
|
||||
startCurrentRun(filepath: string, name: string, options: SnapshotStateOptions): Promise<void>;
|
||||
getSnapshotState(filepath: string): SnapshotState;
|
||||
clearTest(): void;
|
||||
skipTestSnapshots(name: string): void;
|
||||
assert(options: AssertOptions): void;
|
||||
assertRaw(options: AssertOptions): Promise<void>;
|
||||
finishCurrentRun(): Promise<SnapshotResult | null>;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare function addSerializer(plugin: Plugin): void;
|
||||
declare function getSerializers(): Plugins;
|
||||
|
||||
declare function stripSnapshotIndentation(inlineSnapshot: string): string;
|
||||
|
||||
export { SnapshotClient, SnapshotMatchOptions, SnapshotResult, SnapshotState, SnapshotStateOptions, addSerializer, getSerializers, stripSnapshotIndentation };
|
||||
+1548
@@ -0,0 +1,1548 @@
|
||||
import { plugins, format } from 'pretty-format';
|
||||
import { resolve as resolve$2 } from 'pathe';
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
var naturalCompare$2 = {exports: {}};
|
||||
|
||||
/*
|
||||
* @version 1.4.0
|
||||
* @date 2015-10-26
|
||||
* @stability 3 - Stable
|
||||
* @author Lauri Rooden (https://github.com/litejs/natural-compare-lite)
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
|
||||
var naturalCompare = function(a, b) {
|
||||
var i, codeA
|
||||
, codeB = 1
|
||||
, posA = 0
|
||||
, posB = 0
|
||||
, alphabet = String.alphabet;
|
||||
|
||||
function getCode(str, pos, code) {
|
||||
if (code) {
|
||||
for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i;
|
||||
return +str.slice(pos - 1, i)
|
||||
}
|
||||
code = alphabet && alphabet.indexOf(str.charAt(pos));
|
||||
return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code
|
||||
: code < 46 ? 65 // -
|
||||
: code < 48 ? code - 1
|
||||
: code < 58 ? code + 18 // 0-9
|
||||
: code < 65 ? code - 11
|
||||
: code < 91 ? code + 11 // A-Z
|
||||
: code < 97 ? code - 37
|
||||
: code < 123 ? code + 5 // a-z
|
||||
: code - 63
|
||||
}
|
||||
|
||||
|
||||
if ((a+="") != (b+="")) for (;codeB;) {
|
||||
codeA = getCode(a, posA++);
|
||||
codeB = getCode(b, posB++);
|
||||
|
||||
if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
|
||||
codeA = getCode(a, posA, posA);
|
||||
codeB = getCode(b, posB, posA = i);
|
||||
posB = i;
|
||||
}
|
||||
|
||||
if (codeA != codeB) return (codeA < codeB) ? -1 : 1
|
||||
}
|
||||
return 0
|
||||
};
|
||||
|
||||
try {
|
||||
naturalCompare$2.exports = naturalCompare;
|
||||
} catch (e) {
|
||||
String.naturalCompare = naturalCompare;
|
||||
}
|
||||
|
||||
var naturalCompareExports = naturalCompare$2.exports;
|
||||
var naturalCompare$1 = /*@__PURE__*/getDefaultExportFromCjs(naturalCompareExports);
|
||||
|
||||
function notNullish(v) {
|
||||
return v != null;
|
||||
}
|
||||
function isPrimitive(value) {
|
||||
return value === null || typeof value !== "function" && typeof value !== "object";
|
||||
}
|
||||
function isObject(item) {
|
||||
return item != null && typeof item === "object" && !Array.isArray(item);
|
||||
}
|
||||
function getCallLastIndex(code) {
|
||||
let charIndex = -1;
|
||||
let inString = null;
|
||||
let startedBracers = 0;
|
||||
let endedBracers = 0;
|
||||
let beforeChar = null;
|
||||
while (charIndex <= code.length) {
|
||||
beforeChar = code[charIndex];
|
||||
charIndex++;
|
||||
const char = code[charIndex];
|
||||
const isCharString = char === '"' || char === "'" || char === "`";
|
||||
if (isCharString && beforeChar !== "\\") {
|
||||
if (inString === char)
|
||||
inString = null;
|
||||
else if (!inString)
|
||||
inString = char;
|
||||
}
|
||||
if (!inString) {
|
||||
if (char === "(")
|
||||
startedBracers++;
|
||||
if (char === ")")
|
||||
endedBracers++;
|
||||
}
|
||||
if (startedBracers && endedBracers && startedBracers === endedBracers)
|
||||
return charIndex;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let getPromiseValue = () => 'Promise{…}';
|
||||
try {
|
||||
const { getPromiseDetails, kPending, kRejected } = process.binding('util');
|
||||
if (Array.isArray(getPromiseDetails(Promise.resolve()))) {
|
||||
getPromiseValue = (value, options) => {
|
||||
const [state, innerValue] = getPromiseDetails(value);
|
||||
if (state === kPending) {
|
||||
return 'Promise{<pending>}'
|
||||
}
|
||||
return `Promise${state === kRejected ? '!' : ''}{${options.inspect(innerValue, options)}}`
|
||||
};
|
||||
}
|
||||
} catch (notNode) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
/* !
|
||||
* loupe
|
||||
* Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
let nodeInspect = false;
|
||||
try {
|
||||
// eslint-disable-next-line global-require
|
||||
const nodeUtil = require('util');
|
||||
nodeInspect = nodeUtil.inspect ? nodeUtil.inspect.custom : false;
|
||||
} catch (noNodeInspect) {
|
||||
nodeInspect = false;
|
||||
}
|
||||
|
||||
const lineSplitRE = /\r?\n/;
|
||||
function positionToOffset(source, lineNumber, columnNumber) {
|
||||
const lines = source.split(lineSplitRE);
|
||||
const nl = /\r\n/.test(source) ? 2 : 1;
|
||||
let start = 0;
|
||||
if (lineNumber > lines.length)
|
||||
return source.length;
|
||||
for (let i = 0; i < lineNumber - 1; i++)
|
||||
start += lines[i].length + nl;
|
||||
return start + columnNumber;
|
||||
}
|
||||
function offsetToLineNumber(source, offset) {
|
||||
if (offset > source.length) {
|
||||
throw new Error(
|
||||
`offset is longer than source length! offset ${offset} > length ${source.length}`
|
||||
);
|
||||
}
|
||||
const lines = source.split(lineSplitRE);
|
||||
const nl = /\r\n/.test(source) ? 2 : 1;
|
||||
let counted = 0;
|
||||
let line = 0;
|
||||
for (; line < lines.length; line++) {
|
||||
const lineLength = lines[line].length + nl;
|
||||
if (counted + lineLength >= offset)
|
||||
break;
|
||||
counted += lineLength;
|
||||
}
|
||||
return line + 1;
|
||||
}
|
||||
|
||||
// Copyright 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Simon Lydell
|
||||
// License: MIT.
|
||||
var LineTerminatorSequence;
|
||||
LineTerminatorSequence = /\r?\n|[\r\u2028\u2029]/y;
|
||||
RegExp(LineTerminatorSequence.source);
|
||||
|
||||
// src/index.ts
|
||||
var reservedWords = {
|
||||
keyword: [
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"do",
|
||||
"else",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"return",
|
||||
"switch",
|
||||
"throw",
|
||||
"try",
|
||||
"var",
|
||||
"const",
|
||||
"while",
|
||||
"with",
|
||||
"new",
|
||||
"this",
|
||||
"super",
|
||||
"class",
|
||||
"extends",
|
||||
"export",
|
||||
"import",
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"in",
|
||||
"instanceof",
|
||||
"typeof",
|
||||
"void",
|
||||
"delete"
|
||||
],
|
||||
strict: [
|
||||
"implements",
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield"
|
||||
]
|
||||
}; new Set(reservedWords.keyword); new Set(reservedWords.strict);
|
||||
|
||||
const serialize$1 = (val, config, indentation, depth, refs, printer) => {
|
||||
const name = val.getMockName();
|
||||
const nameString = name === "vi.fn()" ? "" : ` ${name}`;
|
||||
let callsString = "";
|
||||
if (val.mock.calls.length !== 0) {
|
||||
const indentationNext = indentation + config.indent;
|
||||
callsString = ` {${config.spacingOuter}${indentationNext}"calls": ${printer(val.mock.calls, config, indentationNext, depth, refs)}${config.min ? ", " : ","}${config.spacingOuter}${indentationNext}"results": ${printer(val.mock.results, config, indentationNext, depth, refs)}${config.min ? "" : ","}${config.spacingOuter}${indentation}}`;
|
||||
}
|
||||
return `[MockFunction${nameString}]${callsString}`;
|
||||
};
|
||||
const test = (val) => val && !!val._isMockFunction;
|
||||
const plugin = { serialize: serialize$1, test };
|
||||
|
||||
const {
|
||||
DOMCollection,
|
||||
DOMElement,
|
||||
Immutable,
|
||||
ReactElement,
|
||||
ReactTestComponent,
|
||||
AsymmetricMatcher
|
||||
} = plugins;
|
||||
let PLUGINS = [
|
||||
ReactTestComponent,
|
||||
ReactElement,
|
||||
DOMElement,
|
||||
DOMCollection,
|
||||
Immutable,
|
||||
AsymmetricMatcher,
|
||||
plugin
|
||||
];
|
||||
function addSerializer(plugin) {
|
||||
PLUGINS = [plugin].concat(PLUGINS);
|
||||
}
|
||||
function getSerializers() {
|
||||
return PLUGINS;
|
||||
}
|
||||
|
||||
function testNameToKey(testName, count) {
|
||||
return `${testName} ${count}`;
|
||||
}
|
||||
function keyToTestName(key) {
|
||||
if (!/ \d+$/.test(key))
|
||||
throw new Error("Snapshot keys must end with a number.");
|
||||
return key.replace(/ \d+$/, "");
|
||||
}
|
||||
function getSnapshotData(content, options) {
|
||||
const update = options.updateSnapshot;
|
||||
const data = /* @__PURE__ */ Object.create(null);
|
||||
let snapshotContents = "";
|
||||
let dirty = false;
|
||||
if (content != null) {
|
||||
try {
|
||||
snapshotContents = content;
|
||||
const populate = new Function("exports", snapshotContents);
|
||||
populate(data);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
const isInvalid = snapshotContents;
|
||||
if ((update === "all" || update === "new") && isInvalid)
|
||||
dirty = true;
|
||||
return { data, dirty };
|
||||
}
|
||||
function addExtraLineBreaks(string) {
|
||||
return string.includes("\n") ? `
|
||||
${string}
|
||||
` : string;
|
||||
}
|
||||
function removeExtraLineBreaks(string) {
|
||||
return string.length > 2 && string.startsWith("\n") && string.endsWith("\n") ? string.slice(1, -1) : string;
|
||||
}
|
||||
const escapeRegex = true;
|
||||
const printFunctionName = false;
|
||||
function serialize(val, indent = 2, formatOverrides = {}) {
|
||||
return normalizeNewlines(
|
||||
format(val, {
|
||||
escapeRegex,
|
||||
indent,
|
||||
plugins: getSerializers(),
|
||||
printFunctionName,
|
||||
...formatOverrides
|
||||
})
|
||||
);
|
||||
}
|
||||
function escapeBacktickString(str) {
|
||||
return str.replace(/`|\\|\${/g, "\\$&");
|
||||
}
|
||||
function printBacktickString(str) {
|
||||
return `\`${escapeBacktickString(str)}\``;
|
||||
}
|
||||
function normalizeNewlines(string) {
|
||||
return string.replace(/\r\n|\r/g, "\n");
|
||||
}
|
||||
async function saveSnapshotFile(environment, snapshotData, snapshotPath) {
|
||||
const snapshots = Object.keys(snapshotData).sort(naturalCompare$1).map(
|
||||
(key) => `exports[${printBacktickString(key)}] = ${printBacktickString(normalizeNewlines(snapshotData[key]))};`
|
||||
);
|
||||
const content = `${environment.getHeader()}
|
||||
|
||||
${snapshots.join("\n\n")}
|
||||
`;
|
||||
const oldContent = await environment.readSnapshotFile(snapshotPath);
|
||||
const skipWriting = oldContent != null && oldContent === content;
|
||||
if (skipWriting)
|
||||
return;
|
||||
await environment.saveSnapshotFile(
|
||||
snapshotPath,
|
||||
content
|
||||
);
|
||||
}
|
||||
function prepareExpected(expected) {
|
||||
function findStartIndent() {
|
||||
var _a, _b;
|
||||
const matchObject = /^( +)}\s+$/m.exec(expected || "");
|
||||
const objectIndent = (_a = matchObject == null ? void 0 : matchObject[1]) == null ? void 0 : _a.length;
|
||||
if (objectIndent)
|
||||
return objectIndent;
|
||||
const matchText = /^\n( +)"/.exec(expected || "");
|
||||
return ((_b = matchText == null ? void 0 : matchText[1]) == null ? void 0 : _b.length) || 0;
|
||||
}
|
||||
const startIndent = findStartIndent();
|
||||
let expectedTrimmed = expected == null ? void 0 : expected.trim();
|
||||
if (startIndent) {
|
||||
expectedTrimmed = expectedTrimmed == null ? void 0 : expectedTrimmed.replace(new RegExp(`^${" ".repeat(startIndent)}`, "gm"), "").replace(/ +}$/, "}");
|
||||
}
|
||||
return expectedTrimmed;
|
||||
}
|
||||
function deepMergeArray(target = [], source = []) {
|
||||
const mergedOutput = Array.from(target);
|
||||
source.forEach((sourceElement, index) => {
|
||||
const targetElement = mergedOutput[index];
|
||||
if (Array.isArray(target[index])) {
|
||||
mergedOutput[index] = deepMergeArray(target[index], sourceElement);
|
||||
} else if (isObject(targetElement)) {
|
||||
mergedOutput[index] = deepMergeSnapshot(target[index], sourceElement);
|
||||
} else {
|
||||
mergedOutput[index] = sourceElement;
|
||||
}
|
||||
});
|
||||
return mergedOutput;
|
||||
}
|
||||
function deepMergeSnapshot(target, source) {
|
||||
if (isObject(target) && isObject(source)) {
|
||||
const mergedOutput = { ...target };
|
||||
Object.keys(source).forEach((key) => {
|
||||
if (isObject(source[key]) && !source[key].$$typeof) {
|
||||
if (!(key in target))
|
||||
Object.assign(mergedOutput, { [key]: source[key] });
|
||||
else
|
||||
mergedOutput[key] = deepMergeSnapshot(target[key], source[key]);
|
||||
} else if (Array.isArray(source[key])) {
|
||||
mergedOutput[key] = deepMergeArray(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(mergedOutput, { [key]: source[key] });
|
||||
}
|
||||
});
|
||||
return mergedOutput;
|
||||
} else if (Array.isArray(target) && Array.isArray(source)) {
|
||||
return deepMergeArray(target, source);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
const comma = ','.charCodeAt(0);
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
const intToChar = new Uint8Array(64); // 64 possible chars.
|
||||
const charToInt = new Uint8Array(128); // z is 122 in ASCII
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const c = chars.charCodeAt(i);
|
||||
intToChar[i] = c;
|
||||
charToInt[c] = i;
|
||||
}
|
||||
function decode(mappings) {
|
||||
const state = new Int32Array(5);
|
||||
const decoded = [];
|
||||
let index = 0;
|
||||
do {
|
||||
const semi = indexOf(mappings, index);
|
||||
const line = [];
|
||||
let sorted = true;
|
||||
let lastCol = 0;
|
||||
state[0] = 0;
|
||||
for (let i = index; i < semi; i++) {
|
||||
let seg;
|
||||
i = decodeInteger(mappings, i, state, 0); // genColumn
|
||||
const col = state[0];
|
||||
if (col < lastCol)
|
||||
sorted = false;
|
||||
lastCol = col;
|
||||
if (hasMoreVlq(mappings, i, semi)) {
|
||||
i = decodeInteger(mappings, i, state, 1); // sourcesIndex
|
||||
i = decodeInteger(mappings, i, state, 2); // sourceLine
|
||||
i = decodeInteger(mappings, i, state, 3); // sourceColumn
|
||||
if (hasMoreVlq(mappings, i, semi)) {
|
||||
i = decodeInteger(mappings, i, state, 4); // namesIndex
|
||||
seg = [col, state[1], state[2], state[3], state[4]];
|
||||
}
|
||||
else {
|
||||
seg = [col, state[1], state[2], state[3]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
seg = [col];
|
||||
}
|
||||
line.push(seg);
|
||||
}
|
||||
if (!sorted)
|
||||
sort(line);
|
||||
decoded.push(line);
|
||||
index = semi + 1;
|
||||
} while (index <= mappings.length);
|
||||
return decoded;
|
||||
}
|
||||
function indexOf(mappings, index) {
|
||||
const idx = mappings.indexOf(';', index);
|
||||
return idx === -1 ? mappings.length : idx;
|
||||
}
|
||||
function decodeInteger(mappings, pos, state, j) {
|
||||
let value = 0;
|
||||
let shift = 0;
|
||||
let integer = 0;
|
||||
do {
|
||||
const c = mappings.charCodeAt(pos++);
|
||||
integer = charToInt[c];
|
||||
value |= (integer & 31) << shift;
|
||||
shift += 5;
|
||||
} while (integer & 32);
|
||||
const shouldNegate = value & 1;
|
||||
value >>>= 1;
|
||||
if (shouldNegate) {
|
||||
value = -0x80000000 | -value;
|
||||
}
|
||||
state[j] += value;
|
||||
return pos;
|
||||
}
|
||||
function hasMoreVlq(mappings, i, length) {
|
||||
if (i >= length)
|
||||
return false;
|
||||
return mappings.charCodeAt(i) !== comma;
|
||||
}
|
||||
function sort(line) {
|
||||
line.sort(sortComparator$1);
|
||||
}
|
||||
function sortComparator$1(a, b) {
|
||||
return a[0] - b[0];
|
||||
}
|
||||
|
||||
// Matches the scheme of a URL, eg "http://"
|
||||
const schemeRegex = /^[\w+.-]+:\/\//;
|
||||
/**
|
||||
* Matches the parts of a URL:
|
||||
* 1. Scheme, including ":", guaranteed.
|
||||
* 2. User/password, including "@", optional.
|
||||
* 3. Host, guaranteed.
|
||||
* 4. Port, including ":", optional.
|
||||
* 5. Path, including "/", optional.
|
||||
* 6. Query, including "?", optional.
|
||||
* 7. Hash, including "#", optional.
|
||||
*/
|
||||
const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
|
||||
/**
|
||||
* File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
|
||||
* with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
|
||||
*
|
||||
* 1. Host, optional.
|
||||
* 2. Path, which may include "/", guaranteed.
|
||||
* 3. Query, including "?", optional.
|
||||
* 4. Hash, including "#", optional.
|
||||
*/
|
||||
const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
|
||||
var UrlType;
|
||||
(function (UrlType) {
|
||||
UrlType[UrlType["Empty"] = 1] = "Empty";
|
||||
UrlType[UrlType["Hash"] = 2] = "Hash";
|
||||
UrlType[UrlType["Query"] = 3] = "Query";
|
||||
UrlType[UrlType["RelativePath"] = 4] = "RelativePath";
|
||||
UrlType[UrlType["AbsolutePath"] = 5] = "AbsolutePath";
|
||||
UrlType[UrlType["SchemeRelative"] = 6] = "SchemeRelative";
|
||||
UrlType[UrlType["Absolute"] = 7] = "Absolute";
|
||||
})(UrlType || (UrlType = {}));
|
||||
function isAbsoluteUrl(input) {
|
||||
return schemeRegex.test(input);
|
||||
}
|
||||
function isSchemeRelativeUrl(input) {
|
||||
return input.startsWith('//');
|
||||
}
|
||||
function isAbsolutePath(input) {
|
||||
return input.startsWith('/');
|
||||
}
|
||||
function isFileUrl(input) {
|
||||
return input.startsWith('file:');
|
||||
}
|
||||
function isRelative(input) {
|
||||
return /^[.?#]/.test(input);
|
||||
}
|
||||
function parseAbsoluteUrl(input) {
|
||||
const match = urlRegex.exec(input);
|
||||
return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
|
||||
}
|
||||
function parseFileUrl(input) {
|
||||
const match = fileRegex.exec(input);
|
||||
const path = match[2];
|
||||
return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
|
||||
}
|
||||
function makeUrl(scheme, user, host, port, path, query, hash) {
|
||||
return {
|
||||
scheme,
|
||||
user,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
query,
|
||||
hash,
|
||||
type: UrlType.Absolute,
|
||||
};
|
||||
}
|
||||
function parseUrl(input) {
|
||||
if (isSchemeRelativeUrl(input)) {
|
||||
const url = parseAbsoluteUrl('http:' + input);
|
||||
url.scheme = '';
|
||||
url.type = UrlType.SchemeRelative;
|
||||
return url;
|
||||
}
|
||||
if (isAbsolutePath(input)) {
|
||||
const url = parseAbsoluteUrl('http://foo.com' + input);
|
||||
url.scheme = '';
|
||||
url.host = '';
|
||||
url.type = UrlType.AbsolutePath;
|
||||
return url;
|
||||
}
|
||||
if (isFileUrl(input))
|
||||
return parseFileUrl(input);
|
||||
if (isAbsoluteUrl(input))
|
||||
return parseAbsoluteUrl(input);
|
||||
const url = parseAbsoluteUrl('http://foo.com/' + input);
|
||||
url.scheme = '';
|
||||
url.host = '';
|
||||
url.type = input
|
||||
? input.startsWith('?')
|
||||
? UrlType.Query
|
||||
: input.startsWith('#')
|
||||
? UrlType.Hash
|
||||
: UrlType.RelativePath
|
||||
: UrlType.Empty;
|
||||
return url;
|
||||
}
|
||||
function stripPathFilename(path) {
|
||||
// If a path ends with a parent directory "..", then it's a relative path with excess parent
|
||||
// paths. It's not a file, so we can't strip it.
|
||||
if (path.endsWith('/..'))
|
||||
return path;
|
||||
const index = path.lastIndexOf('/');
|
||||
return path.slice(0, index + 1);
|
||||
}
|
||||
function mergePaths(url, base) {
|
||||
normalizePath(base, base.type);
|
||||
// If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
|
||||
// path).
|
||||
if (url.path === '/') {
|
||||
url.path = base.path;
|
||||
}
|
||||
else {
|
||||
// Resolution happens relative to the base path's directory, not the file.
|
||||
url.path = stripPathFilename(base.path) + url.path;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The path can have empty directories "//", unneeded parents "foo/..", or current directory
|
||||
* "foo/.". We need to normalize to a standard representation.
|
||||
*/
|
||||
function normalizePath(url, type) {
|
||||
const rel = type <= UrlType.RelativePath;
|
||||
const pieces = url.path.split('/');
|
||||
// We need to preserve the first piece always, so that we output a leading slash. The item at
|
||||
// pieces[0] is an empty string.
|
||||
let pointer = 1;
|
||||
// Positive is the number of real directories we've output, used for popping a parent directory.
|
||||
// Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
|
||||
let positive = 0;
|
||||
// We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
|
||||
// generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
|
||||
// real directory, we won't need to append, unless the other conditions happen again.
|
||||
let addTrailingSlash = false;
|
||||
for (let i = 1; i < pieces.length; i++) {
|
||||
const piece = pieces[i];
|
||||
// An empty directory, could be a trailing slash, or just a double "//" in the path.
|
||||
if (!piece) {
|
||||
addTrailingSlash = true;
|
||||
continue;
|
||||
}
|
||||
// If we encounter a real directory, then we don't need to append anymore.
|
||||
addTrailingSlash = false;
|
||||
// A current directory, which we can always drop.
|
||||
if (piece === '.')
|
||||
continue;
|
||||
// A parent directory, we need to see if there are any real directories we can pop. Else, we
|
||||
// have an excess of parents, and we'll need to keep the "..".
|
||||
if (piece === '..') {
|
||||
if (positive) {
|
||||
addTrailingSlash = true;
|
||||
positive--;
|
||||
pointer--;
|
||||
}
|
||||
else if (rel) {
|
||||
// If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
|
||||
// URL, protocol relative URL, or an absolute path, we don't need to keep excess.
|
||||
pieces[pointer++] = piece;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// We've encountered a real directory. Move it to the next insertion pointer, which accounts for
|
||||
// any popped or dropped directories.
|
||||
pieces[pointer++] = piece;
|
||||
positive++;
|
||||
}
|
||||
let path = '';
|
||||
for (let i = 1; i < pointer; i++) {
|
||||
path += '/' + pieces[i];
|
||||
}
|
||||
if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
|
||||
path += '/';
|
||||
}
|
||||
url.path = path;
|
||||
}
|
||||
/**
|
||||
* Attempts to resolve `input` URL/path relative to `base`.
|
||||
*/
|
||||
function resolve$1(input, base) {
|
||||
if (!input && !base)
|
||||
return '';
|
||||
const url = parseUrl(input);
|
||||
let inputType = url.type;
|
||||
if (base && inputType !== UrlType.Absolute) {
|
||||
const baseUrl = parseUrl(base);
|
||||
const baseType = baseUrl.type;
|
||||
switch (inputType) {
|
||||
case UrlType.Empty:
|
||||
url.hash = baseUrl.hash;
|
||||
// fall through
|
||||
case UrlType.Hash:
|
||||
url.query = baseUrl.query;
|
||||
// fall through
|
||||
case UrlType.Query:
|
||||
case UrlType.RelativePath:
|
||||
mergePaths(url, baseUrl);
|
||||
// fall through
|
||||
case UrlType.AbsolutePath:
|
||||
// The host, user, and port are joined, you can't copy one without the others.
|
||||
url.user = baseUrl.user;
|
||||
url.host = baseUrl.host;
|
||||
url.port = baseUrl.port;
|
||||
// fall through
|
||||
case UrlType.SchemeRelative:
|
||||
// The input doesn't have a schema at least, so we need to copy at least that over.
|
||||
url.scheme = baseUrl.scheme;
|
||||
}
|
||||
if (baseType > inputType)
|
||||
inputType = baseType;
|
||||
}
|
||||
normalizePath(url, inputType);
|
||||
const queryHash = url.query + url.hash;
|
||||
switch (inputType) {
|
||||
// This is impossible, because of the empty checks at the start of the function.
|
||||
// case UrlType.Empty:
|
||||
case UrlType.Hash:
|
||||
case UrlType.Query:
|
||||
return queryHash;
|
||||
case UrlType.RelativePath: {
|
||||
// The first char is always a "/", and we need it to be relative.
|
||||
const path = url.path.slice(1);
|
||||
if (!path)
|
||||
return queryHash || '.';
|
||||
if (isRelative(base || input) && !isRelative(path)) {
|
||||
// If base started with a leading ".", or there is no base and input started with a ".",
|
||||
// then we need to ensure that the relative path starts with a ".". We don't know if
|
||||
// relative starts with a "..", though, so check before prepending.
|
||||
return './' + path + queryHash;
|
||||
}
|
||||
return path + queryHash;
|
||||
}
|
||||
case UrlType.AbsolutePath:
|
||||
return url.path + queryHash;
|
||||
default:
|
||||
return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
|
||||
}
|
||||
}
|
||||
|
||||
function resolve(input, base) {
|
||||
// The base is always treated as a directory, if it's not empty.
|
||||
// https://github.com/mozilla/source-map/blob/8cb3ee57/lib/util.js#L327
|
||||
// https://github.com/chromium/chromium/blob/da4adbb3/third_party/blink/renderer/devtools/front_end/sdk/SourceMap.js#L400-L401
|
||||
if (base && !base.endsWith('/'))
|
||||
base += '/';
|
||||
return resolve$1(input, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes everything after the last "/", but leaves the slash.
|
||||
*/
|
||||
function stripFilename(path) {
|
||||
if (!path)
|
||||
return '';
|
||||
const index = path.lastIndexOf('/');
|
||||
return path.slice(0, index + 1);
|
||||
}
|
||||
|
||||
const COLUMN = 0;
|
||||
const SOURCES_INDEX = 1;
|
||||
const SOURCE_LINE = 2;
|
||||
const SOURCE_COLUMN = 3;
|
||||
const NAMES_INDEX = 4;
|
||||
|
||||
function maybeSort(mappings, owned) {
|
||||
const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
|
||||
if (unsortedIndex === mappings.length)
|
||||
return mappings;
|
||||
// If we own the array (meaning we parsed it from JSON), then we're free to directly mutate it. If
|
||||
// not, we do not want to modify the consumer's input array.
|
||||
if (!owned)
|
||||
mappings = mappings.slice();
|
||||
for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) {
|
||||
mappings[i] = sortSegments(mappings[i], owned);
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
function nextUnsortedSegmentLine(mappings, start) {
|
||||
for (let i = start; i < mappings.length; i++) {
|
||||
if (!isSorted(mappings[i]))
|
||||
return i;
|
||||
}
|
||||
return mappings.length;
|
||||
}
|
||||
function isSorted(line) {
|
||||
for (let j = 1; j < line.length; j++) {
|
||||
if (line[j][COLUMN] < line[j - 1][COLUMN]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function sortSegments(line, owned) {
|
||||
if (!owned)
|
||||
line = line.slice();
|
||||
return line.sort(sortComparator);
|
||||
}
|
||||
function sortComparator(a, b) {
|
||||
return a[COLUMN] - b[COLUMN];
|
||||
}
|
||||
|
||||
let found = false;
|
||||
/**
|
||||
* A binary search implementation that returns the index if a match is found.
|
||||
* If no match is found, then the left-index (the index associated with the item that comes just
|
||||
* before the desired index) is returned. To maintain proper sort order, a splice would happen at
|
||||
* the next index:
|
||||
*
|
||||
* ```js
|
||||
* const array = [1, 3];
|
||||
* const needle = 2;
|
||||
* const index = binarySearch(array, needle, (item, needle) => item - needle);
|
||||
*
|
||||
* assert.equal(index, 0);
|
||||
* array.splice(index + 1, 0, needle);
|
||||
* assert.deepEqual(array, [1, 2, 3]);
|
||||
* ```
|
||||
*/
|
||||
function binarySearch(haystack, needle, low, high) {
|
||||
while (low <= high) {
|
||||
const mid = low + ((high - low) >> 1);
|
||||
const cmp = haystack[mid][COLUMN] - needle;
|
||||
if (cmp === 0) {
|
||||
found = true;
|
||||
return mid;
|
||||
}
|
||||
if (cmp < 0) {
|
||||
low = mid + 1;
|
||||
}
|
||||
else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
found = false;
|
||||
return low - 1;
|
||||
}
|
||||
function upperBound(haystack, needle, index) {
|
||||
for (let i = index + 1; i < haystack.length; index = i++) {
|
||||
if (haystack[i][COLUMN] !== needle)
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function lowerBound(haystack, needle, index) {
|
||||
for (let i = index - 1; i >= 0; index = i--) {
|
||||
if (haystack[i][COLUMN] !== needle)
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function memoizedState() {
|
||||
return {
|
||||
lastKey: -1,
|
||||
lastNeedle: -1,
|
||||
lastIndex: -1,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This overly complicated beast is just to record the last tested line/column and the resulting
|
||||
* index, allowing us to skip a few tests if mappings are monotonically increasing.
|
||||
*/
|
||||
function memoizedBinarySearch(haystack, needle, state, key) {
|
||||
const { lastKey, lastNeedle, lastIndex } = state;
|
||||
let low = 0;
|
||||
let high = haystack.length - 1;
|
||||
if (key === lastKey) {
|
||||
if (needle === lastNeedle) {
|
||||
found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
|
||||
return lastIndex;
|
||||
}
|
||||
if (needle >= lastNeedle) {
|
||||
// lastIndex may be -1 if the previous needle was not found.
|
||||
low = lastIndex === -1 ? 0 : lastIndex;
|
||||
}
|
||||
else {
|
||||
high = lastIndex;
|
||||
}
|
||||
}
|
||||
state.lastKey = key;
|
||||
state.lastNeedle = needle;
|
||||
return (state.lastIndex = binarySearch(haystack, needle, low, high));
|
||||
}
|
||||
|
||||
const LINE_GTR_ZERO = '`line` must be greater than 0 (lines start at line 1)';
|
||||
const COL_GTR_EQ_ZERO = '`column` must be greater than or equal to 0 (columns start at column 0)';
|
||||
const LEAST_UPPER_BOUND = -1;
|
||||
const GREATEST_LOWER_BOUND = 1;
|
||||
/**
|
||||
* Returns the decoded (array of lines of segments) form of the SourceMap's mappings field.
|
||||
*/
|
||||
let decodedMappings;
|
||||
/**
|
||||
* A higher-level API to find the source/line/column associated with a generated line/column
|
||||
* (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in
|
||||
* `source-map` library.
|
||||
*/
|
||||
let originalPositionFor;
|
||||
class TraceMap {
|
||||
constructor(map, mapUrl) {
|
||||
const isString = typeof map === 'string';
|
||||
if (!isString && map._decodedMemo)
|
||||
return map;
|
||||
const parsed = (isString ? JSON.parse(map) : map);
|
||||
const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
|
||||
this.version = version;
|
||||
this.file = file;
|
||||
this.names = names || [];
|
||||
this.sourceRoot = sourceRoot;
|
||||
this.sources = sources;
|
||||
this.sourcesContent = sourcesContent;
|
||||
const from = resolve(sourceRoot || '', stripFilename(mapUrl));
|
||||
this.resolvedSources = sources.map((s) => resolve(s || '', from));
|
||||
const { mappings } = parsed;
|
||||
if (typeof mappings === 'string') {
|
||||
this._encoded = mappings;
|
||||
this._decoded = undefined;
|
||||
}
|
||||
else {
|
||||
this._encoded = undefined;
|
||||
this._decoded = maybeSort(mappings, isString);
|
||||
}
|
||||
this._decodedMemo = memoizedState();
|
||||
this._bySources = undefined;
|
||||
this._bySourceMemos = undefined;
|
||||
}
|
||||
}
|
||||
(() => {
|
||||
decodedMappings = (map) => {
|
||||
return (map._decoded || (map._decoded = decode(map._encoded)));
|
||||
};
|
||||
originalPositionFor = (map, { line, column, bias }) => {
|
||||
line--;
|
||||
if (line < 0)
|
||||
throw new Error(LINE_GTR_ZERO);
|
||||
if (column < 0)
|
||||
throw new Error(COL_GTR_EQ_ZERO);
|
||||
const decoded = decodedMappings(map);
|
||||
// It's common for parent source maps to have pointers to lines that have no
|
||||
// mapping (like a "//# sourceMappingURL=") at the end of the child file.
|
||||
if (line >= decoded.length)
|
||||
return OMapping(null, null, null, null);
|
||||
const segments = decoded[line];
|
||||
const index = traceSegmentInternal(segments, map._decodedMemo, line, column, bias || GREATEST_LOWER_BOUND);
|
||||
if (index === -1)
|
||||
return OMapping(null, null, null, null);
|
||||
const segment = segments[index];
|
||||
if (segment.length === 1)
|
||||
return OMapping(null, null, null, null);
|
||||
const { names, resolvedSources } = map;
|
||||
return OMapping(resolvedSources[segment[SOURCES_INDEX]], segment[SOURCE_LINE] + 1, segment[SOURCE_COLUMN], segment.length === 5 ? names[segment[NAMES_INDEX]] : null);
|
||||
};
|
||||
})();
|
||||
function OMapping(source, line, column, name) {
|
||||
return { source, line, column, name };
|
||||
}
|
||||
function traceSegmentInternal(segments, memo, line, column, bias) {
|
||||
let index = memoizedBinarySearch(segments, column, memo, line);
|
||||
if (found) {
|
||||
index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
|
||||
}
|
||||
else if (bias === LEAST_UPPER_BOUND)
|
||||
index++;
|
||||
if (index === -1 || index === segments.length)
|
||||
return -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
|
||||
const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
|
||||
const stackIgnorePatterns = [
|
||||
"node:internal",
|
||||
/\/packages\/\w+\/dist\//,
|
||||
/\/@vitest\/\w+\/dist\//,
|
||||
"/vitest/dist/",
|
||||
"/vitest/src/",
|
||||
"/vite-node/dist/",
|
||||
"/vite-node/src/",
|
||||
"/node_modules/chai/",
|
||||
"/node_modules/tinypool/",
|
||||
"/node_modules/tinyspy/",
|
||||
"/deps/chai.js",
|
||||
/__vitest_browser__/
|
||||
];
|
||||
function extractLocation(urlLike) {
|
||||
if (!urlLike.includes(":"))
|
||||
return [urlLike];
|
||||
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
|
||||
const parts = regExp.exec(urlLike.replace(/^\(|\)$/g, ""));
|
||||
if (!parts)
|
||||
return [urlLike];
|
||||
let url = parts[1];
|
||||
if (url.startsWith("http:") || url.startsWith("https:")) {
|
||||
const urlObj = new URL(url);
|
||||
url = urlObj.pathname;
|
||||
}
|
||||
if (url.startsWith("/@fs/")) {
|
||||
url = url.slice(typeof process !== "undefined" && process.platform === "win32" ? 5 : 4);
|
||||
}
|
||||
return [url, parts[2] || void 0, parts[3] || void 0];
|
||||
}
|
||||
function parseSingleFFOrSafariStack(raw) {
|
||||
let line = raw.trim();
|
||||
if (SAFARI_NATIVE_CODE_REGEXP.test(line))
|
||||
return null;
|
||||
if (line.includes(" > eval"))
|
||||
line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
|
||||
if (!line.includes("@") && !line.includes(":"))
|
||||
return null;
|
||||
const functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
|
||||
const matches = line.match(functionNameRegex);
|
||||
const functionName = matches && matches[1] ? matches[1] : void 0;
|
||||
const [url, lineNumber, columnNumber] = extractLocation(line.replace(functionNameRegex, ""));
|
||||
if (!url || !lineNumber || !columnNumber)
|
||||
return null;
|
||||
return {
|
||||
file: url,
|
||||
method: functionName || "",
|
||||
line: Number.parseInt(lineNumber),
|
||||
column: Number.parseInt(columnNumber)
|
||||
};
|
||||
}
|
||||
function parseSingleV8Stack(raw) {
|
||||
let line = raw.trim();
|
||||
if (!CHROME_IE_STACK_REGEXP.test(line))
|
||||
return null;
|
||||
if (line.includes("(eval "))
|
||||
line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
|
||||
let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
|
||||
const location = sanitizedLine.match(/ (\(.+\)$)/);
|
||||
sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
|
||||
const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
|
||||
let method = location && sanitizedLine || "";
|
||||
let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
|
||||
if (!file || !lineNumber || !columnNumber)
|
||||
return null;
|
||||
if (method.startsWith("async "))
|
||||
method = method.slice(6);
|
||||
if (file.startsWith("file://"))
|
||||
file = file.slice(7);
|
||||
file = resolve$2(file);
|
||||
if (method)
|
||||
method = method.replace(/__vite_ssr_import_\d+__\./g, "");
|
||||
return {
|
||||
method,
|
||||
file,
|
||||
line: Number.parseInt(lineNumber),
|
||||
column: Number.parseInt(columnNumber)
|
||||
};
|
||||
}
|
||||
function parseStacktrace(stack, options = {}) {
|
||||
const { ignoreStackEntries = stackIgnorePatterns } = options;
|
||||
let stacks = !CHROME_IE_STACK_REGEXP.test(stack) ? parseFFOrSafariStackTrace(stack) : parseV8Stacktrace(stack);
|
||||
if (ignoreStackEntries.length)
|
||||
stacks = stacks.filter((stack2) => !ignoreStackEntries.some((p) => stack2.file.match(p)));
|
||||
return stacks.map((stack2) => {
|
||||
var _a;
|
||||
const map = (_a = options.getSourceMap) == null ? void 0 : _a.call(options, stack2.file);
|
||||
if (!map || typeof map !== "object" || !map.version)
|
||||
return stack2;
|
||||
const traceMap = new TraceMap(map);
|
||||
const { line, column } = originalPositionFor(traceMap, stack2);
|
||||
if (line != null && column != null)
|
||||
return { ...stack2, line, column };
|
||||
return stack2;
|
||||
});
|
||||
}
|
||||
function parseFFOrSafariStackTrace(stack) {
|
||||
return stack.split("\n").map((line) => parseSingleFFOrSafariStack(line)).filter(notNullish);
|
||||
}
|
||||
function parseV8Stacktrace(stack) {
|
||||
return stack.split("\n").map((line) => parseSingleV8Stack(line)).filter(notNullish);
|
||||
}
|
||||
function parseErrorStacktrace(e, options = {}) {
|
||||
if (!e || isPrimitive(e))
|
||||
return [];
|
||||
if (e.stacks)
|
||||
return e.stacks;
|
||||
const stackStr = e.stack || e.stackStr || "";
|
||||
let stackFrames = parseStacktrace(stackStr, options);
|
||||
if (options.frameFilter)
|
||||
stackFrames = stackFrames.filter((f) => options.frameFilter(e, f) !== false);
|
||||
e.stacks = stackFrames;
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
async function saveInlineSnapshots(environment, snapshots) {
|
||||
const MagicString = (await import('magic-string')).default;
|
||||
const files = new Set(snapshots.map((i) => i.file));
|
||||
await Promise.all(Array.from(files).map(async (file) => {
|
||||
const snaps = snapshots.filter((i) => i.file === file);
|
||||
const code = await environment.readSnapshotFile(file);
|
||||
const s = new MagicString(code);
|
||||
for (const snap of snaps) {
|
||||
const index = positionToOffset(code, snap.line, snap.column);
|
||||
replaceInlineSnap(code, s, index, snap.snapshot);
|
||||
}
|
||||
const transformed = s.toString();
|
||||
if (transformed !== code)
|
||||
await environment.saveSnapshotFile(file, transformed);
|
||||
}));
|
||||
}
|
||||
const startObjectRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\S\s]*\*\/\s*|\/\/.*\s+)*\s*({)/m;
|
||||
function replaceObjectSnap(code, s, index, newSnap) {
|
||||
let _code = code.slice(index);
|
||||
const startMatch = startObjectRegex.exec(_code);
|
||||
if (!startMatch)
|
||||
return false;
|
||||
_code = _code.slice(startMatch.index);
|
||||
let callEnd = getCallLastIndex(_code);
|
||||
if (callEnd === null)
|
||||
return false;
|
||||
callEnd += index + startMatch.index;
|
||||
const shapeStart = index + startMatch.index + startMatch[0].length;
|
||||
const shapeEnd = getObjectShapeEndIndex(code, shapeStart);
|
||||
const snap = `, ${prepareSnapString(newSnap, code, index)}`;
|
||||
if (shapeEnd === callEnd) {
|
||||
s.appendLeft(callEnd, snap);
|
||||
} else {
|
||||
s.overwrite(shapeEnd, callEnd, snap);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function getObjectShapeEndIndex(code, index) {
|
||||
let startBraces = 1;
|
||||
let endBraces = 0;
|
||||
while (startBraces !== endBraces && index < code.length) {
|
||||
const s = code[index++];
|
||||
if (s === "{")
|
||||
startBraces++;
|
||||
else if (s === "}")
|
||||
endBraces++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function prepareSnapString(snap, source, index) {
|
||||
const lineNumber = offsetToLineNumber(source, index);
|
||||
const line = source.split(lineSplitRE)[lineNumber - 1];
|
||||
const indent = line.match(/^\s*/)[0] || "";
|
||||
const indentNext = indent.includes(" ") ? `${indent} ` : `${indent} `;
|
||||
const lines = snap.trim().replace(/\\/g, "\\\\").split(/\n/g);
|
||||
const isOneline = lines.length <= 1;
|
||||
const quote = "`";
|
||||
if (isOneline)
|
||||
return `${quote}${lines.join("\n").replace(/`/g, "\\`").replace(/\${/g, "\\${")}${quote}`;
|
||||
return `${quote}
|
||||
${lines.map((i) => i ? indentNext + i : "").join("\n").replace(/`/g, "\\`").replace(/\${/g, "\\${")}
|
||||
${indent}${quote}`;
|
||||
}
|
||||
const startRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\S\s]*\*\/\s*|\/\/.*\s+)*\s*[\w_$]*(['"`\)])/m;
|
||||
function replaceInlineSnap(code, s, index, newSnap) {
|
||||
const codeStartingAtIndex = code.slice(index);
|
||||
const startMatch = startRegex.exec(codeStartingAtIndex);
|
||||
const firstKeywordMatch = /toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot/.exec(codeStartingAtIndex);
|
||||
if (!startMatch || startMatch.index !== (firstKeywordMatch == null ? void 0 : firstKeywordMatch.index))
|
||||
return replaceObjectSnap(code, s, index, newSnap);
|
||||
const quote = startMatch[1];
|
||||
const startIndex = index + startMatch.index + startMatch[0].length;
|
||||
const snapString = prepareSnapString(newSnap, code, index);
|
||||
if (quote === ")") {
|
||||
s.appendRight(startIndex - 1, snapString);
|
||||
return true;
|
||||
}
|
||||
const quoteEndRE = new RegExp(`(?:^|[^\\\\])${quote}`);
|
||||
const endMatch = quoteEndRE.exec(code.slice(startIndex));
|
||||
if (!endMatch)
|
||||
return false;
|
||||
const endIndex = startIndex + endMatch.index + endMatch[0].length;
|
||||
s.overwrite(startIndex - 1, endIndex, snapString);
|
||||
return true;
|
||||
}
|
||||
const INDENTATION_REGEX = /^([^\S\n]*)\S/m;
|
||||
function stripSnapshotIndentation(inlineSnapshot) {
|
||||
const match = inlineSnapshot.match(INDENTATION_REGEX);
|
||||
if (!match || !match[1]) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
const indentation = match[1];
|
||||
const lines = inlineSnapshot.split(/\n/g);
|
||||
if (lines.length <= 2) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
if (lines[0].trim() !== "" || lines[lines.length - 1].trim() !== "") {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
for (let i = 1; i < lines.length - 1; i++) {
|
||||
if (lines[i] !== "") {
|
||||
if (lines[i].indexOf(indentation) !== 0) {
|
||||
return inlineSnapshot;
|
||||
}
|
||||
lines[i] = lines[i].substring(indentation.length);
|
||||
}
|
||||
}
|
||||
lines[lines.length - 1] = "";
|
||||
inlineSnapshot = lines.join("\n");
|
||||
return inlineSnapshot;
|
||||
}
|
||||
|
||||
async function saveRawSnapshots(environment, snapshots) {
|
||||
await Promise.all(snapshots.map(async (snap) => {
|
||||
if (!snap.readonly)
|
||||
await environment.saveSnapshotFile(snap.file, snap.snapshot);
|
||||
}));
|
||||
}
|
||||
|
||||
class SnapshotState {
|
||||
constructor(testFilePath, snapshotPath, snapshotContent, options) {
|
||||
this.testFilePath = testFilePath;
|
||||
this.snapshotPath = snapshotPath;
|
||||
const { data, dirty } = getSnapshotData(
|
||||
snapshotContent,
|
||||
options
|
||||
);
|
||||
this._fileExists = snapshotContent != null;
|
||||
this._initialData = data;
|
||||
this._snapshotData = data;
|
||||
this._dirty = dirty;
|
||||
this._inlineSnapshots = [];
|
||||
this._rawSnapshots = [];
|
||||
this._uncheckedKeys = new Set(Object.keys(this._snapshotData));
|
||||
this._counters = /* @__PURE__ */ new Map();
|
||||
this.expand = options.expand || false;
|
||||
this.added = 0;
|
||||
this.matched = 0;
|
||||
this.unmatched = 0;
|
||||
this._updateSnapshot = options.updateSnapshot;
|
||||
this.updated = 0;
|
||||
this._snapshotFormat = {
|
||||
printBasicPrototype: false,
|
||||
escapeString: false,
|
||||
...options.snapshotFormat
|
||||
};
|
||||
this._environment = options.snapshotEnvironment;
|
||||
}
|
||||
_counters;
|
||||
_dirty;
|
||||
_updateSnapshot;
|
||||
_snapshotData;
|
||||
_initialData;
|
||||
_inlineSnapshots;
|
||||
_rawSnapshots;
|
||||
_uncheckedKeys;
|
||||
_snapshotFormat;
|
||||
_environment;
|
||||
_fileExists;
|
||||
added;
|
||||
expand;
|
||||
matched;
|
||||
unmatched;
|
||||
updated;
|
||||
static async create(testFilePath, options) {
|
||||
const snapshotPath = await options.snapshotEnvironment.resolvePath(testFilePath);
|
||||
const content = await options.snapshotEnvironment.readSnapshotFile(snapshotPath);
|
||||
return new SnapshotState(testFilePath, snapshotPath, content, options);
|
||||
}
|
||||
get environment() {
|
||||
return this._environment;
|
||||
}
|
||||
markSnapshotsAsCheckedForTest(testName) {
|
||||
this._uncheckedKeys.forEach((uncheckedKey) => {
|
||||
if (keyToTestName(uncheckedKey) === testName)
|
||||
this._uncheckedKeys.delete(uncheckedKey);
|
||||
});
|
||||
}
|
||||
_inferInlineSnapshotStack(stacks) {
|
||||
const promiseIndex = stacks.findIndex((i) => i.method.match(/__VITEST_(RESOLVES|REJECTS)__/));
|
||||
if (promiseIndex !== -1)
|
||||
return stacks[promiseIndex + 3];
|
||||
const stackIndex = stacks.findIndex((i) => i.method.includes("__INLINE_SNAPSHOT__"));
|
||||
return stackIndex !== -1 ? stacks[stackIndex + 2] : null;
|
||||
}
|
||||
_addSnapshot(key, receivedSerialized, options) {
|
||||
this._dirty = true;
|
||||
if (options.isInline) {
|
||||
const stacks = parseErrorStacktrace(options.error || new Error("snapshot"), { ignoreStackEntries: [] });
|
||||
const stack = this._inferInlineSnapshotStack(stacks);
|
||||
if (!stack) {
|
||||
throw new Error(
|
||||
`@vitest/snapshot: Couldn't infer stack frame for inline snapshot.
|
||||
${JSON.stringify(stacks)}`
|
||||
);
|
||||
}
|
||||
stack.column--;
|
||||
this._inlineSnapshots.push({
|
||||
snapshot: receivedSerialized,
|
||||
...stack
|
||||
});
|
||||
} else if (options.rawSnapshot) {
|
||||
this._rawSnapshots.push({
|
||||
...options.rawSnapshot,
|
||||
snapshot: receivedSerialized
|
||||
});
|
||||
} else {
|
||||
this._snapshotData[key] = receivedSerialized;
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
this._snapshotData = this._initialData;
|
||||
this._counters = /* @__PURE__ */ new Map();
|
||||
this.added = 0;
|
||||
this.matched = 0;
|
||||
this.unmatched = 0;
|
||||
this.updated = 0;
|
||||
this._dirty = false;
|
||||
}
|
||||
async save() {
|
||||
const hasExternalSnapshots = Object.keys(this._snapshotData).length;
|
||||
const hasInlineSnapshots = this._inlineSnapshots.length;
|
||||
const hasRawSnapshots = this._rawSnapshots.length;
|
||||
const isEmpty = !hasExternalSnapshots && !hasInlineSnapshots && !hasRawSnapshots;
|
||||
const status = {
|
||||
deleted: false,
|
||||
saved: false
|
||||
};
|
||||
if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) {
|
||||
if (hasExternalSnapshots) {
|
||||
await saveSnapshotFile(this._environment, this._snapshotData, this.snapshotPath);
|
||||
this._fileExists = true;
|
||||
}
|
||||
if (hasInlineSnapshots)
|
||||
await saveInlineSnapshots(this._environment, this._inlineSnapshots);
|
||||
if (hasRawSnapshots)
|
||||
await saveRawSnapshots(this._environment, this._rawSnapshots);
|
||||
status.saved = true;
|
||||
} else if (!hasExternalSnapshots && this._fileExists) {
|
||||
if (this._updateSnapshot === "all") {
|
||||
await this._environment.removeSnapshotFile(this.snapshotPath);
|
||||
this._fileExists = false;
|
||||
}
|
||||
status.deleted = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
getUncheckedCount() {
|
||||
return this._uncheckedKeys.size || 0;
|
||||
}
|
||||
getUncheckedKeys() {
|
||||
return Array.from(this._uncheckedKeys);
|
||||
}
|
||||
removeUncheckedKeys() {
|
||||
if (this._updateSnapshot === "all" && this._uncheckedKeys.size) {
|
||||
this._dirty = true;
|
||||
this._uncheckedKeys.forEach((key) => delete this._snapshotData[key]);
|
||||
this._uncheckedKeys.clear();
|
||||
}
|
||||
}
|
||||
match({
|
||||
testName,
|
||||
received,
|
||||
key,
|
||||
inlineSnapshot,
|
||||
isInline,
|
||||
error,
|
||||
rawSnapshot
|
||||
}) {
|
||||
this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
|
||||
const count = Number(this._counters.get(testName));
|
||||
if (!key)
|
||||
key = testNameToKey(testName, count);
|
||||
if (!(isInline && this._snapshotData[key] !== void 0))
|
||||
this._uncheckedKeys.delete(key);
|
||||
let receivedSerialized = rawSnapshot && typeof received === "string" ? received : serialize(received, void 0, this._snapshotFormat);
|
||||
if (!rawSnapshot)
|
||||
receivedSerialized = addExtraLineBreaks(receivedSerialized);
|
||||
if (rawSnapshot) {
|
||||
if (rawSnapshot.content && rawSnapshot.content.match(/\r\n/) && !receivedSerialized.match(/\r\n/))
|
||||
rawSnapshot.content = normalizeNewlines(rawSnapshot.content);
|
||||
}
|
||||
const expected = isInline ? inlineSnapshot : rawSnapshot ? rawSnapshot.content : this._snapshotData[key];
|
||||
const expectedTrimmed = prepareExpected(expected);
|
||||
const pass = expectedTrimmed === prepareExpected(receivedSerialized);
|
||||
const hasSnapshot = expected !== void 0;
|
||||
const snapshotIsPersisted = isInline || this._fileExists || rawSnapshot && rawSnapshot.content != null;
|
||||
if (pass && !isInline && !rawSnapshot) {
|
||||
this._snapshotData[key] = receivedSerialized;
|
||||
}
|
||||
if (hasSnapshot && this._updateSnapshot === "all" || (!hasSnapshot || !snapshotIsPersisted) && (this._updateSnapshot === "new" || this._updateSnapshot === "all")) {
|
||||
if (this._updateSnapshot === "all") {
|
||||
if (!pass) {
|
||||
if (hasSnapshot)
|
||||
this.updated++;
|
||||
else
|
||||
this.added++;
|
||||
this._addSnapshot(key, receivedSerialized, { error, isInline, rawSnapshot });
|
||||
} else {
|
||||
this.matched++;
|
||||
}
|
||||
} else {
|
||||
this._addSnapshot(key, receivedSerialized, { error, isInline, rawSnapshot });
|
||||
this.added++;
|
||||
}
|
||||
return {
|
||||
actual: "",
|
||||
count,
|
||||
expected: "",
|
||||
key,
|
||||
pass: true
|
||||
};
|
||||
} else {
|
||||
if (!pass) {
|
||||
this.unmatched++;
|
||||
return {
|
||||
actual: removeExtraLineBreaks(receivedSerialized),
|
||||
count,
|
||||
expected: expectedTrimmed !== void 0 ? removeExtraLineBreaks(expectedTrimmed) : void 0,
|
||||
key,
|
||||
pass: false
|
||||
};
|
||||
} else {
|
||||
this.matched++;
|
||||
return {
|
||||
actual: "",
|
||||
count,
|
||||
expected: "",
|
||||
key,
|
||||
pass: true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
async pack() {
|
||||
const snapshot = {
|
||||
filepath: this.testFilePath,
|
||||
added: 0,
|
||||
fileDeleted: false,
|
||||
matched: 0,
|
||||
unchecked: 0,
|
||||
uncheckedKeys: [],
|
||||
unmatched: 0,
|
||||
updated: 0
|
||||
};
|
||||
const uncheckedCount = this.getUncheckedCount();
|
||||
const uncheckedKeys = this.getUncheckedKeys();
|
||||
if (uncheckedCount)
|
||||
this.removeUncheckedKeys();
|
||||
const status = await this.save();
|
||||
snapshot.fileDeleted = status.deleted;
|
||||
snapshot.added = this.added;
|
||||
snapshot.matched = this.matched;
|
||||
snapshot.unmatched = this.unmatched;
|
||||
snapshot.updated = this.updated;
|
||||
snapshot.unchecked = !status.deleted ? uncheckedCount : 0;
|
||||
snapshot.uncheckedKeys = Array.from(uncheckedKeys);
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
function createMismatchError(message, expand, actual, expected) {
|
||||
const error = new Error(message);
|
||||
Object.defineProperty(error, "actual", {
|
||||
value: actual,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
Object.defineProperty(error, "expected", {
|
||||
value: expected,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
Object.defineProperty(error, "diffOptions", { value: { expand } });
|
||||
return error;
|
||||
}
|
||||
class SnapshotClient {
|
||||
constructor(options = {}) {
|
||||
this.options = options;
|
||||
}
|
||||
filepath;
|
||||
name;
|
||||
snapshotState;
|
||||
snapshotStateMap = /* @__PURE__ */ new Map();
|
||||
async startCurrentRun(filepath, name, options) {
|
||||
var _a;
|
||||
this.filepath = filepath;
|
||||
this.name = name;
|
||||
if (((_a = this.snapshotState) == null ? void 0 : _a.testFilePath) !== filepath) {
|
||||
await this.finishCurrentRun();
|
||||
if (!this.getSnapshotState(filepath)) {
|
||||
this.snapshotStateMap.set(
|
||||
filepath,
|
||||
await SnapshotState.create(
|
||||
filepath,
|
||||
options
|
||||
)
|
||||
);
|
||||
}
|
||||
this.snapshotState = this.getSnapshotState(filepath);
|
||||
}
|
||||
}
|
||||
getSnapshotState(filepath) {
|
||||
return this.snapshotStateMap.get(filepath);
|
||||
}
|
||||
clearTest() {
|
||||
this.filepath = void 0;
|
||||
this.name = void 0;
|
||||
}
|
||||
skipTestSnapshots(name) {
|
||||
var _a;
|
||||
(_a = this.snapshotState) == null ? void 0 : _a.markSnapshotsAsCheckedForTest(name);
|
||||
}
|
||||
assert(options) {
|
||||
var _a, _b, _c, _d;
|
||||
const {
|
||||
filepath = this.filepath,
|
||||
name = this.name,
|
||||
message,
|
||||
isInline = false,
|
||||
properties,
|
||||
inlineSnapshot,
|
||||
error,
|
||||
errorMessage,
|
||||
rawSnapshot
|
||||
} = options;
|
||||
let { received } = options;
|
||||
if (!filepath)
|
||||
throw new Error("Snapshot cannot be used outside of test");
|
||||
if (typeof properties === "object") {
|
||||
if (typeof received !== "object" || !received)
|
||||
throw new Error("Received value must be an object when the matcher has properties");
|
||||
try {
|
||||
const pass2 = ((_b = (_a = this.options).isEqual) == null ? void 0 : _b.call(_a, received, properties)) ?? false;
|
||||
if (!pass2)
|
||||
throw createMismatchError("Snapshot properties mismatched", (_c = this.snapshotState) == null ? void 0 : _c.expand, received, properties);
|
||||
else
|
||||
received = deepMergeSnapshot(received, properties);
|
||||
} catch (err) {
|
||||
err.message = errorMessage || "Snapshot mismatched";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const testName = [
|
||||
name,
|
||||
...message ? [message] : []
|
||||
].join(" > ");
|
||||
const snapshotState = this.getSnapshotState(filepath);
|
||||
const { actual, expected, key, pass } = snapshotState.match({
|
||||
testName,
|
||||
received,
|
||||
isInline,
|
||||
error,
|
||||
inlineSnapshot,
|
||||
rawSnapshot
|
||||
});
|
||||
if (!pass)
|
||||
throw createMismatchError(`Snapshot \`${key || "unknown"}\` mismatched`, (_d = this.snapshotState) == null ? void 0 : _d.expand, actual == null ? void 0 : actual.trim(), expected == null ? void 0 : expected.trim());
|
||||
}
|
||||
async assertRaw(options) {
|
||||
if (!options.rawSnapshot)
|
||||
throw new Error("Raw snapshot is required");
|
||||
const {
|
||||
filepath = this.filepath,
|
||||
rawSnapshot
|
||||
} = options;
|
||||
if (rawSnapshot.content == null) {
|
||||
if (!filepath)
|
||||
throw new Error("Snapshot cannot be used outside of test");
|
||||
const snapshotState = this.getSnapshotState(filepath);
|
||||
options.filepath || (options.filepath = filepath);
|
||||
rawSnapshot.file = await snapshotState.environment.resolveRawPath(filepath, rawSnapshot.file);
|
||||
rawSnapshot.content = await snapshotState.environment.readSnapshotFile(rawSnapshot.file) || void 0;
|
||||
}
|
||||
return this.assert(options);
|
||||
}
|
||||
async finishCurrentRun() {
|
||||
if (!this.snapshotState)
|
||||
return null;
|
||||
const result = await this.snapshotState.pack();
|
||||
this.snapshotState = void 0;
|
||||
return result;
|
||||
}
|
||||
clear() {
|
||||
this.snapshotStateMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export { SnapshotClient, SnapshotState, addSerializer, getSerializers, stripSnapshotIndentation };
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { S as SnapshotStateOptions, f as SnapshotSummary, b as SnapshotResult } from './index-S94ASl6q.js';
|
||||
import 'pretty-format';
|
||||
import './environment-cMiGIVXz.js';
|
||||
|
||||
declare class SnapshotManager {
|
||||
options: Omit<SnapshotStateOptions, 'snapshotEnvironment'>;
|
||||
summary: SnapshotSummary;
|
||||
extension: string;
|
||||
constructor(options: Omit<SnapshotStateOptions, 'snapshotEnvironment'>);
|
||||
clear(): void;
|
||||
add(result: SnapshotResult): void;
|
||||
resolvePath(testPath: string): string;
|
||||
resolveRawPath(testPath: string, rawPath: string): string;
|
||||
}
|
||||
declare function emptySummary(options: Omit<SnapshotStateOptions, 'snapshotEnvironment'>): SnapshotSummary;
|
||||
declare function addSnapshotResult(summary: SnapshotSummary, result: SnapshotResult): void;
|
||||
|
||||
export { SnapshotManager, addSnapshotResult, emptySummary };
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { join, dirname, basename, isAbsolute, resolve } from 'pathe';
|
||||
|
||||
class SnapshotManager {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.clear();
|
||||
}
|
||||
summary = void 0;
|
||||
extension = ".snap";
|
||||
clear() {
|
||||
this.summary = emptySummary(this.options);
|
||||
}
|
||||
add(result) {
|
||||
addSnapshotResult(this.summary, result);
|
||||
}
|
||||
resolvePath(testPath) {
|
||||
const resolver = this.options.resolveSnapshotPath || (() => {
|
||||
return join(
|
||||
join(
|
||||
dirname(testPath),
|
||||
"__snapshots__"
|
||||
),
|
||||
`${basename(testPath)}${this.extension}`
|
||||
);
|
||||
});
|
||||
const path = resolver(testPath, this.extension);
|
||||
return path;
|
||||
}
|
||||
resolveRawPath(testPath, rawPath) {
|
||||
return isAbsolute(rawPath) ? rawPath : resolve(dirname(testPath), rawPath);
|
||||
}
|
||||
}
|
||||
function emptySummary(options) {
|
||||
const summary = {
|
||||
added: 0,
|
||||
failure: false,
|
||||
filesAdded: 0,
|
||||
filesRemoved: 0,
|
||||
filesRemovedList: [],
|
||||
filesUnmatched: 0,
|
||||
filesUpdated: 0,
|
||||
matched: 0,
|
||||
total: 0,
|
||||
unchecked: 0,
|
||||
uncheckedKeysByFile: [],
|
||||
unmatched: 0,
|
||||
updated: 0,
|
||||
didUpdate: options.updateSnapshot === "all"
|
||||
};
|
||||
return summary;
|
||||
}
|
||||
function addSnapshotResult(summary, result) {
|
||||
if (result.added)
|
||||
summary.filesAdded++;
|
||||
if (result.fileDeleted)
|
||||
summary.filesRemoved++;
|
||||
if (result.unmatched)
|
||||
summary.filesUnmatched++;
|
||||
if (result.updated)
|
||||
summary.filesUpdated++;
|
||||
summary.added += result.added;
|
||||
summary.matched += result.matched;
|
||||
summary.unchecked += result.unchecked;
|
||||
if (result.uncheckedKeys && result.uncheckedKeys.length > 0) {
|
||||
summary.uncheckedKeysByFile.push({
|
||||
filePath: result.filepath,
|
||||
keys: result.uncheckedKeys
|
||||
});
|
||||
}
|
||||
summary.unmatched += result.unmatched;
|
||||
summary.updated += result.updated;
|
||||
summary.total += result.added + result.matched + result.unmatched + result.updated;
|
||||
}
|
||||
|
||||
export { SnapshotManager, addSnapshotResult, emptySummary };
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/environment.js'
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/manager.js'
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@vitest/snapshot",
|
||||
"type": "module",
|
||||
"version": "1.6.1",
|
||||
"description": "Vitest snapshot manager",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/snapshot#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/snapshot"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./environment": {
|
||||
"types": "./dist/environment.d.ts",
|
||||
"default": "./dist/environment.js"
|
||||
},
|
||||
"./manager": {
|
||||
"types": "./dist/manager.d.ts",
|
||||
"default": "./dist/manager.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"magic-string": "^0.30.5",
|
||||
"pathe": "^1.1.1",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/natural-compare": "^1.4.3",
|
||||
"natural-compare": "^1.4.0",
|
||||
"@vitest/utils": "1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c",
|
||||
"dev": "rollup -c --watch"
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-Present Vitest Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# @vitest/spy
|
||||
|
||||
Lightweight Jest compatible spy implementation.
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
interface MockResultReturn<T> {
|
||||
type: 'return';
|
||||
/**
|
||||
* The value that was returned from the function. If function returned a Promise, then this will be a resolved value.
|
||||
*/
|
||||
value: T;
|
||||
}
|
||||
interface MockResultIncomplete {
|
||||
type: 'incomplete';
|
||||
value: undefined;
|
||||
}
|
||||
interface MockResultThrow {
|
||||
type: 'throw';
|
||||
/**
|
||||
* An error that was thrown during function execution.
|
||||
*/
|
||||
value: any;
|
||||
}
|
||||
type MockResult<T> = MockResultReturn<T> | MockResultThrow | MockResultIncomplete;
|
||||
interface MockContext<TArgs, TReturns> {
|
||||
/**
|
||||
* This is an array containing all arguments for each call. One item of the array is the arguments of that call.
|
||||
*
|
||||
* @example
|
||||
* const fn = vi.fn()
|
||||
*
|
||||
* fn('arg1', 'arg2')
|
||||
* fn('arg3')
|
||||
*
|
||||
* fn.mock.calls === [
|
||||
* ['arg1', 'arg2'], // first call
|
||||
* ['arg3'], // second call
|
||||
* ]
|
||||
*/
|
||||
calls: TArgs[];
|
||||
/**
|
||||
* This is an array containing all instances that were instantiated when mock was called with a `new` keyword. Note that this is an actual context (`this`) of the function, not a return value.
|
||||
*/
|
||||
instances: TReturns[];
|
||||
/**
|
||||
* The order of mock's execution. This returns an array of numbers which are shared between all defined mocks.
|
||||
*
|
||||
* @example
|
||||
* const fn1 = vi.fn()
|
||||
* const fn2 = vi.fn()
|
||||
*
|
||||
* fn1()
|
||||
* fn2()
|
||||
* fn1()
|
||||
*
|
||||
* fn1.mock.invocationCallOrder === [1, 3]
|
||||
* fn2.mock.invocationCallOrder === [2]
|
||||
*/
|
||||
invocationCallOrder: number[];
|
||||
/**
|
||||
* This is an array containing all values that were `returned` from the function.
|
||||
*
|
||||
* The `value` property contains the returned value or thrown error. If the function returned a promise, the `value` will be the _resolved_ value, not the actual `Promise`, unless it was never resolved.
|
||||
*
|
||||
* @example
|
||||
* const fn = vi.fn()
|
||||
* .mockReturnValueOnce('result')
|
||||
* .mockImplementationOnce(() => { throw new Error('thrown error') })
|
||||
*
|
||||
* const result = fn()
|
||||
*
|
||||
* try {
|
||||
* fn()
|
||||
* }
|
||||
* catch {}
|
||||
*
|
||||
* fn.mock.results === [
|
||||
* {
|
||||
* type: 'return',
|
||||
* value: 'result',
|
||||
* },
|
||||
* {
|
||||
* type: 'throw',
|
||||
* value: Error,
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
results: MockResult<TReturns>[];
|
||||
/**
|
||||
* This contains the arguments of the last call. If spy wasn't called, will return `undefined`.
|
||||
*/
|
||||
lastCall: TArgs | undefined;
|
||||
}
|
||||
type Procedure = (...args: any[]) => any;
|
||||
type Methods<T> = keyof {
|
||||
[K in keyof T as T[K] extends Procedure ? K : never]: T[K];
|
||||
};
|
||||
type Properties<T> = {
|
||||
[K in keyof T]: T[K] extends Procedure ? never : K;
|
||||
}[keyof T] & (string | symbol);
|
||||
type Classes<T> = {
|
||||
[K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never;
|
||||
}[keyof T] & (string | symbol);
|
||||
/**
|
||||
* @deprecated Use MockInstance<A, R> instead
|
||||
*/
|
||||
interface SpyInstance<TArgs extends any[] = any[], TReturns = any> extends MockInstance<TArgs, TReturns> {
|
||||
}
|
||||
interface MockInstance<TArgs extends any[] = any[], TReturns = any> {
|
||||
/**
|
||||
* Use it to return the name given to mock with method `.mockName(name)`.
|
||||
*/
|
||||
getMockName: () => string;
|
||||
/**
|
||||
* Sets internal mock name. Useful to see the name of the mock if an assertion fails.
|
||||
*/
|
||||
mockName: (n: string) => this;
|
||||
/**
|
||||
* Current context of the mock. It stores information about all invocation calls, instances, and results.
|
||||
*/
|
||||
mock: MockContext<TArgs, TReturns>;
|
||||
/**
|
||||
* Clears all information about every call. After calling it, all properties on `.mock` will return an empty state. This method does not reset implementations.
|
||||
*
|
||||
* It is useful if you need to clean up mock between different assertions.
|
||||
*/
|
||||
mockClear: () => this;
|
||||
/**
|
||||
* Does what `mockClear` does and makes inner implementation an empty function (returning `undefined` when invoked). This also resets all "once" implementations.
|
||||
*
|
||||
* This is useful when you want to completely reset a mock to the default state.
|
||||
*/
|
||||
mockReset: () => this;
|
||||
/**
|
||||
* Does what `mockReset` does and restores inner implementation to the original function.
|
||||
*
|
||||
* Note that restoring mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. Restoring a `vi.fn(impl)` will restore implementation to `impl`.
|
||||
*/
|
||||
mockRestore: () => void;
|
||||
/**
|
||||
* Returns current mock implementation if there is one.
|
||||
*
|
||||
* If mock was created with `vi.fn`, it will consider passed down method as a mock implementation.
|
||||
*
|
||||
* If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided.
|
||||
*/
|
||||
getMockImplementation: () => ((...args: TArgs) => TReturns) | undefined;
|
||||
/**
|
||||
* Accepts a function that will be used as an implementation of the mock.
|
||||
* @example
|
||||
* const increment = vi.fn().mockImplementation(count => count + 1);
|
||||
* expect(increment(3)).toBe(4);
|
||||
*/
|
||||
mockImplementation: (fn: ((...args: TArgs) => TReturns)) => this;
|
||||
/**
|
||||
* Accepts a function that will be used as a mock implementation during the next call. Can be chained so that multiple function calls produce different results.
|
||||
* @example
|
||||
* const fn = vi.fn(count => count).mockImplementationOnce(count => count + 1);
|
||||
* expect(fn(3)).toBe(4);
|
||||
* expect(fn(3)).toBe(3);
|
||||
*/
|
||||
mockImplementationOnce: (fn: ((...args: TArgs) => TReturns)) => this;
|
||||
/**
|
||||
* Overrides the original mock implementation temporarily while the callback is being executed.
|
||||
* @example
|
||||
* const myMockFn = vi.fn(() => 'original')
|
||||
*
|
||||
* myMockFn.withImplementation(() => 'temp', () => {
|
||||
* myMockFn() // 'temp'
|
||||
* })
|
||||
*
|
||||
* myMockFn() // 'original'
|
||||
*/
|
||||
withImplementation: <T>(fn: ((...args: TArgs) => TReturns), cb: () => T) => T extends Promise<unknown> ? Promise<this> : this;
|
||||
/**
|
||||
* Use this if you need to return `this` context from the method without invoking actual implementation.
|
||||
*/
|
||||
mockReturnThis: () => this;
|
||||
/**
|
||||
* Accepts a value that will be returned whenever the mock function is called.
|
||||
*/
|
||||
mockReturnValue: (obj: TReturns) => this;
|
||||
/**
|
||||
* Accepts a value that will be returned during the next function call. If chained, every consecutive call will return the specified value.
|
||||
*
|
||||
* When there are no more `mockReturnValueOnce` values to use, mock will fallback to the previously defined implementation if there is one.
|
||||
* @example
|
||||
* const myMockFn = vi
|
||||
* .fn()
|
||||
* .mockReturnValue('default')
|
||||
* .mockReturnValueOnce('first call')
|
||||
* .mockReturnValueOnce('second call')
|
||||
*
|
||||
* // 'first call', 'second call', 'default'
|
||||
* console.log(myMockFn(), myMockFn(), myMockFn())
|
||||
*/
|
||||
mockReturnValueOnce: (obj: TReturns) => this;
|
||||
/**
|
||||
* Accepts a value that will be resolved when async function is called.
|
||||
* @example
|
||||
* const asyncMock = vi.fn().mockResolvedValue(42)
|
||||
* asyncMock() // Promise<42>
|
||||
*/
|
||||
mockResolvedValue: (obj: Awaited<TReturns>) => this;
|
||||
/**
|
||||
* Accepts a value that will be resolved during the next function call. If chained, every consecutive call will resolve specified value.
|
||||
* @example
|
||||
* const myMockFn = vi
|
||||
* .fn()
|
||||
* .mockResolvedValue('default')
|
||||
* .mockResolvedValueOnce('first call')
|
||||
* .mockResolvedValueOnce('second call')
|
||||
*
|
||||
* // Promise<'first call'>, Promise<'second call'>, Promise<'default'>
|
||||
* console.log(myMockFn(), myMockFn(), myMockFn())
|
||||
*/
|
||||
mockResolvedValueOnce: (obj: Awaited<TReturns>) => this;
|
||||
/**
|
||||
* Accepts an error that will be rejected when async function is called.
|
||||
* @example
|
||||
* const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))
|
||||
* await asyncMock() // throws 'Async error'
|
||||
*/
|
||||
mockRejectedValue: (obj: any) => this;
|
||||
/**
|
||||
* Accepts a value that will be rejected during the next function call. If chained, every consecutive call will reject specified value.
|
||||
* @example
|
||||
* const asyncMock = vi
|
||||
* .fn()
|
||||
* .mockResolvedValueOnce('first call')
|
||||
* .mockRejectedValueOnce(new Error('Async error'))
|
||||
*
|
||||
* await asyncMock() // first call
|
||||
* await asyncMock() // throws "Async error"
|
||||
*/
|
||||
mockRejectedValueOnce: (obj: any) => this;
|
||||
}
|
||||
interface Mock<TArgs extends any[] = any, TReturns = any> extends MockInstance<TArgs, TReturns> {
|
||||
new (...args: TArgs): TReturns;
|
||||
(...args: TArgs): TReturns;
|
||||
}
|
||||
interface PartialMock<TArgs extends any[] = any, TReturns = any> extends MockInstance<TArgs, TReturns extends Promise<Awaited<TReturns>> ? Promise<Partial<Awaited<TReturns>>> : Partial<TReturns>> {
|
||||
new (...args: TArgs): TReturns;
|
||||
(...args: TArgs): TReturns;
|
||||
}
|
||||
type MaybeMockedConstructor<T> = T extends new (...args: Array<any>) => infer R ? Mock<ConstructorParameters<T>, R> : T;
|
||||
type MockedFunction<T extends Procedure> = Mock<Parameters<T>, ReturnType<T>> & {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
type PartiallyMockedFunction<T extends Procedure> = PartialMock<Parameters<T>, ReturnType<T>> & {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
type MockedFunctionDeep<T extends Procedure> = Mock<Parameters<T>, ReturnType<T>> & MockedObjectDeep<T>;
|
||||
type PartiallyMockedFunctionDeep<T extends Procedure> = PartialMock<Parameters<T>, ReturnType<T>> & MockedObjectDeep<T>;
|
||||
type MockedObject<T> = MaybeMockedConstructor<T> & {
|
||||
[K in Methods<T>]: T[K] extends Procedure ? MockedFunction<T[K]> : T[K];
|
||||
} & {
|
||||
[K in Properties<T>]: T[K];
|
||||
};
|
||||
type MockedObjectDeep<T> = MaybeMockedConstructor<T> & {
|
||||
[K in Methods<T>]: T[K] extends Procedure ? MockedFunctionDeep<T[K]> : T[K];
|
||||
} & {
|
||||
[K in Properties<T>]: MaybeMockedDeep<T[K]>;
|
||||
};
|
||||
type MaybeMockedDeep<T> = T extends Procedure ? MockedFunctionDeep<T> : T extends object ? MockedObjectDeep<T> : T;
|
||||
type MaybePartiallyMockedDeep<T> = T extends Procedure ? PartiallyMockedFunctionDeep<T> : T extends object ? MockedObjectDeep<T> : T;
|
||||
type MaybeMocked<T> = T extends Procedure ? MockedFunction<T> : T extends object ? MockedObject<T> : T;
|
||||
type MaybePartiallyMocked<T> = T extends Procedure ? PartiallyMockedFunction<T> : T extends object ? MockedObject<T> : T;
|
||||
interface Constructable {
|
||||
new (...args: any[]): any;
|
||||
}
|
||||
type MockedClass<T extends Constructable> = MockInstance<T extends new (...args: infer P) => any ? P : never, InstanceType<T>> & {
|
||||
prototype: T extends {
|
||||
prototype: any;
|
||||
} ? Mocked<T['prototype']> : never;
|
||||
} & T;
|
||||
type Mocked<T> = {
|
||||
[P in keyof T]: T[P] extends (...args: infer Args) => infer Returns ? MockInstance<Args, Returns> : T[P] extends Constructable ? MockedClass<T[P]> : T[P];
|
||||
} & T;
|
||||
declare const mocks: Set<MockInstance<any[], any>>;
|
||||
declare function isMockFunction(fn: any): fn is MockInstance;
|
||||
declare function spyOn<T, S extends Properties<Required<T>>>(obj: T, methodName: S, accessType: 'get'): MockInstance<[], T[S]>;
|
||||
declare function spyOn<T, G extends Properties<Required<T>>>(obj: T, methodName: G, accessType: 'set'): MockInstance<[T[G]], void>;
|
||||
declare function spyOn<T, M extends (Classes<Required<T>> | Methods<Required<T>>)>(obj: T, methodName: M): Required<T>[M] extends ({
|
||||
new (...args: infer A): infer R;
|
||||
}) | ((...args: infer A) => infer R) ? MockInstance<A, R> : never;
|
||||
declare function fn<TArgs extends any[] = any, R = any>(): Mock<TArgs, R>;
|
||||
declare function fn<TArgs extends any[] = any[], R = any>(implementation: (...args: TArgs) => R): Mock<TArgs, R>;
|
||||
|
||||
export { type MaybeMocked, type MaybeMockedConstructor, type MaybeMockedDeep, type MaybePartiallyMocked, type MaybePartiallyMockedDeep, type Mock, type MockContext, type MockInstance, type Mocked, type MockedClass, type MockedFunction, type MockedFunctionDeep, type MockedObject, type MockedObjectDeep, type PartialMock, type PartiallyMockedFunction, type PartiallyMockedFunctionDeep, type SpyInstance, fn, isMockFunction, mocks, spyOn };
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import * as tinyspy from 'tinyspy';
|
||||
|
||||
const mocks = /* @__PURE__ */ new Set();
|
||||
function isMockFunction(fn2) {
|
||||
return typeof fn2 === "function" && "_isMockFunction" in fn2 && fn2._isMockFunction;
|
||||
}
|
||||
function spyOn(obj, method, accessType) {
|
||||
const dictionary = {
|
||||
get: "getter",
|
||||
set: "setter"
|
||||
};
|
||||
const objMethod = accessType ? { [dictionary[accessType]]: method } : method;
|
||||
const stub = tinyspy.internalSpyOn(obj, objMethod);
|
||||
return enhanceSpy(stub);
|
||||
}
|
||||
let callOrder = 0;
|
||||
function enhanceSpy(spy) {
|
||||
const stub = spy;
|
||||
let implementation;
|
||||
let instances = [];
|
||||
let invocations = [];
|
||||
const state = tinyspy.getInternalState(spy);
|
||||
const mockContext = {
|
||||
get calls() {
|
||||
return state.calls;
|
||||
},
|
||||
get instances() {
|
||||
return instances;
|
||||
},
|
||||
get invocationCallOrder() {
|
||||
return invocations;
|
||||
},
|
||||
get results() {
|
||||
return state.results.map(([callType, value]) => {
|
||||
const type = callType === "error" ? "throw" : "return";
|
||||
return { type, value };
|
||||
});
|
||||
},
|
||||
get lastCall() {
|
||||
return state.calls[state.calls.length - 1];
|
||||
}
|
||||
};
|
||||
let onceImplementations = [];
|
||||
let implementationChangedTemporarily = false;
|
||||
function mockCall(...args) {
|
||||
instances.push(this);
|
||||
invocations.push(++callOrder);
|
||||
const impl = implementationChangedTemporarily ? implementation : onceImplementations.shift() || implementation || state.getOriginal() || (() => {
|
||||
});
|
||||
return impl.apply(this, args);
|
||||
}
|
||||
let name = stub.name;
|
||||
stub.getMockName = () => name || "vi.fn()";
|
||||
stub.mockName = (n) => {
|
||||
name = n;
|
||||
return stub;
|
||||
};
|
||||
stub.mockClear = () => {
|
||||
state.reset();
|
||||
instances = [];
|
||||
invocations = [];
|
||||
return stub;
|
||||
};
|
||||
stub.mockReset = () => {
|
||||
stub.mockClear();
|
||||
implementation = () => void 0;
|
||||
onceImplementations = [];
|
||||
return stub;
|
||||
};
|
||||
stub.mockRestore = () => {
|
||||
stub.mockReset();
|
||||
state.restore();
|
||||
implementation = void 0;
|
||||
return stub;
|
||||
};
|
||||
stub.getMockImplementation = () => implementation;
|
||||
stub.mockImplementation = (fn2) => {
|
||||
implementation = fn2;
|
||||
state.willCall(mockCall);
|
||||
return stub;
|
||||
};
|
||||
stub.mockImplementationOnce = (fn2) => {
|
||||
onceImplementations.push(fn2);
|
||||
return stub;
|
||||
};
|
||||
function withImplementation(fn2, cb) {
|
||||
const originalImplementation = implementation;
|
||||
implementation = fn2;
|
||||
state.willCall(mockCall);
|
||||
implementationChangedTemporarily = true;
|
||||
const reset = () => {
|
||||
implementation = originalImplementation;
|
||||
implementationChangedTemporarily = false;
|
||||
};
|
||||
const result = cb();
|
||||
if (result instanceof Promise) {
|
||||
return result.then(() => {
|
||||
reset();
|
||||
return stub;
|
||||
});
|
||||
}
|
||||
reset();
|
||||
return stub;
|
||||
}
|
||||
stub.withImplementation = withImplementation;
|
||||
stub.mockReturnThis = () => stub.mockImplementation(function() {
|
||||
return this;
|
||||
});
|
||||
stub.mockReturnValue = (val) => stub.mockImplementation(() => val);
|
||||
stub.mockReturnValueOnce = (val) => stub.mockImplementationOnce(() => val);
|
||||
stub.mockResolvedValue = (val) => stub.mockImplementation(() => Promise.resolve(val));
|
||||
stub.mockResolvedValueOnce = (val) => stub.mockImplementationOnce(() => Promise.resolve(val));
|
||||
stub.mockRejectedValue = (val) => stub.mockImplementation(() => Promise.reject(val));
|
||||
stub.mockRejectedValueOnce = (val) => stub.mockImplementationOnce(() => Promise.reject(val));
|
||||
Object.defineProperty(stub, "mock", {
|
||||
get: () => mockContext
|
||||
});
|
||||
state.willCall(mockCall);
|
||||
mocks.add(stub);
|
||||
return stub;
|
||||
}
|
||||
function fn(implementation) {
|
||||
const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {
|
||||
}) }, "spy"));
|
||||
if (implementation)
|
||||
enhancedSpy.mockImplementation(implementation);
|
||||
return enhancedSpy;
|
||||
}
|
||||
|
||||
export { fn, isMockFunction, mocks, spyOn };
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@vitest/spy",
|
||||
"type": "module",
|
||||
"version": "1.6.1",
|
||||
"description": "Lightweight Jest compatible spy implementation",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/spy#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/spy"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"tinyspy": "^2.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c",
|
||||
"dev": "rollup -c --watch"
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-Present Vitest Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/diff.js'
|
||||
+723
@@ -0,0 +1,723 @@
|
||||
// This definition file follows a somewhat unusual format. ESTree allows
|
||||
// runtime type checks based on the `type` parameter. In order to explain this
|
||||
// to typescript we want to use discriminated union types:
|
||||
// https://github.com/Microsoft/TypeScript/pull/9163
|
||||
//
|
||||
// For ESTree this is a bit tricky because the high level interfaces like
|
||||
// Node or Function are pulling double duty. We want to pass common fields down
|
||||
// to the interfaces that extend them (like Identifier or
|
||||
// ArrowFunctionExpression), but you can't extend a type union or enforce
|
||||
// common fields on them. So we've split the high level interfaces into two
|
||||
// types, a base type which passes down inherited fields, and a type union of
|
||||
// all types which extend the base type. Only the type union is exported, and
|
||||
// the union is how other types refer to the collection of inheriting types.
|
||||
//
|
||||
// This makes the definitions file here somewhat more difficult to maintain,
|
||||
// but it has the notable advantage of making ESTree much easier to use as
|
||||
// an end user.
|
||||
|
||||
interface BaseNodeWithoutComments {
|
||||
// Every leaf interface that extends BaseNode must specify a type property.
|
||||
// The type property should be a string literal. For example, Identifier
|
||||
// has: `type: "Identifier"`
|
||||
type: string;
|
||||
loc?: SourceLocation | null | undefined;
|
||||
range?: [number, number] | undefined;
|
||||
}
|
||||
|
||||
interface BaseNode extends BaseNodeWithoutComments {
|
||||
leadingComments?: Comment[] | undefined;
|
||||
trailingComments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
interface NodeMap {
|
||||
AssignmentProperty: AssignmentProperty;
|
||||
CatchClause: CatchClause;
|
||||
Class: Class;
|
||||
ClassBody: ClassBody;
|
||||
Expression: Expression;
|
||||
Function: Function;
|
||||
Identifier: Identifier;
|
||||
Literal: Literal;
|
||||
MethodDefinition: MethodDefinition;
|
||||
ModuleDeclaration: ModuleDeclaration;
|
||||
ModuleSpecifier: ModuleSpecifier;
|
||||
Pattern: Pattern;
|
||||
PrivateIdentifier: PrivateIdentifier;
|
||||
Program: Program;
|
||||
Property: Property;
|
||||
PropertyDefinition: PropertyDefinition;
|
||||
SpreadElement: SpreadElement;
|
||||
Statement: Statement;
|
||||
Super: Super;
|
||||
SwitchCase: SwitchCase;
|
||||
TemplateElement: TemplateElement;
|
||||
VariableDeclarator: VariableDeclarator;
|
||||
}
|
||||
|
||||
type Node$1 = NodeMap[keyof NodeMap];
|
||||
|
||||
interface Comment extends BaseNodeWithoutComments {
|
||||
type: "Line" | "Block";
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface SourceLocation {
|
||||
source?: string | null | undefined;
|
||||
start: Position;
|
||||
end: Position;
|
||||
}
|
||||
|
||||
interface Position {
|
||||
/** >= 1 */
|
||||
line: number;
|
||||
/** >= 0 */
|
||||
column: number;
|
||||
}
|
||||
|
||||
interface Program extends BaseNode {
|
||||
type: "Program";
|
||||
sourceType: "script" | "module";
|
||||
body: Array<Directive | Statement | ModuleDeclaration>;
|
||||
comments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
interface Directive extends BaseNode {
|
||||
type: "ExpressionStatement";
|
||||
expression: Literal;
|
||||
directive: string;
|
||||
}
|
||||
|
||||
interface BaseFunction extends BaseNode {
|
||||
params: Pattern[];
|
||||
generator?: boolean | undefined;
|
||||
async?: boolean | undefined;
|
||||
// The body is either BlockStatement or Expression because arrow functions
|
||||
// can have a body that's either. FunctionDeclarations and
|
||||
// FunctionExpressions have only BlockStatement bodies.
|
||||
body: BlockStatement | Expression;
|
||||
}
|
||||
|
||||
type Function = FunctionDeclaration | FunctionExpression | ArrowFunctionExpression;
|
||||
|
||||
type Statement =
|
||||
| ExpressionStatement
|
||||
| BlockStatement
|
||||
| StaticBlock
|
||||
| EmptyStatement
|
||||
| DebuggerStatement
|
||||
| WithStatement
|
||||
| ReturnStatement
|
||||
| LabeledStatement
|
||||
| BreakStatement
|
||||
| ContinueStatement
|
||||
| IfStatement
|
||||
| SwitchStatement
|
||||
| ThrowStatement
|
||||
| TryStatement
|
||||
| WhileStatement
|
||||
| DoWhileStatement
|
||||
| ForStatement
|
||||
| ForInStatement
|
||||
| ForOfStatement
|
||||
| Declaration;
|
||||
|
||||
interface BaseStatement extends BaseNode {}
|
||||
|
||||
interface EmptyStatement extends BaseStatement {
|
||||
type: "EmptyStatement";
|
||||
}
|
||||
|
||||
interface BlockStatement extends BaseStatement {
|
||||
type: "BlockStatement";
|
||||
body: Statement[];
|
||||
innerComments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
interface StaticBlock extends Omit<BlockStatement, "type"> {
|
||||
type: "StaticBlock";
|
||||
}
|
||||
|
||||
interface ExpressionStatement extends BaseStatement {
|
||||
type: "ExpressionStatement";
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
interface IfStatement extends BaseStatement {
|
||||
type: "IfStatement";
|
||||
test: Expression;
|
||||
consequent: Statement;
|
||||
alternate?: Statement | null | undefined;
|
||||
}
|
||||
|
||||
interface LabeledStatement extends BaseStatement {
|
||||
type: "LabeledStatement";
|
||||
label: Identifier;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
interface BreakStatement extends BaseStatement {
|
||||
type: "BreakStatement";
|
||||
label?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
interface ContinueStatement extends BaseStatement {
|
||||
type: "ContinueStatement";
|
||||
label?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
interface WithStatement extends BaseStatement {
|
||||
type: "WithStatement";
|
||||
object: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
interface SwitchStatement extends BaseStatement {
|
||||
type: "SwitchStatement";
|
||||
discriminant: Expression;
|
||||
cases: SwitchCase[];
|
||||
}
|
||||
|
||||
interface ReturnStatement extends BaseStatement {
|
||||
type: "ReturnStatement";
|
||||
argument?: Expression | null | undefined;
|
||||
}
|
||||
|
||||
interface ThrowStatement extends BaseStatement {
|
||||
type: "ThrowStatement";
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
interface TryStatement extends BaseStatement {
|
||||
type: "TryStatement";
|
||||
block: BlockStatement;
|
||||
handler?: CatchClause | null | undefined;
|
||||
finalizer?: BlockStatement | null | undefined;
|
||||
}
|
||||
|
||||
interface WhileStatement extends BaseStatement {
|
||||
type: "WhileStatement";
|
||||
test: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
interface DoWhileStatement extends BaseStatement {
|
||||
type: "DoWhileStatement";
|
||||
body: Statement;
|
||||
test: Expression;
|
||||
}
|
||||
|
||||
interface ForStatement extends BaseStatement {
|
||||
type: "ForStatement";
|
||||
init?: VariableDeclaration | Expression | null | undefined;
|
||||
test?: Expression | null | undefined;
|
||||
update?: Expression | null | undefined;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
interface BaseForXStatement extends BaseStatement {
|
||||
left: VariableDeclaration | Pattern;
|
||||
right: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
interface ForInStatement extends BaseForXStatement {
|
||||
type: "ForInStatement";
|
||||
}
|
||||
|
||||
interface DebuggerStatement extends BaseStatement {
|
||||
type: "DebuggerStatement";
|
||||
}
|
||||
|
||||
type Declaration = FunctionDeclaration | VariableDeclaration | ClassDeclaration;
|
||||
|
||||
interface BaseDeclaration extends BaseStatement {}
|
||||
|
||||
interface MaybeNamedFunctionDeclaration extends BaseFunction, BaseDeclaration {
|
||||
type: "FunctionDeclaration";
|
||||
/** It is null when a function declaration is a part of the `export default function` statement */
|
||||
id: Identifier | null;
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
interface FunctionDeclaration extends MaybeNamedFunctionDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
interface VariableDeclaration extends BaseDeclaration {
|
||||
type: "VariableDeclaration";
|
||||
declarations: VariableDeclarator[];
|
||||
kind: "var" | "let" | "const";
|
||||
}
|
||||
|
||||
interface VariableDeclarator extends BaseNode {
|
||||
type: "VariableDeclarator";
|
||||
id: Pattern;
|
||||
init?: Expression | null | undefined;
|
||||
}
|
||||
|
||||
interface ExpressionMap {
|
||||
ArrayExpression: ArrayExpression;
|
||||
ArrowFunctionExpression: ArrowFunctionExpression;
|
||||
AssignmentExpression: AssignmentExpression;
|
||||
AwaitExpression: AwaitExpression;
|
||||
BinaryExpression: BinaryExpression;
|
||||
CallExpression: CallExpression;
|
||||
ChainExpression: ChainExpression;
|
||||
ClassExpression: ClassExpression;
|
||||
ConditionalExpression: ConditionalExpression;
|
||||
FunctionExpression: FunctionExpression;
|
||||
Identifier: Identifier;
|
||||
ImportExpression: ImportExpression;
|
||||
Literal: Literal;
|
||||
LogicalExpression: LogicalExpression;
|
||||
MemberExpression: MemberExpression;
|
||||
MetaProperty: MetaProperty;
|
||||
NewExpression: NewExpression;
|
||||
ObjectExpression: ObjectExpression;
|
||||
SequenceExpression: SequenceExpression;
|
||||
TaggedTemplateExpression: TaggedTemplateExpression;
|
||||
TemplateLiteral: TemplateLiteral;
|
||||
ThisExpression: ThisExpression;
|
||||
UnaryExpression: UnaryExpression;
|
||||
UpdateExpression: UpdateExpression;
|
||||
YieldExpression: YieldExpression;
|
||||
}
|
||||
|
||||
type Expression = ExpressionMap[keyof ExpressionMap];
|
||||
|
||||
interface BaseExpression extends BaseNode {}
|
||||
|
||||
type ChainElement = SimpleCallExpression | MemberExpression;
|
||||
|
||||
interface ChainExpression extends BaseExpression {
|
||||
type: "ChainExpression";
|
||||
expression: ChainElement;
|
||||
}
|
||||
|
||||
interface ThisExpression extends BaseExpression {
|
||||
type: "ThisExpression";
|
||||
}
|
||||
|
||||
interface ArrayExpression extends BaseExpression {
|
||||
type: "ArrayExpression";
|
||||
elements: Array<Expression | SpreadElement | null>;
|
||||
}
|
||||
|
||||
interface ObjectExpression extends BaseExpression {
|
||||
type: "ObjectExpression";
|
||||
properties: Array<Property | SpreadElement>;
|
||||
}
|
||||
|
||||
interface PrivateIdentifier extends BaseNode {
|
||||
type: "PrivateIdentifier";
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface Property extends BaseNode {
|
||||
type: "Property";
|
||||
key: Expression | PrivateIdentifier;
|
||||
value: Expression | Pattern; // Could be an AssignmentProperty
|
||||
kind: "init" | "get" | "set";
|
||||
method: boolean;
|
||||
shorthand: boolean;
|
||||
computed: boolean;
|
||||
}
|
||||
|
||||
interface PropertyDefinition extends BaseNode {
|
||||
type: "PropertyDefinition";
|
||||
key: Expression | PrivateIdentifier;
|
||||
value?: Expression | null | undefined;
|
||||
computed: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface FunctionExpression extends BaseFunction, BaseExpression {
|
||||
id?: Identifier | null | undefined;
|
||||
type: "FunctionExpression";
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
interface SequenceExpression extends BaseExpression {
|
||||
type: "SequenceExpression";
|
||||
expressions: Expression[];
|
||||
}
|
||||
|
||||
interface UnaryExpression extends BaseExpression {
|
||||
type: "UnaryExpression";
|
||||
operator: UnaryOperator;
|
||||
prefix: true;
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
interface BinaryExpression extends BaseExpression {
|
||||
type: "BinaryExpression";
|
||||
operator: BinaryOperator;
|
||||
left: Expression;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
interface AssignmentExpression extends BaseExpression {
|
||||
type: "AssignmentExpression";
|
||||
operator: AssignmentOperator;
|
||||
left: Pattern | MemberExpression;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
interface UpdateExpression extends BaseExpression {
|
||||
type: "UpdateExpression";
|
||||
operator: UpdateOperator;
|
||||
argument: Expression;
|
||||
prefix: boolean;
|
||||
}
|
||||
|
||||
interface LogicalExpression extends BaseExpression {
|
||||
type: "LogicalExpression";
|
||||
operator: LogicalOperator;
|
||||
left: Expression;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
interface ConditionalExpression extends BaseExpression {
|
||||
type: "ConditionalExpression";
|
||||
test: Expression;
|
||||
alternate: Expression;
|
||||
consequent: Expression;
|
||||
}
|
||||
|
||||
interface BaseCallExpression extends BaseExpression {
|
||||
callee: Expression | Super;
|
||||
arguments: Array<Expression | SpreadElement>;
|
||||
}
|
||||
type CallExpression = SimpleCallExpression | NewExpression;
|
||||
|
||||
interface SimpleCallExpression extends BaseCallExpression {
|
||||
type: "CallExpression";
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
interface NewExpression extends BaseCallExpression {
|
||||
type: "NewExpression";
|
||||
}
|
||||
|
||||
interface MemberExpression extends BaseExpression, BasePattern {
|
||||
type: "MemberExpression";
|
||||
object: Expression | Super;
|
||||
property: Expression | PrivateIdentifier;
|
||||
computed: boolean;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
type Pattern = Identifier | ObjectPattern | ArrayPattern | RestElement | AssignmentPattern | MemberExpression;
|
||||
|
||||
interface BasePattern extends BaseNode {}
|
||||
|
||||
interface SwitchCase extends BaseNode {
|
||||
type: "SwitchCase";
|
||||
test?: Expression | null | undefined;
|
||||
consequent: Statement[];
|
||||
}
|
||||
|
||||
interface CatchClause extends BaseNode {
|
||||
type: "CatchClause";
|
||||
param: Pattern | null;
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
interface Identifier extends BaseNode, BaseExpression, BasePattern {
|
||||
type: "Identifier";
|
||||
name: string;
|
||||
}
|
||||
|
||||
type Literal = SimpleLiteral | RegExpLiteral | BigIntLiteral;
|
||||
|
||||
interface SimpleLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value: string | boolean | number | null;
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
interface RegExpLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value?: RegExp | null | undefined;
|
||||
regex: {
|
||||
pattern: string;
|
||||
flags: string;
|
||||
};
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
interface BigIntLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value?: bigint | null | undefined;
|
||||
bigint: string;
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
type UnaryOperator = "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";
|
||||
|
||||
type BinaryOperator =
|
||||
| "=="
|
||||
| "!="
|
||||
| "==="
|
||||
| "!=="
|
||||
| "<"
|
||||
| "<="
|
||||
| ">"
|
||||
| ">="
|
||||
| "<<"
|
||||
| ">>"
|
||||
| ">>>"
|
||||
| "+"
|
||||
| "-"
|
||||
| "*"
|
||||
| "/"
|
||||
| "%"
|
||||
| "**"
|
||||
| "|"
|
||||
| "^"
|
||||
| "&"
|
||||
| "in"
|
||||
| "instanceof";
|
||||
|
||||
type LogicalOperator = "||" | "&&" | "??";
|
||||
|
||||
type AssignmentOperator =
|
||||
| "="
|
||||
| "+="
|
||||
| "-="
|
||||
| "*="
|
||||
| "/="
|
||||
| "%="
|
||||
| "**="
|
||||
| "<<="
|
||||
| ">>="
|
||||
| ">>>="
|
||||
| "|="
|
||||
| "^="
|
||||
| "&="
|
||||
| "||="
|
||||
| "&&="
|
||||
| "??=";
|
||||
|
||||
type UpdateOperator = "++" | "--";
|
||||
|
||||
interface ForOfStatement extends BaseForXStatement {
|
||||
type: "ForOfStatement";
|
||||
await: boolean;
|
||||
}
|
||||
|
||||
interface Super extends BaseNode {
|
||||
type: "Super";
|
||||
}
|
||||
|
||||
interface SpreadElement extends BaseNode {
|
||||
type: "SpreadElement";
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
interface ArrowFunctionExpression extends BaseExpression, BaseFunction {
|
||||
type: "ArrowFunctionExpression";
|
||||
expression: boolean;
|
||||
body: BlockStatement | Expression;
|
||||
}
|
||||
|
||||
interface YieldExpression extends BaseExpression {
|
||||
type: "YieldExpression";
|
||||
argument?: Expression | null | undefined;
|
||||
delegate: boolean;
|
||||
}
|
||||
|
||||
interface TemplateLiteral extends BaseExpression {
|
||||
type: "TemplateLiteral";
|
||||
quasis: TemplateElement[];
|
||||
expressions: Expression[];
|
||||
}
|
||||
|
||||
interface TaggedTemplateExpression extends BaseExpression {
|
||||
type: "TaggedTemplateExpression";
|
||||
tag: Expression;
|
||||
quasi: TemplateLiteral;
|
||||
}
|
||||
|
||||
interface TemplateElement extends BaseNode {
|
||||
type: "TemplateElement";
|
||||
tail: boolean;
|
||||
value: {
|
||||
/** It is null when the template literal is tagged and the text has an invalid escape (e.g. - tag`\unicode and \u{55}`) */
|
||||
cooked?: string | null | undefined;
|
||||
raw: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AssignmentProperty extends Property {
|
||||
value: Pattern;
|
||||
kind: "init";
|
||||
method: boolean; // false
|
||||
}
|
||||
|
||||
interface ObjectPattern extends BasePattern {
|
||||
type: "ObjectPattern";
|
||||
properties: Array<AssignmentProperty | RestElement>;
|
||||
}
|
||||
|
||||
interface ArrayPattern extends BasePattern {
|
||||
type: "ArrayPattern";
|
||||
elements: Array<Pattern | null>;
|
||||
}
|
||||
|
||||
interface RestElement extends BasePattern {
|
||||
type: "RestElement";
|
||||
argument: Pattern;
|
||||
}
|
||||
|
||||
interface AssignmentPattern extends BasePattern {
|
||||
type: "AssignmentPattern";
|
||||
left: Pattern;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
type Class = ClassDeclaration | ClassExpression;
|
||||
interface BaseClass extends BaseNode {
|
||||
superClass?: Expression | null | undefined;
|
||||
body: ClassBody;
|
||||
}
|
||||
|
||||
interface ClassBody extends BaseNode {
|
||||
type: "ClassBody";
|
||||
body: Array<MethodDefinition | PropertyDefinition | StaticBlock>;
|
||||
}
|
||||
|
||||
interface MethodDefinition extends BaseNode {
|
||||
type: "MethodDefinition";
|
||||
key: Expression | PrivateIdentifier;
|
||||
value: FunctionExpression;
|
||||
kind: "constructor" | "method" | "get" | "set";
|
||||
computed: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface MaybeNamedClassDeclaration extends BaseClass, BaseDeclaration {
|
||||
type: "ClassDeclaration";
|
||||
/** It is null when a class declaration is a part of the `export default class` statement */
|
||||
id: Identifier | null;
|
||||
}
|
||||
|
||||
interface ClassDeclaration extends MaybeNamedClassDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
interface ClassExpression extends BaseClass, BaseExpression {
|
||||
type: "ClassExpression";
|
||||
id?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
interface MetaProperty extends BaseExpression {
|
||||
type: "MetaProperty";
|
||||
meta: Identifier;
|
||||
property: Identifier;
|
||||
}
|
||||
|
||||
type ModuleDeclaration =
|
||||
| ImportDeclaration
|
||||
| ExportNamedDeclaration
|
||||
| ExportDefaultDeclaration
|
||||
| ExportAllDeclaration;
|
||||
interface BaseModuleDeclaration extends BaseNode {}
|
||||
|
||||
type ModuleSpecifier = ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier;
|
||||
interface BaseModuleSpecifier extends BaseNode {
|
||||
local: Identifier;
|
||||
}
|
||||
|
||||
interface ImportDeclaration extends BaseModuleDeclaration {
|
||||
type: "ImportDeclaration";
|
||||
specifiers: Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>;
|
||||
source: Literal;
|
||||
}
|
||||
|
||||
interface ImportSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportSpecifier";
|
||||
imported: Identifier;
|
||||
}
|
||||
|
||||
interface ImportExpression extends BaseExpression {
|
||||
type: "ImportExpression";
|
||||
source: Expression;
|
||||
}
|
||||
|
||||
interface ImportDefaultSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportDefaultSpecifier";
|
||||
}
|
||||
|
||||
interface ImportNamespaceSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportNamespaceSpecifier";
|
||||
}
|
||||
|
||||
interface ExportNamedDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportNamedDeclaration";
|
||||
declaration?: Declaration | null | undefined;
|
||||
specifiers: ExportSpecifier[];
|
||||
source?: Literal | null | undefined;
|
||||
}
|
||||
|
||||
interface ExportSpecifier extends BaseModuleSpecifier {
|
||||
type: "ExportSpecifier";
|
||||
exported: Identifier;
|
||||
}
|
||||
|
||||
interface ExportDefaultDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportDefaultDeclaration";
|
||||
declaration: MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | Expression;
|
||||
}
|
||||
|
||||
interface ExportAllDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportAllDeclaration";
|
||||
exported: Identifier | null;
|
||||
source: Literal;
|
||||
}
|
||||
|
||||
interface AwaitExpression extends BaseExpression {
|
||||
type: "AwaitExpression";
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
type Positioned<T> = T & {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
type Node = Positioned<Node$1>;
|
||||
interface IdentifierInfo {
|
||||
/**
|
||||
* If the identifier is used in a property shorthand
|
||||
* { foo } -> { foo: __import_x__.foo }
|
||||
*/
|
||||
hasBindingShortcut: boolean;
|
||||
/**
|
||||
* The identifier is used in a class declaration
|
||||
*/
|
||||
classDeclaration: boolean;
|
||||
/**
|
||||
* The identifier is a name for a class expression
|
||||
*/
|
||||
classExpression: boolean;
|
||||
}
|
||||
interface Visitors {
|
||||
onIdentifier?: (node: Positioned<Identifier>, info: IdentifierInfo, parentStack: Node[]) => void;
|
||||
onImportMeta?: (node: Node) => void;
|
||||
onDynamicImport?: (node: Positioned<ImportExpression>) => void;
|
||||
onCallExpression?: (node: Positioned<CallExpression>) => void;
|
||||
}
|
||||
declare function setIsNodeInPattern(node: Property): WeakSet<Node$1>;
|
||||
declare function isNodeInPattern(node: Node$1): node is Property;
|
||||
/**
|
||||
* Same logic from \@vue/compiler-core & \@vue/compiler-sfc
|
||||
* Except this is using acorn AST
|
||||
*/
|
||||
declare function esmWalker(root: Node, { onIdentifier, onImportMeta, onDynamicImport, onCallExpression }: Visitors): void;
|
||||
declare function isStaticProperty(node: Node$1): node is Property;
|
||||
declare function isStaticPropertyKey(node: Node$1, parent: Node$1): boolean;
|
||||
declare function isFunctionNode(node: Node$1): node is Function;
|
||||
declare function isInDestructuringAssignment(parent: Node$1, parentStack: Node$1[]): boolean;
|
||||
|
||||
export { type ArrayExpression, type ArrayPattern, type ArrowFunctionExpression, type AssignmentExpression, type AssignmentOperator, type AssignmentPattern, type AssignmentProperty, type AwaitExpression, type BaseCallExpression, type BaseClass, type BaseDeclaration, type BaseExpression, type BaseForXStatement, type BaseFunction, type BaseModuleDeclaration, type BaseModuleSpecifier, type BaseNode, type BaseNodeWithoutComments, type BasePattern, type BaseStatement, type BigIntLiteral, type BinaryExpression, type BinaryOperator, type BlockStatement, type BreakStatement, type CallExpression, type CatchClause, type ChainElement, type ChainExpression, type Class, type ClassBody, type ClassDeclaration, type ClassExpression, type Comment, type ConditionalExpression, type ContinueStatement, type DebuggerStatement, type Declaration, type Directive, type DoWhileStatement, type EmptyStatement, type ExportAllDeclaration, type ExportDefaultDeclaration, type ExportNamedDeclaration, type ExportSpecifier, type Expression, type ExpressionMap, type ExpressionStatement, type ForInStatement, type ForOfStatement, type ForStatement, type Function, type FunctionDeclaration, type FunctionExpression, type Identifier, type IfStatement, type ImportDeclaration, type ImportDefaultSpecifier, type ImportExpression, type ImportNamespaceSpecifier, type ImportSpecifier, type LabeledStatement, type Literal, type LogicalExpression, type LogicalOperator, type MaybeNamedClassDeclaration, type MaybeNamedFunctionDeclaration, type MemberExpression, type MetaProperty, type MethodDefinition, type ModuleDeclaration, type ModuleSpecifier, type NewExpression, type Node, type NodeMap, type ObjectExpression, type ObjectPattern, type Pattern, type Position, type Positioned, type PrivateIdentifier, type Program, type Property, type PropertyDefinition, type RegExpLiteral, type RestElement, type ReturnStatement, type SequenceExpression, type SimpleCallExpression, type SimpleLiteral, type SourceLocation, type SpreadElement, type Statement, type StaticBlock, type Super, type SwitchCase, type SwitchStatement, type TaggedTemplateExpression, type TemplateElement, type TemplateLiteral, type ThisExpression, type ThrowStatement, type TryStatement, type UnaryExpression, type UnaryOperator, type UpdateExpression, type UpdateOperator, type VariableDeclaration, type VariableDeclarator, type WhileStatement, type WithStatement, type YieldExpression, esmWalker, isFunctionNode, isInDestructuringAssignment, isNodeInPattern, isStaticProperty, isStaticPropertyKey, setIsNodeInPattern };
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
import { walk } from 'estree-walker';
|
||||
|
||||
const isNodeInPatternWeakSet = /* @__PURE__ */ new WeakSet();
|
||||
function setIsNodeInPattern(node) {
|
||||
return isNodeInPatternWeakSet.add(node);
|
||||
}
|
||||
function isNodeInPattern(node) {
|
||||
return isNodeInPatternWeakSet.has(node);
|
||||
}
|
||||
function esmWalker(root, { onIdentifier, onImportMeta, onDynamicImport, onCallExpression }) {
|
||||
const parentStack = [];
|
||||
const varKindStack = [];
|
||||
const scopeMap = /* @__PURE__ */ new WeakMap();
|
||||
const identifiers = [];
|
||||
const setScope = (node, name) => {
|
||||
let scopeIds = scopeMap.get(node);
|
||||
if (scopeIds && scopeIds.has(name))
|
||||
return;
|
||||
if (!scopeIds) {
|
||||
scopeIds = /* @__PURE__ */ new Set();
|
||||
scopeMap.set(node, scopeIds);
|
||||
}
|
||||
scopeIds.add(name);
|
||||
};
|
||||
function isInScope(name, parents) {
|
||||
return parents.some((node) => {
|
||||
var _a;
|
||||
return node && ((_a = scopeMap.get(node)) == null ? void 0 : _a.has(name));
|
||||
});
|
||||
}
|
||||
function handlePattern(p, parentScope) {
|
||||
if (p.type === "Identifier") {
|
||||
setScope(parentScope, p.name);
|
||||
} else if (p.type === "RestElement") {
|
||||
handlePattern(p.argument, parentScope);
|
||||
} else if (p.type === "ObjectPattern") {
|
||||
p.properties.forEach((property) => {
|
||||
if (property.type === "RestElement")
|
||||
setScope(parentScope, property.argument.name);
|
||||
else
|
||||
handlePattern(property.value, parentScope);
|
||||
});
|
||||
} else if (p.type === "ArrayPattern") {
|
||||
p.elements.forEach((element) => {
|
||||
if (element)
|
||||
handlePattern(element, parentScope);
|
||||
});
|
||||
} else if (p.type === "AssignmentPattern") {
|
||||
handlePattern(p.left, parentScope);
|
||||
} else {
|
||||
setScope(parentScope, p.name);
|
||||
}
|
||||
}
|
||||
walk(root, {
|
||||
enter(node, parent) {
|
||||
if (node.type === "ImportDeclaration")
|
||||
return this.skip();
|
||||
if (parent && !(parent.type === "IfStatement" && node === parent.alternate))
|
||||
parentStack.unshift(parent);
|
||||
if (node.type === "VariableDeclaration")
|
||||
varKindStack.unshift(node.kind);
|
||||
if (node.type === "CallExpression")
|
||||
onCallExpression == null ? void 0 : onCallExpression(node);
|
||||
if (node.type === "MetaProperty" && node.meta.name === "import")
|
||||
onImportMeta == null ? void 0 : onImportMeta(node);
|
||||
else if (node.type === "ImportExpression")
|
||||
onDynamicImport == null ? void 0 : onDynamicImport(node);
|
||||
if (node.type === "Identifier") {
|
||||
if (!isInScope(node.name, parentStack) && isRefIdentifier(node, parent, parentStack)) {
|
||||
identifiers.push([node, parentStack.slice(0)]);
|
||||
}
|
||||
} else if (isFunctionNode(node)) {
|
||||
if (node.type === "FunctionDeclaration") {
|
||||
const parentScope = findParentScope(parentStack);
|
||||
if (parentScope)
|
||||
setScope(parentScope, node.id.name);
|
||||
}
|
||||
node.params.forEach((p) => {
|
||||
if (p.type === "ObjectPattern" || p.type === "ArrayPattern") {
|
||||
handlePattern(p, node);
|
||||
return;
|
||||
}
|
||||
walk(p.type === "AssignmentPattern" ? p.left : p, {
|
||||
enter(child, parent2) {
|
||||
if ((parent2 == null ? void 0 : parent2.type) === "AssignmentPattern" && (parent2 == null ? void 0 : parent2.right) === child)
|
||||
return this.skip();
|
||||
if (child.type !== "Identifier")
|
||||
return;
|
||||
if (isStaticPropertyKey(child, parent2))
|
||||
return;
|
||||
if ((parent2 == null ? void 0 : parent2.type) === "TemplateLiteral" && (parent2 == null ? void 0 : parent2.expressions.includes(child)) || (parent2 == null ? void 0 : parent2.type) === "CallExpression" && (parent2 == null ? void 0 : parent2.callee) === child)
|
||||
return;
|
||||
setScope(node, child.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (node.type === "Property" && parent.type === "ObjectPattern") {
|
||||
setIsNodeInPattern(node);
|
||||
} else if (node.type === "VariableDeclarator") {
|
||||
const parentFunction = findParentScope(
|
||||
parentStack,
|
||||
varKindStack[0] === "var"
|
||||
);
|
||||
if (parentFunction)
|
||||
handlePattern(node.id, parentFunction);
|
||||
} else if (node.type === "CatchClause" && node.param) {
|
||||
handlePattern(node.param, node);
|
||||
}
|
||||
},
|
||||
leave(node, parent) {
|
||||
if (parent && !(parent.type === "IfStatement" && node === parent.alternate))
|
||||
parentStack.shift();
|
||||
if (node.type === "VariableDeclaration")
|
||||
varKindStack.shift();
|
||||
}
|
||||
});
|
||||
identifiers.forEach(([node, stack]) => {
|
||||
if (!isInScope(node.name, stack)) {
|
||||
const parent = stack[0];
|
||||
const grandparent = stack[1];
|
||||
const hasBindingShortcut = isStaticProperty(parent) && parent.shorthand && (!isNodeInPattern(parent) || isInDestructuringAssignment(parent, parentStack));
|
||||
const classDeclaration = parent.type === "PropertyDefinition" && (grandparent == null ? void 0 : grandparent.type) === "ClassBody" || parent.type === "ClassDeclaration" && node === parent.superClass;
|
||||
const classExpression = parent.type === "ClassExpression" && node === parent.id;
|
||||
onIdentifier == null ? void 0 : onIdentifier(node, {
|
||||
hasBindingShortcut,
|
||||
classDeclaration,
|
||||
classExpression
|
||||
}, stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
function isRefIdentifier(id, parent, parentStack) {
|
||||
if (parent.type === "CatchClause" || (parent.type === "VariableDeclarator" || parent.type === "ClassDeclaration") && parent.id === id)
|
||||
return false;
|
||||
if (isFunctionNode(parent)) {
|
||||
if (parent.id === id)
|
||||
return false;
|
||||
if (parent.params.includes(id))
|
||||
return false;
|
||||
}
|
||||
if (parent.type === "MethodDefinition" && !parent.computed)
|
||||
return false;
|
||||
if (isStaticPropertyKey(id, parent))
|
||||
return false;
|
||||
if (isNodeInPattern(parent) && parent.value === id)
|
||||
return false;
|
||||
if (parent.type === "ArrayPattern" && !isInDestructuringAssignment(parent, parentStack))
|
||||
return false;
|
||||
if (parent.type === "MemberExpression" && parent.property === id && !parent.computed)
|
||||
return false;
|
||||
if (parent.type === "ExportSpecifier")
|
||||
return false;
|
||||
if (id.name === "arguments")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
function isStaticProperty(node) {
|
||||
return node && node.type === "Property" && !node.computed;
|
||||
}
|
||||
function isStaticPropertyKey(node, parent) {
|
||||
return isStaticProperty(parent) && parent.key === node;
|
||||
}
|
||||
const functionNodeTypeRE = /Function(?:Expression|Declaration)$|Method$/;
|
||||
function isFunctionNode(node) {
|
||||
return functionNodeTypeRE.test(node.type);
|
||||
}
|
||||
const blockNodeTypeRE = /^BlockStatement$|^For(?:In|Of)?Statement$/;
|
||||
function isBlock(node) {
|
||||
return blockNodeTypeRE.test(node.type);
|
||||
}
|
||||
function findParentScope(parentStack, isVar = false) {
|
||||
return parentStack.find(isVar ? isFunctionNode : isBlock);
|
||||
}
|
||||
function isInDestructuringAssignment(parent, parentStack) {
|
||||
if (parent && (parent.type === "Property" || parent.type === "ArrayPattern"))
|
||||
return parentStack.some((i) => i.type === "AssignmentExpression");
|
||||
return false;
|
||||
}
|
||||
|
||||
export { esmWalker, isFunctionNode, isInDestructuringAssignment, isNodeInPattern, isStaticProperty, isStaticPropertyKey, setIsNodeInPattern };
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
const SAFE_TIMERS_SYMBOL = Symbol("vitest:SAFE_TIMERS");
|
||||
const SAFE_COLORS_SYMBOL = Symbol("vitest:SAFE_COLORS");
|
||||
|
||||
const colorsMap = {
|
||||
bold: ["\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"],
|
||||
dim: ["\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"],
|
||||
italic: ["\x1B[3m", "\x1B[23m"],
|
||||
underline: ["\x1B[4m", "\x1B[24m"],
|
||||
inverse: ["\x1B[7m", "\x1B[27m"],
|
||||
hidden: ["\x1B[8m", "\x1B[28m"],
|
||||
strikethrough: ["\x1B[9m", "\x1B[29m"],
|
||||
black: ["\x1B[30m", "\x1B[39m"],
|
||||
red: ["\x1B[31m", "\x1B[39m"],
|
||||
green: ["\x1B[32m", "\x1B[39m"],
|
||||
yellow: ["\x1B[33m", "\x1B[39m"],
|
||||
blue: ["\x1B[34m", "\x1B[39m"],
|
||||
magenta: ["\x1B[35m", "\x1B[39m"],
|
||||
cyan: ["\x1B[36m", "\x1B[39m"],
|
||||
white: ["\x1B[37m", "\x1B[39m"],
|
||||
gray: ["\x1B[90m", "\x1B[39m"],
|
||||
bgBlack: ["\x1B[40m", "\x1B[49m"],
|
||||
bgRed: ["\x1B[41m", "\x1B[49m"],
|
||||
bgGreen: ["\x1B[42m", "\x1B[49m"],
|
||||
bgYellow: ["\x1B[43m", "\x1B[49m"],
|
||||
bgBlue: ["\x1B[44m", "\x1B[49m"],
|
||||
bgMagenta: ["\x1B[45m", "\x1B[49m"],
|
||||
bgCyan: ["\x1B[46m", "\x1B[49m"],
|
||||
bgWhite: ["\x1B[47m", "\x1B[49m"]
|
||||
};
|
||||
const colorsEntries = Object.entries(colorsMap);
|
||||
function string(str) {
|
||||
return String(str);
|
||||
}
|
||||
string.open = "";
|
||||
string.close = "";
|
||||
const defaultColors = /* @__PURE__ */ colorsEntries.reduce((acc, [key]) => {
|
||||
acc[key] = string;
|
||||
return acc;
|
||||
}, { isColorSupported: false });
|
||||
function getDefaultColors() {
|
||||
return { ...defaultColors };
|
||||
}
|
||||
function getColors() {
|
||||
return globalThis[SAFE_COLORS_SYMBOL] || defaultColors;
|
||||
}
|
||||
function createColors(isTTY = false) {
|
||||
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);
|
||||
const replaceClose = (string2, close, replace, index) => {
|
||||
const start = string2.substring(0, index) + replace;
|
||||
const end = string2.substring(index + close.length);
|
||||
const nextIndex = end.indexOf(close);
|
||||
return ~nextIndex ? start + replaceClose(end, close, replace, nextIndex) : start + end;
|
||||
};
|
||||
const formatter = (open, close, replace = open) => {
|
||||
const fn = (input) => {
|
||||
const string2 = String(input);
|
||||
const index = string2.indexOf(close, open.length);
|
||||
return ~index ? open + replaceClose(string2, close, replace, index) + close : open + string2 + close;
|
||||
};
|
||||
fn.open = open;
|
||||
fn.close = close;
|
||||
return fn;
|
||||
};
|
||||
const colorsObject = {
|
||||
isColorSupported: enabled,
|
||||
reset: enabled ? (s) => `\x1B[0m${s}\x1B[0m` : string
|
||||
};
|
||||
for (const [name, formatterArgs] of colorsEntries) {
|
||||
colorsObject[name] = enabled ? formatter(...formatterArgs) : string;
|
||||
}
|
||||
return colorsObject;
|
||||
}
|
||||
function setupColors(colors) {
|
||||
globalThis[SAFE_COLORS_SYMBOL] = colors;
|
||||
}
|
||||
|
||||
export { SAFE_TIMERS_SYMBOL as S, SAFE_COLORS_SYMBOL as a, getDefaultColors as b, createColors as c, getColors as g, setupColors as s };
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
import { format as format$1, plugins } from 'pretty-format';
|
||||
import * as loupe from 'loupe';
|
||||
|
||||
const {
|
||||
AsymmetricMatcher,
|
||||
DOMCollection,
|
||||
DOMElement,
|
||||
Immutable,
|
||||
ReactElement,
|
||||
ReactTestComponent
|
||||
} = plugins;
|
||||
const PLUGINS = [
|
||||
ReactTestComponent,
|
||||
ReactElement,
|
||||
DOMElement,
|
||||
DOMCollection,
|
||||
Immutable,
|
||||
AsymmetricMatcher
|
||||
];
|
||||
function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
|
||||
const MAX_LENGTH = maxLength ?? 1e4;
|
||||
let result;
|
||||
try {
|
||||
result = format$1(object, {
|
||||
maxDepth,
|
||||
escapeString: false,
|
||||
// min: true,
|
||||
plugins: PLUGINS,
|
||||
...options
|
||||
});
|
||||
} catch {
|
||||
result = format$1(object, {
|
||||
callToJSON: false,
|
||||
maxDepth,
|
||||
escapeString: false,
|
||||
// min: true,
|
||||
plugins: PLUGINS,
|
||||
...options
|
||||
});
|
||||
}
|
||||
return result.length >= MAX_LENGTH && maxDepth > 1 ? stringify(object, Math.floor(maxDepth / 2)) : result;
|
||||
}
|
||||
|
||||
const formatRegExp = /%[sdjifoOcj%]/g;
|
||||
function format(...args) {
|
||||
if (typeof args[0] !== "string") {
|
||||
const objects = [];
|
||||
for (let i2 = 0; i2 < args.length; i2++)
|
||||
objects.push(inspect(args[i2], { depth: 0, colors: false, compact: 3 }));
|
||||
return objects.join(" ");
|
||||
}
|
||||
const len = args.length;
|
||||
let i = 1;
|
||||
const template = args[0];
|
||||
let str = String(template).replace(formatRegExp, (x) => {
|
||||
if (x === "%%")
|
||||
return "%";
|
||||
if (i >= len)
|
||||
return x;
|
||||
switch (x) {
|
||||
case "%s": {
|
||||
const value = args[i++];
|
||||
if (typeof value === "bigint")
|
||||
return `${value.toString()}n`;
|
||||
if (typeof value === "number" && value === 0 && 1 / value < 0)
|
||||
return "-0";
|
||||
if (typeof value === "object" && value !== null)
|
||||
return inspect(value, { depth: 0, colors: false, compact: 3 });
|
||||
return String(value);
|
||||
}
|
||||
case "%d": {
|
||||
const value = args[i++];
|
||||
if (typeof value === "bigint")
|
||||
return `${value.toString()}n`;
|
||||
return Number(value).toString();
|
||||
}
|
||||
case "%i": {
|
||||
const value = args[i++];
|
||||
if (typeof value === "bigint")
|
||||
return `${value.toString()}n`;
|
||||
return Number.parseInt(String(value)).toString();
|
||||
}
|
||||
case "%f":
|
||||
return Number.parseFloat(String(args[i++])).toString();
|
||||
case "%o":
|
||||
return inspect(args[i++], { showHidden: true, showProxy: true });
|
||||
case "%O":
|
||||
return inspect(args[i++]);
|
||||
case "%c": {
|
||||
i++;
|
||||
return "";
|
||||
}
|
||||
case "%j":
|
||||
try {
|
||||
return JSON.stringify(args[i++]);
|
||||
} catch (err) {
|
||||
const m = err.message;
|
||||
if (
|
||||
// chromium
|
||||
m.includes("circular structure") || m.includes("cyclic structures") || m.includes("cyclic object")
|
||||
)
|
||||
return "[Circular]";
|
||||
throw err;
|
||||
}
|
||||
default:
|
||||
return x;
|
||||
}
|
||||
});
|
||||
for (let x = args[i]; i < len; x = args[++i]) {
|
||||
if (x === null || typeof x !== "object")
|
||||
str += ` ${x}`;
|
||||
else
|
||||
str += ` ${inspect(x)}`;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function inspect(obj, options = {}) {
|
||||
if (options.truncate === 0)
|
||||
options.truncate = Number.POSITIVE_INFINITY;
|
||||
return loupe.inspect(obj, options);
|
||||
}
|
||||
function objDisplay(obj, options = {}) {
|
||||
if (typeof options.truncate === "undefined")
|
||||
options.truncate = 40;
|
||||
const str = inspect(obj, options);
|
||||
const type = Object.prototype.toString.call(obj);
|
||||
if (options.truncate && str.length >= options.truncate) {
|
||||
if (type === "[object Function]") {
|
||||
const fn = obj;
|
||||
return !fn.name ? "[Function]" : `[Function: ${fn.name}]`;
|
||||
} else if (type === "[object Array]") {
|
||||
return `[ Array(${obj.length}) ]`;
|
||||
} else if (type === "[object Object]") {
|
||||
const keys = Object.keys(obj);
|
||||
const kstr = keys.length > 2 ? `${keys.splice(0, 2).join(", ")}, ...` : keys.join(", ");
|
||||
return `{ Object (${kstr}) }`;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export { format as f, inspect as i, objDisplay as o, stringify as s };
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import { D as DiffOptions } from './types-9l4niLY8.js';
|
||||
export { a as DiffOptionsColor } from './types-9l4niLY8.js';
|
||||
import 'pretty-format';
|
||||
|
||||
/**
|
||||
* Diff Match and Patch
|
||||
* Copyright 2018 The diff-match-patch Authors.
|
||||
* https://github.com/google/diff-match-patch
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* @fileoverview Computes the difference between two texts to create a patch.
|
||||
* Applies the patch onto another text, allowing for errors.
|
||||
* @author fraser@google.com (Neil Fraser)
|
||||
*/
|
||||
/**
|
||||
* CHANGES by pedrottimark to diff_match_patch_uncompressed.ts file:
|
||||
*
|
||||
* 1. Delete anything not needed to use diff_cleanupSemantic method
|
||||
* 2. Convert from prototype properties to var declarations
|
||||
* 3. Convert Diff to class from constructor and prototype
|
||||
* 4. Add type annotations for arguments and return values
|
||||
* 5. Add exports
|
||||
*/
|
||||
/**
|
||||
* The data structure representing a diff is an array of tuples:
|
||||
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
|
||||
* which means: delete 'Hello', add 'Goodbye' and keep ' world.'
|
||||
*/
|
||||
declare const DIFF_DELETE = -1;
|
||||
declare const DIFF_INSERT = 1;
|
||||
declare const DIFF_EQUAL = 0;
|
||||
/**
|
||||
* Class representing one diff tuple.
|
||||
* Attempts to look like a two-element array (which is what this used to be).
|
||||
* @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.
|
||||
* @param {string} text Text to be deleted, inserted, or retained.
|
||||
* @constructor
|
||||
*/
|
||||
declare class Diff {
|
||||
0: number;
|
||||
1: string;
|
||||
constructor(op: number, text: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare function diffLinesUnified(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): string;
|
||||
declare function diffLinesUnified2(aLinesDisplay: Array<string>, bLinesDisplay: Array<string>, aLinesCompare: Array<string>, bLinesCompare: Array<string>, options?: DiffOptions): string;
|
||||
declare function diffLinesRaw(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): [Array<Diff>, boolean];
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare function diffStringsUnified(a: string, b: string, options?: DiffOptions): string;
|
||||
declare function diffStringsRaw(a: string, b: string, cleanup: boolean, options?: DiffOptions): [Array<Diff>, boolean];
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param a Expected value
|
||||
* @param b Received value
|
||||
* @param options Diff options
|
||||
* @returns {string | null} a string diff
|
||||
*/
|
||||
declare function diff(a: any, b: any, options?: DiffOptions): string | null;
|
||||
|
||||
export { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff, DiffOptions, diff, diffLinesRaw, diffLinesUnified, diffLinesUnified2, diffStringsRaw, diffStringsUnified };
|
||||
+1081
@@ -0,0 +1,1081 @@
|
||||
import { format, plugins } from 'pretty-format';
|
||||
import * as diff$1 from 'diff-sequences';
|
||||
import { g as getColors } from './chunk-colors.js';
|
||||
|
||||
function getType(value) {
|
||||
if (value === void 0) {
|
||||
return "undefined";
|
||||
} else if (value === null) {
|
||||
return "null";
|
||||
} else if (Array.isArray(value)) {
|
||||
return "array";
|
||||
} else if (typeof value === "boolean") {
|
||||
return "boolean";
|
||||
} else if (typeof value === "function") {
|
||||
return "function";
|
||||
} else if (typeof value === "number") {
|
||||
return "number";
|
||||
} else if (typeof value === "string") {
|
||||
return "string";
|
||||
} else if (typeof value === "bigint") {
|
||||
return "bigint";
|
||||
} else if (typeof value === "object") {
|
||||
if (value != null) {
|
||||
if (value.constructor === RegExp)
|
||||
return "regexp";
|
||||
else if (value.constructor === Map)
|
||||
return "map";
|
||||
else if (value.constructor === Set)
|
||||
return "set";
|
||||
else if (value.constructor === Date)
|
||||
return "date";
|
||||
}
|
||||
return "object";
|
||||
} else if (typeof value === "symbol") {
|
||||
return "symbol";
|
||||
}
|
||||
throw new Error(`value of unknown type: ${value}`);
|
||||
}
|
||||
|
||||
const DIFF_DELETE = -1;
|
||||
const DIFF_INSERT = 1;
|
||||
const DIFF_EQUAL = 0;
|
||||
class Diff {
|
||||
0;
|
||||
1;
|
||||
constructor(op, text) {
|
||||
this[0] = op;
|
||||
this[1] = text;
|
||||
}
|
||||
}
|
||||
const diff_commonPrefix = function(text1, text2) {
|
||||
if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0))
|
||||
return 0;
|
||||
let pointermin = 0;
|
||||
let pointermax = Math.min(text1.length, text2.length);
|
||||
let pointermid = pointermax;
|
||||
let pointerstart = 0;
|
||||
while (pointermin < pointermid) {
|
||||
if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
|
||||
pointermin = pointermid;
|
||||
pointerstart = pointermin;
|
||||
} else {
|
||||
pointermax = pointermid;
|
||||
}
|
||||
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
|
||||
}
|
||||
return pointermid;
|
||||
};
|
||||
const diff_commonSuffix = function(text1, text2) {
|
||||
if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1))
|
||||
return 0;
|
||||
let pointermin = 0;
|
||||
let pointermax = Math.min(text1.length, text2.length);
|
||||
let pointermid = pointermax;
|
||||
let pointerend = 0;
|
||||
while (pointermin < pointermid) {
|
||||
if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
|
||||
pointermin = pointermid;
|
||||
pointerend = pointermin;
|
||||
} else {
|
||||
pointermax = pointermid;
|
||||
}
|
||||
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
|
||||
}
|
||||
return pointermid;
|
||||
};
|
||||
const diff_commonOverlap_ = function(text1, text2) {
|
||||
const text1_length = text1.length;
|
||||
const text2_length = text2.length;
|
||||
if (text1_length === 0 || text2_length === 0)
|
||||
return 0;
|
||||
if (text1_length > text2_length)
|
||||
text1 = text1.substring(text1_length - text2_length);
|
||||
else if (text1_length < text2_length)
|
||||
text2 = text2.substring(0, text1_length);
|
||||
const text_length = Math.min(text1_length, text2_length);
|
||||
if (text1 === text2)
|
||||
return text_length;
|
||||
let best = 0;
|
||||
let length = 1;
|
||||
while (true) {
|
||||
const pattern = text1.substring(text_length - length);
|
||||
const found = text2.indexOf(pattern);
|
||||
if (found === -1)
|
||||
return best;
|
||||
length += found;
|
||||
if (found === 0 || text1.substring(text_length - length) === text2.substring(0, length)) {
|
||||
best = length;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
};
|
||||
const diff_cleanupSemantic = function(diffs) {
|
||||
let changes = false;
|
||||
const equalities = [];
|
||||
let equalitiesLength = 0;
|
||||
let lastEquality = null;
|
||||
let pointer = 0;
|
||||
let length_insertions1 = 0;
|
||||
let length_deletions1 = 0;
|
||||
let length_insertions2 = 0;
|
||||
let length_deletions2 = 0;
|
||||
while (pointer < diffs.length) {
|
||||
if (diffs[pointer][0] === DIFF_EQUAL) {
|
||||
equalities[equalitiesLength++] = pointer;
|
||||
length_insertions1 = length_insertions2;
|
||||
length_deletions1 = length_deletions2;
|
||||
length_insertions2 = 0;
|
||||
length_deletions2 = 0;
|
||||
lastEquality = diffs[pointer][1];
|
||||
} else {
|
||||
if (diffs[pointer][0] === DIFF_INSERT)
|
||||
length_insertions2 += diffs[pointer][1].length;
|
||||
else
|
||||
length_deletions2 += diffs[pointer][1].length;
|
||||
if (lastEquality && lastEquality.length <= Math.max(length_insertions1, length_deletions1) && lastEquality.length <= Math.max(length_insertions2, length_deletions2)) {
|
||||
diffs.splice(equalities[equalitiesLength - 1], 0, new Diff(DIFF_DELETE, lastEquality));
|
||||
diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
|
||||
equalitiesLength--;
|
||||
equalitiesLength--;
|
||||
pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
|
||||
length_insertions1 = 0;
|
||||
length_deletions1 = 0;
|
||||
length_insertions2 = 0;
|
||||
length_deletions2 = 0;
|
||||
lastEquality = null;
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
if (changes)
|
||||
diff_cleanupMerge(diffs);
|
||||
diff_cleanupSemanticLossless(diffs);
|
||||
pointer = 1;
|
||||
while (pointer < diffs.length) {
|
||||
if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
|
||||
const deletion = diffs[pointer - 1][1];
|
||||
const insertion = diffs[pointer][1];
|
||||
const overlap_length1 = diff_commonOverlap_(deletion, insertion);
|
||||
const overlap_length2 = diff_commonOverlap_(insertion, deletion);
|
||||
if (overlap_length1 >= overlap_length2) {
|
||||
if (overlap_length1 >= deletion.length / 2 || overlap_length1 >= insertion.length / 2) {
|
||||
diffs.splice(pointer, 0, new Diff(DIFF_EQUAL, insertion.substring(0, overlap_length1)));
|
||||
diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlap_length1);
|
||||
diffs[pointer + 1][1] = insertion.substring(overlap_length1);
|
||||
pointer++;
|
||||
}
|
||||
} else {
|
||||
if (overlap_length2 >= deletion.length / 2 || overlap_length2 >= insertion.length / 2) {
|
||||
diffs.splice(pointer, 0, new Diff(DIFF_EQUAL, deletion.substring(0, overlap_length2)));
|
||||
diffs[pointer - 1][0] = DIFF_INSERT;
|
||||
diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlap_length2);
|
||||
diffs[pointer + 1][0] = DIFF_DELETE;
|
||||
diffs[pointer + 1][1] = deletion.substring(overlap_length2);
|
||||
pointer++;
|
||||
}
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
};
|
||||
const nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/;
|
||||
const whitespaceRegex_ = /\s/;
|
||||
const linebreakRegex_ = /[\r\n]/;
|
||||
const blanklineEndRegex_ = /\n\r?\n$/;
|
||||
const blanklineStartRegex_ = /^\r?\n\r?\n/;
|
||||
function diff_cleanupSemanticLossless(diffs) {
|
||||
function diff_cleanupSemanticScore_(one, two) {
|
||||
if (!one || !two) {
|
||||
return 6;
|
||||
}
|
||||
const char1 = one.charAt(one.length - 1);
|
||||
const char2 = two.charAt(0);
|
||||
const nonAlphaNumeric1 = char1.match(nonAlphaNumericRegex_);
|
||||
const nonAlphaNumeric2 = char2.match(nonAlphaNumericRegex_);
|
||||
const whitespace1 = nonAlphaNumeric1 && char1.match(whitespaceRegex_);
|
||||
const whitespace2 = nonAlphaNumeric2 && char2.match(whitespaceRegex_);
|
||||
const lineBreak1 = whitespace1 && char1.match(linebreakRegex_);
|
||||
const lineBreak2 = whitespace2 && char2.match(linebreakRegex_);
|
||||
const blankLine1 = lineBreak1 && one.match(blanklineEndRegex_);
|
||||
const blankLine2 = lineBreak2 && two.match(blanklineStartRegex_);
|
||||
if (blankLine1 || blankLine2) {
|
||||
return 5;
|
||||
} else if (lineBreak1 || lineBreak2) {
|
||||
return 4;
|
||||
} else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {
|
||||
return 3;
|
||||
} else if (whitespace1 || whitespace2) {
|
||||
return 2;
|
||||
} else if (nonAlphaNumeric1 || nonAlphaNumeric2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
let pointer = 1;
|
||||
while (pointer < diffs.length - 1) {
|
||||
if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
|
||||
let equality1 = diffs[pointer - 1][1];
|
||||
let edit = diffs[pointer][1];
|
||||
let equality2 = diffs[pointer + 1][1];
|
||||
const commonOffset = diff_commonSuffix(equality1, edit);
|
||||
if (commonOffset) {
|
||||
const commonString = edit.substring(edit.length - commonOffset);
|
||||
equality1 = equality1.substring(0, equality1.length - commonOffset);
|
||||
edit = commonString + edit.substring(0, edit.length - commonOffset);
|
||||
equality2 = commonString + equality2;
|
||||
}
|
||||
let bestEquality1 = equality1;
|
||||
let bestEdit = edit;
|
||||
let bestEquality2 = equality2;
|
||||
let bestScore = diff_cleanupSemanticScore_(equality1, edit) + diff_cleanupSemanticScore_(edit, equality2);
|
||||
while (edit.charAt(0) === equality2.charAt(0)) {
|
||||
equality1 += edit.charAt(0);
|
||||
edit = edit.substring(1) + equality2.charAt(0);
|
||||
equality2 = equality2.substring(1);
|
||||
const score = diff_cleanupSemanticScore_(equality1, edit) + diff_cleanupSemanticScore_(edit, equality2);
|
||||
if (score >= bestScore) {
|
||||
bestScore = score;
|
||||
bestEquality1 = equality1;
|
||||
bestEdit = edit;
|
||||
bestEquality2 = equality2;
|
||||
}
|
||||
}
|
||||
if (diffs[pointer - 1][1] !== bestEquality1) {
|
||||
if (bestEquality1) {
|
||||
diffs[pointer - 1][1] = bestEquality1;
|
||||
} else {
|
||||
diffs.splice(pointer - 1, 1);
|
||||
pointer--;
|
||||
}
|
||||
diffs[pointer][1] = bestEdit;
|
||||
if (bestEquality2) {
|
||||
diffs[pointer + 1][1] = bestEquality2;
|
||||
} else {
|
||||
diffs.splice(pointer + 1, 1);
|
||||
pointer--;
|
||||
}
|
||||
}
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
}
|
||||
function diff_cleanupMerge(diffs) {
|
||||
diffs.push(new Diff(DIFF_EQUAL, ""));
|
||||
let pointer = 0;
|
||||
let count_delete = 0;
|
||||
let count_insert = 0;
|
||||
let text_delete = "";
|
||||
let text_insert = "";
|
||||
let commonlength;
|
||||
while (pointer < diffs.length) {
|
||||
switch (diffs[pointer][0]) {
|
||||
case DIFF_INSERT:
|
||||
count_insert++;
|
||||
text_insert += diffs[pointer][1];
|
||||
pointer++;
|
||||
break;
|
||||
case DIFF_DELETE:
|
||||
count_delete++;
|
||||
text_delete += diffs[pointer][1];
|
||||
pointer++;
|
||||
break;
|
||||
case DIFF_EQUAL:
|
||||
if (count_delete + count_insert > 1) {
|
||||
if (count_delete !== 0 && count_insert !== 0) {
|
||||
commonlength = diff_commonPrefix(text_insert, text_delete);
|
||||
if (commonlength !== 0) {
|
||||
if (pointer - count_delete - count_insert > 0 && diffs[pointer - count_delete - count_insert - 1][0] === DIFF_EQUAL) {
|
||||
diffs[pointer - count_delete - count_insert - 1][1] += text_insert.substring(0, commonlength);
|
||||
} else {
|
||||
diffs.splice(0, 0, new Diff(DIFF_EQUAL, text_insert.substring(0, commonlength)));
|
||||
pointer++;
|
||||
}
|
||||
text_insert = text_insert.substring(commonlength);
|
||||
text_delete = text_delete.substring(commonlength);
|
||||
}
|
||||
commonlength = diff_commonSuffix(text_insert, text_delete);
|
||||
if (commonlength !== 0) {
|
||||
diffs[pointer][1] = text_insert.substring(text_insert.length - commonlength) + diffs[pointer][1];
|
||||
text_insert = text_insert.substring(0, text_insert.length - commonlength);
|
||||
text_delete = text_delete.substring(0, text_delete.length - commonlength);
|
||||
}
|
||||
}
|
||||
pointer -= count_delete + count_insert;
|
||||
diffs.splice(pointer, count_delete + count_insert);
|
||||
if (text_delete.length) {
|
||||
diffs.splice(pointer, 0, new Diff(DIFF_DELETE, text_delete));
|
||||
pointer++;
|
||||
}
|
||||
if (text_insert.length) {
|
||||
diffs.splice(pointer, 0, new Diff(DIFF_INSERT, text_insert));
|
||||
pointer++;
|
||||
}
|
||||
pointer++;
|
||||
} else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
|
||||
diffs[pointer - 1][1] += diffs[pointer][1];
|
||||
diffs.splice(pointer, 1);
|
||||
} else {
|
||||
pointer++;
|
||||
}
|
||||
count_insert = 0;
|
||||
count_delete = 0;
|
||||
text_delete = "";
|
||||
text_insert = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (diffs[diffs.length - 1][1] === "")
|
||||
diffs.pop();
|
||||
let changes = false;
|
||||
pointer = 1;
|
||||
while (pointer < diffs.length - 1) {
|
||||
if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
|
||||
if (diffs[pointer][1].substring(diffs[pointer][1].length - diffs[pointer - 1][1].length) === diffs[pointer - 1][1]) {
|
||||
diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
|
||||
diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
|
||||
diffs.splice(pointer - 1, 1);
|
||||
changes = true;
|
||||
} else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
|
||||
diffs[pointer - 1][1] += diffs[pointer + 1][1];
|
||||
diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
|
||||
diffs.splice(pointer + 1, 1);
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
if (changes)
|
||||
diff_cleanupMerge(diffs);
|
||||
}
|
||||
|
||||
const NO_DIFF_MESSAGE = "Compared values have no visual difference.";
|
||||
const SIMILAR_MESSAGE = "Compared values serialize to the same structure.\nPrinting internal object structure without calling `toJSON` instead.";
|
||||
|
||||
function formatTrailingSpaces(line, trailingSpaceFormatter) {
|
||||
return line.replace(/\s+$/, (match) => trailingSpaceFormatter(match));
|
||||
}
|
||||
function printDiffLine(line, isFirstOrLast, color, indicator, trailingSpaceFormatter, emptyFirstOrLastLinePlaceholder) {
|
||||
return line.length !== 0 ? color(
|
||||
`${indicator} ${formatTrailingSpaces(line, trailingSpaceFormatter)}`
|
||||
) : indicator !== " " ? color(indicator) : isFirstOrLast && emptyFirstOrLastLinePlaceholder.length !== 0 ? color(`${indicator} ${emptyFirstOrLastLinePlaceholder}`) : "";
|
||||
}
|
||||
function printDeleteLine(line, isFirstOrLast, {
|
||||
aColor,
|
||||
aIndicator,
|
||||
changeLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
}) {
|
||||
return printDiffLine(
|
||||
line,
|
||||
isFirstOrLast,
|
||||
aColor,
|
||||
aIndicator,
|
||||
changeLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
);
|
||||
}
|
||||
function printInsertLine(line, isFirstOrLast, {
|
||||
bColor,
|
||||
bIndicator,
|
||||
changeLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
}) {
|
||||
return printDiffLine(
|
||||
line,
|
||||
isFirstOrLast,
|
||||
bColor,
|
||||
bIndicator,
|
||||
changeLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
);
|
||||
}
|
||||
function printCommonLine(line, isFirstOrLast, {
|
||||
commonColor,
|
||||
commonIndicator,
|
||||
commonLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
}) {
|
||||
return printDiffLine(
|
||||
line,
|
||||
isFirstOrLast,
|
||||
commonColor,
|
||||
commonIndicator,
|
||||
commonLineTrailingSpaceColor,
|
||||
emptyFirstOrLastLinePlaceholder
|
||||
);
|
||||
}
|
||||
function createPatchMark(aStart, aEnd, bStart, bEnd, { patchColor }) {
|
||||
return patchColor(
|
||||
`@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`
|
||||
);
|
||||
}
|
||||
function joinAlignedDiffsNoExpand(diffs, options) {
|
||||
const iLength = diffs.length;
|
||||
const nContextLines = options.contextLines;
|
||||
const nContextLines2 = nContextLines + nContextLines;
|
||||
let jLength = iLength;
|
||||
let hasExcessAtStartOrEnd = false;
|
||||
let nExcessesBetweenChanges = 0;
|
||||
let i = 0;
|
||||
while (i !== iLength) {
|
||||
const iStart = i;
|
||||
while (i !== iLength && diffs[i][0] === DIFF_EQUAL)
|
||||
i += 1;
|
||||
if (iStart !== i) {
|
||||
if (iStart === 0) {
|
||||
if (i > nContextLines) {
|
||||
jLength -= i - nContextLines;
|
||||
hasExcessAtStartOrEnd = true;
|
||||
}
|
||||
} else if (i === iLength) {
|
||||
const n = i - iStart;
|
||||
if (n > nContextLines) {
|
||||
jLength -= n - nContextLines;
|
||||
hasExcessAtStartOrEnd = true;
|
||||
}
|
||||
} else {
|
||||
const n = i - iStart;
|
||||
if (n > nContextLines2) {
|
||||
jLength -= n - nContextLines2;
|
||||
nExcessesBetweenChanges += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i !== iLength && diffs[i][0] !== DIFF_EQUAL)
|
||||
i += 1;
|
||||
}
|
||||
const hasPatch = nExcessesBetweenChanges !== 0 || hasExcessAtStartOrEnd;
|
||||
if (nExcessesBetweenChanges !== 0)
|
||||
jLength += nExcessesBetweenChanges + 1;
|
||||
else if (hasExcessAtStartOrEnd)
|
||||
jLength += 1;
|
||||
const jLast = jLength - 1;
|
||||
const lines = [];
|
||||
let jPatchMark = 0;
|
||||
if (hasPatch)
|
||||
lines.push("");
|
||||
let aStart = 0;
|
||||
let bStart = 0;
|
||||
let aEnd = 0;
|
||||
let bEnd = 0;
|
||||
const pushCommonLine = (line) => {
|
||||
const j = lines.length;
|
||||
lines.push(printCommonLine(line, j === 0 || j === jLast, options));
|
||||
aEnd += 1;
|
||||
bEnd += 1;
|
||||
};
|
||||
const pushDeleteLine = (line) => {
|
||||
const j = lines.length;
|
||||
lines.push(printDeleteLine(line, j === 0 || j === jLast, options));
|
||||
aEnd += 1;
|
||||
};
|
||||
const pushInsertLine = (line) => {
|
||||
const j = lines.length;
|
||||
lines.push(printInsertLine(line, j === 0 || j === jLast, options));
|
||||
bEnd += 1;
|
||||
};
|
||||
i = 0;
|
||||
while (i !== iLength) {
|
||||
let iStart = i;
|
||||
while (i !== iLength && diffs[i][0] === DIFF_EQUAL)
|
||||
i += 1;
|
||||
if (iStart !== i) {
|
||||
if (iStart === 0) {
|
||||
if (i > nContextLines) {
|
||||
iStart = i - nContextLines;
|
||||
aStart = iStart;
|
||||
bStart = iStart;
|
||||
aEnd = aStart;
|
||||
bEnd = bStart;
|
||||
}
|
||||
for (let iCommon = iStart; iCommon !== i; iCommon += 1)
|
||||
pushCommonLine(diffs[iCommon][1]);
|
||||
} else if (i === iLength) {
|
||||
const iEnd = i - iStart > nContextLines ? iStart + nContextLines : i;
|
||||
for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1)
|
||||
pushCommonLine(diffs[iCommon][1]);
|
||||
} else {
|
||||
const nCommon = i - iStart;
|
||||
if (nCommon > nContextLines2) {
|
||||
const iEnd = iStart + nContextLines;
|
||||
for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1)
|
||||
pushCommonLine(diffs[iCommon][1]);
|
||||
lines[jPatchMark] = createPatchMark(
|
||||
aStart,
|
||||
aEnd,
|
||||
bStart,
|
||||
bEnd,
|
||||
options
|
||||
);
|
||||
jPatchMark = lines.length;
|
||||
lines.push("");
|
||||
const nOmit = nCommon - nContextLines2;
|
||||
aStart = aEnd + nOmit;
|
||||
bStart = bEnd + nOmit;
|
||||
aEnd = aStart;
|
||||
bEnd = bStart;
|
||||
for (let iCommon = i - nContextLines; iCommon !== i; iCommon += 1)
|
||||
pushCommonLine(diffs[iCommon][1]);
|
||||
} else {
|
||||
for (let iCommon = iStart; iCommon !== i; iCommon += 1)
|
||||
pushCommonLine(diffs[iCommon][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i !== iLength && diffs[i][0] === DIFF_DELETE) {
|
||||
pushDeleteLine(diffs[i][1]);
|
||||
i += 1;
|
||||
}
|
||||
while (i !== iLength && diffs[i][0] === DIFF_INSERT) {
|
||||
pushInsertLine(diffs[i][1]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if (hasPatch)
|
||||
lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options);
|
||||
return lines.join("\n");
|
||||
}
|
||||
function joinAlignedDiffsExpand(diffs, options) {
|
||||
return diffs.map((diff, i, diffs2) => {
|
||||
const line = diff[1];
|
||||
const isFirstOrLast = i === 0 || i === diffs2.length - 1;
|
||||
switch (diff[0]) {
|
||||
case DIFF_DELETE:
|
||||
return printDeleteLine(line, isFirstOrLast, options);
|
||||
case DIFF_INSERT:
|
||||
return printInsertLine(line, isFirstOrLast, options);
|
||||
default:
|
||||
return printCommonLine(line, isFirstOrLast, options);
|
||||
}
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
const noColor = (string) => string;
|
||||
const DIFF_CONTEXT_DEFAULT = 5;
|
||||
const DIFF_TRUNCATE_THRESHOLD_DEFAULT = 0;
|
||||
function getDefaultOptions() {
|
||||
const c = getColors();
|
||||
return {
|
||||
aAnnotation: "Expected",
|
||||
aColor: c.green,
|
||||
aIndicator: "-",
|
||||
bAnnotation: "Received",
|
||||
bColor: c.red,
|
||||
bIndicator: "+",
|
||||
changeColor: c.inverse,
|
||||
changeLineTrailingSpaceColor: noColor,
|
||||
commonColor: c.dim,
|
||||
commonIndicator: " ",
|
||||
commonLineTrailingSpaceColor: noColor,
|
||||
compareKeys: void 0,
|
||||
contextLines: DIFF_CONTEXT_DEFAULT,
|
||||
emptyFirstOrLastLinePlaceholder: "",
|
||||
expand: true,
|
||||
includeChangeCounts: false,
|
||||
omitAnnotationLines: false,
|
||||
patchColor: c.yellow,
|
||||
truncateThreshold: DIFF_TRUNCATE_THRESHOLD_DEFAULT,
|
||||
truncateAnnotation: "... Diff result is truncated",
|
||||
truncateAnnotationColor: noColor
|
||||
};
|
||||
}
|
||||
function getCompareKeys(compareKeys) {
|
||||
return compareKeys && typeof compareKeys === "function" ? compareKeys : void 0;
|
||||
}
|
||||
function getContextLines(contextLines) {
|
||||
return typeof contextLines === "number" && Number.isSafeInteger(contextLines) && contextLines >= 0 ? contextLines : DIFF_CONTEXT_DEFAULT;
|
||||
}
|
||||
function normalizeDiffOptions(options = {}) {
|
||||
return {
|
||||
...getDefaultOptions(),
|
||||
...options,
|
||||
compareKeys: getCompareKeys(options.compareKeys),
|
||||
contextLines: getContextLines(options.contextLines)
|
||||
};
|
||||
}
|
||||
|
||||
function isEmptyString(lines) {
|
||||
return lines.length === 1 && lines[0].length === 0;
|
||||
}
|
||||
function countChanges(diffs) {
|
||||
let a = 0;
|
||||
let b = 0;
|
||||
diffs.forEach((diff2) => {
|
||||
switch (diff2[0]) {
|
||||
case DIFF_DELETE:
|
||||
a += 1;
|
||||
break;
|
||||
case DIFF_INSERT:
|
||||
b += 1;
|
||||
break;
|
||||
}
|
||||
});
|
||||
return { a, b };
|
||||
}
|
||||
function printAnnotation({
|
||||
aAnnotation,
|
||||
aColor,
|
||||
aIndicator,
|
||||
bAnnotation,
|
||||
bColor,
|
||||
bIndicator,
|
||||
includeChangeCounts,
|
||||
omitAnnotationLines
|
||||
}, changeCounts) {
|
||||
if (omitAnnotationLines)
|
||||
return "";
|
||||
let aRest = "";
|
||||
let bRest = "";
|
||||
if (includeChangeCounts) {
|
||||
const aCount = String(changeCounts.a);
|
||||
const bCount = String(changeCounts.b);
|
||||
const baAnnotationLengthDiff = bAnnotation.length - aAnnotation.length;
|
||||
const aAnnotationPadding = " ".repeat(Math.max(0, baAnnotationLengthDiff));
|
||||
const bAnnotationPadding = " ".repeat(Math.max(0, -baAnnotationLengthDiff));
|
||||
const baCountLengthDiff = bCount.length - aCount.length;
|
||||
const aCountPadding = " ".repeat(Math.max(0, baCountLengthDiff));
|
||||
const bCountPadding = " ".repeat(Math.max(0, -baCountLengthDiff));
|
||||
aRest = `${aAnnotationPadding} ${aIndicator} ${aCountPadding}${aCount}`;
|
||||
bRest = `${bAnnotationPadding} ${bIndicator} ${bCountPadding}${bCount}`;
|
||||
}
|
||||
const a = `${aIndicator} ${aAnnotation}${aRest}`;
|
||||
const b = `${bIndicator} ${bAnnotation}${bRest}`;
|
||||
return `${aColor(a)}
|
||||
${bColor(b)}
|
||||
|
||||
`;
|
||||
}
|
||||
function printDiffLines(diffs, truncated, options) {
|
||||
return printAnnotation(options, countChanges(diffs)) + (options.expand ? joinAlignedDiffsExpand(diffs, options) : joinAlignedDiffsNoExpand(diffs, options)) + (truncated ? options.truncateAnnotationColor(`
|
||||
${options.truncateAnnotation}`) : "");
|
||||
}
|
||||
function diffLinesUnified(aLines, bLines, options) {
|
||||
const normalizedOptions = normalizeDiffOptions(options);
|
||||
const [diffs, truncated] = diffLinesRaw(
|
||||
isEmptyString(aLines) ? [] : aLines,
|
||||
isEmptyString(bLines) ? [] : bLines,
|
||||
normalizedOptions
|
||||
);
|
||||
return printDiffLines(
|
||||
diffs,
|
||||
truncated,
|
||||
normalizedOptions
|
||||
);
|
||||
}
|
||||
function diffLinesUnified2(aLinesDisplay, bLinesDisplay, aLinesCompare, bLinesCompare, options) {
|
||||
if (isEmptyString(aLinesDisplay) && isEmptyString(aLinesCompare)) {
|
||||
aLinesDisplay = [];
|
||||
aLinesCompare = [];
|
||||
}
|
||||
if (isEmptyString(bLinesDisplay) && isEmptyString(bLinesCompare)) {
|
||||
bLinesDisplay = [];
|
||||
bLinesCompare = [];
|
||||
}
|
||||
if (aLinesDisplay.length !== aLinesCompare.length || bLinesDisplay.length !== bLinesCompare.length) {
|
||||
return diffLinesUnified(aLinesDisplay, bLinesDisplay, options);
|
||||
}
|
||||
const [diffs, truncated] = diffLinesRaw(aLinesCompare, bLinesCompare, options);
|
||||
let aIndex = 0;
|
||||
let bIndex = 0;
|
||||
diffs.forEach((diff2) => {
|
||||
switch (diff2[0]) {
|
||||
case DIFF_DELETE:
|
||||
diff2[1] = aLinesDisplay[aIndex];
|
||||
aIndex += 1;
|
||||
break;
|
||||
case DIFF_INSERT:
|
||||
diff2[1] = bLinesDisplay[bIndex];
|
||||
bIndex += 1;
|
||||
break;
|
||||
default:
|
||||
diff2[1] = bLinesDisplay[bIndex];
|
||||
aIndex += 1;
|
||||
bIndex += 1;
|
||||
}
|
||||
});
|
||||
return printDiffLines(diffs, truncated, normalizeDiffOptions(options));
|
||||
}
|
||||
function diffLinesRaw(aLines, bLines, options) {
|
||||
const truncate = (options == null ? void 0 : options.truncateThreshold) ?? false;
|
||||
const truncateThreshold = Math.max(Math.floor((options == null ? void 0 : options.truncateThreshold) ?? 0), 0);
|
||||
const aLength = truncate ? Math.min(aLines.length, truncateThreshold) : aLines.length;
|
||||
const bLength = truncate ? Math.min(bLines.length, truncateThreshold) : bLines.length;
|
||||
const truncated = aLength !== aLines.length || bLength !== bLines.length;
|
||||
const isCommon = (aIndex2, bIndex2) => aLines[aIndex2] === bLines[bIndex2];
|
||||
const diffs = [];
|
||||
let aIndex = 0;
|
||||
let bIndex = 0;
|
||||
const foundSubsequence = (nCommon, aCommon, bCommon) => {
|
||||
for (; aIndex !== aCommon; aIndex += 1)
|
||||
diffs.push(new Diff(DIFF_DELETE, aLines[aIndex]));
|
||||
for (; bIndex !== bCommon; bIndex += 1)
|
||||
diffs.push(new Diff(DIFF_INSERT, bLines[bIndex]));
|
||||
for (; nCommon !== 0; nCommon -= 1, aIndex += 1, bIndex += 1)
|
||||
diffs.push(new Diff(DIFF_EQUAL, bLines[bIndex]));
|
||||
};
|
||||
const diffSequences = diff$1.default.default || diff$1.default;
|
||||
diffSequences(aLength, bLength, isCommon, foundSubsequence);
|
||||
for (; aIndex !== aLength; aIndex += 1)
|
||||
diffs.push(new Diff(DIFF_DELETE, aLines[aIndex]));
|
||||
for (; bIndex !== bLength; bIndex += 1)
|
||||
diffs.push(new Diff(DIFF_INSERT, bLines[bIndex]));
|
||||
return [diffs, truncated];
|
||||
}
|
||||
|
||||
function getNewLineSymbol(string) {
|
||||
return string.includes("\r\n") ? "\r\n" : "\n";
|
||||
}
|
||||
function diffStrings(a, b, options) {
|
||||
const truncate = (options == null ? void 0 : options.truncateThreshold) ?? false;
|
||||
const truncateThreshold = Math.max(Math.floor((options == null ? void 0 : options.truncateThreshold) ?? 0), 0);
|
||||
let aLength = a.length;
|
||||
let bLength = b.length;
|
||||
if (truncate) {
|
||||
const aMultipleLines = a.includes("\n");
|
||||
const bMultipleLines = b.includes("\n");
|
||||
const aNewLineSymbol = getNewLineSymbol(a);
|
||||
const bNewLineSymbol = getNewLineSymbol(b);
|
||||
const _a = aMultipleLines ? `${a.split(aNewLineSymbol, truncateThreshold).join(aNewLineSymbol)}
|
||||
` : a;
|
||||
const _b = bMultipleLines ? `${b.split(bNewLineSymbol, truncateThreshold).join(bNewLineSymbol)}
|
||||
` : b;
|
||||
aLength = _a.length;
|
||||
bLength = _b.length;
|
||||
}
|
||||
const truncated = aLength !== a.length || bLength !== b.length;
|
||||
const isCommon = (aIndex2, bIndex2) => a[aIndex2] === b[bIndex2];
|
||||
let aIndex = 0;
|
||||
let bIndex = 0;
|
||||
const diffs = [];
|
||||
const foundSubsequence = (nCommon, aCommon, bCommon) => {
|
||||
if (aIndex !== aCommon)
|
||||
diffs.push(new Diff(DIFF_DELETE, a.slice(aIndex, aCommon)));
|
||||
if (bIndex !== bCommon)
|
||||
diffs.push(new Diff(DIFF_INSERT, b.slice(bIndex, bCommon)));
|
||||
aIndex = aCommon + nCommon;
|
||||
bIndex = bCommon + nCommon;
|
||||
diffs.push(new Diff(DIFF_EQUAL, b.slice(bCommon, bIndex)));
|
||||
};
|
||||
const diffSequences = diff$1.default.default || diff$1.default;
|
||||
diffSequences(aLength, bLength, isCommon, foundSubsequence);
|
||||
if (aIndex !== aLength)
|
||||
diffs.push(new Diff(DIFF_DELETE, a.slice(aIndex)));
|
||||
if (bIndex !== bLength)
|
||||
diffs.push(new Diff(DIFF_INSERT, b.slice(bIndex)));
|
||||
return [diffs, truncated];
|
||||
}
|
||||
|
||||
function concatenateRelevantDiffs(op, diffs, changeColor) {
|
||||
return diffs.reduce(
|
||||
(reduced, diff) => reduced + (diff[0] === DIFF_EQUAL ? diff[1] : diff[0] === op && diff[1].length !== 0 ? changeColor(diff[1]) : ""),
|
||||
""
|
||||
);
|
||||
}
|
||||
class ChangeBuffer {
|
||||
op;
|
||||
line;
|
||||
// incomplete line
|
||||
lines;
|
||||
// complete lines
|
||||
changeColor;
|
||||
constructor(op, changeColor) {
|
||||
this.op = op;
|
||||
this.line = [];
|
||||
this.lines = [];
|
||||
this.changeColor = changeColor;
|
||||
}
|
||||
pushSubstring(substring) {
|
||||
this.pushDiff(new Diff(this.op, substring));
|
||||
}
|
||||
pushLine() {
|
||||
this.lines.push(
|
||||
this.line.length !== 1 ? new Diff(
|
||||
this.op,
|
||||
concatenateRelevantDiffs(this.op, this.line, this.changeColor)
|
||||
) : this.line[0][0] === this.op ? this.line[0] : new Diff(this.op, this.line[0][1])
|
||||
// was common diff
|
||||
);
|
||||
this.line.length = 0;
|
||||
}
|
||||
isLineEmpty() {
|
||||
return this.line.length === 0;
|
||||
}
|
||||
// Minor input to buffer.
|
||||
pushDiff(diff) {
|
||||
this.line.push(diff);
|
||||
}
|
||||
// Main input to buffer.
|
||||
align(diff) {
|
||||
const string = diff[1];
|
||||
if (string.includes("\n")) {
|
||||
const substrings = string.split("\n");
|
||||
const iLast = substrings.length - 1;
|
||||
substrings.forEach((substring, i) => {
|
||||
if (i < iLast) {
|
||||
this.pushSubstring(substring);
|
||||
this.pushLine();
|
||||
} else if (substring.length !== 0) {
|
||||
this.pushSubstring(substring);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.pushDiff(diff);
|
||||
}
|
||||
}
|
||||
// Output from buffer.
|
||||
moveLinesTo(lines) {
|
||||
if (!this.isLineEmpty())
|
||||
this.pushLine();
|
||||
lines.push(...this.lines);
|
||||
this.lines.length = 0;
|
||||
}
|
||||
}
|
||||
class CommonBuffer {
|
||||
deleteBuffer;
|
||||
insertBuffer;
|
||||
lines;
|
||||
constructor(deleteBuffer, insertBuffer) {
|
||||
this.deleteBuffer = deleteBuffer;
|
||||
this.insertBuffer = insertBuffer;
|
||||
this.lines = [];
|
||||
}
|
||||
pushDiffCommonLine(diff) {
|
||||
this.lines.push(diff);
|
||||
}
|
||||
pushDiffChangeLines(diff) {
|
||||
const isDiffEmpty = diff[1].length === 0;
|
||||
if (!isDiffEmpty || this.deleteBuffer.isLineEmpty())
|
||||
this.deleteBuffer.pushDiff(diff);
|
||||
if (!isDiffEmpty || this.insertBuffer.isLineEmpty())
|
||||
this.insertBuffer.pushDiff(diff);
|
||||
}
|
||||
flushChangeLines() {
|
||||
this.deleteBuffer.moveLinesTo(this.lines);
|
||||
this.insertBuffer.moveLinesTo(this.lines);
|
||||
}
|
||||
// Input to buffer.
|
||||
align(diff) {
|
||||
const op = diff[0];
|
||||
const string = diff[1];
|
||||
if (string.includes("\n")) {
|
||||
const substrings = string.split("\n");
|
||||
const iLast = substrings.length - 1;
|
||||
substrings.forEach((substring, i) => {
|
||||
if (i === 0) {
|
||||
const subdiff = new Diff(op, substring);
|
||||
if (this.deleteBuffer.isLineEmpty() && this.insertBuffer.isLineEmpty()) {
|
||||
this.flushChangeLines();
|
||||
this.pushDiffCommonLine(subdiff);
|
||||
} else {
|
||||
this.pushDiffChangeLines(subdiff);
|
||||
this.flushChangeLines();
|
||||
}
|
||||
} else if (i < iLast) {
|
||||
this.pushDiffCommonLine(new Diff(op, substring));
|
||||
} else if (substring.length !== 0) {
|
||||
this.pushDiffChangeLines(new Diff(op, substring));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.pushDiffChangeLines(diff);
|
||||
}
|
||||
}
|
||||
// Output from buffer.
|
||||
getLines() {
|
||||
this.flushChangeLines();
|
||||
return this.lines;
|
||||
}
|
||||
}
|
||||
function getAlignedDiffs(diffs, changeColor) {
|
||||
const deleteBuffer = new ChangeBuffer(DIFF_DELETE, changeColor);
|
||||
const insertBuffer = new ChangeBuffer(DIFF_INSERT, changeColor);
|
||||
const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer);
|
||||
diffs.forEach((diff) => {
|
||||
switch (diff[0]) {
|
||||
case DIFF_DELETE:
|
||||
deleteBuffer.align(diff);
|
||||
break;
|
||||
case DIFF_INSERT:
|
||||
insertBuffer.align(diff);
|
||||
break;
|
||||
default:
|
||||
commonBuffer.align(diff);
|
||||
}
|
||||
});
|
||||
return commonBuffer.getLines();
|
||||
}
|
||||
|
||||
function hasCommonDiff(diffs, isMultiline) {
|
||||
if (isMultiline) {
|
||||
const iLast = diffs.length - 1;
|
||||
return diffs.some(
|
||||
(diff, i) => diff[0] === DIFF_EQUAL && (i !== iLast || diff[1] !== "\n")
|
||||
);
|
||||
}
|
||||
return diffs.some((diff) => diff[0] === DIFF_EQUAL);
|
||||
}
|
||||
function diffStringsUnified(a, b, options) {
|
||||
if (a !== b && a.length !== 0 && b.length !== 0) {
|
||||
const isMultiline = a.includes("\n") || b.includes("\n");
|
||||
const [diffs, truncated] = diffStringsRaw(
|
||||
isMultiline ? `${a}
|
||||
` : a,
|
||||
isMultiline ? `${b}
|
||||
` : b,
|
||||
true,
|
||||
// cleanupSemantic
|
||||
options
|
||||
);
|
||||
if (hasCommonDiff(diffs, isMultiline)) {
|
||||
const optionsNormalized = normalizeDiffOptions(options);
|
||||
const lines = getAlignedDiffs(diffs, optionsNormalized.changeColor);
|
||||
return printDiffLines(lines, truncated, optionsNormalized);
|
||||
}
|
||||
}
|
||||
return diffLinesUnified(a.split("\n"), b.split("\n"), options);
|
||||
}
|
||||
function diffStringsRaw(a, b, cleanup, options) {
|
||||
const [diffs, truncated] = diffStrings(a, b, options);
|
||||
if (cleanup)
|
||||
diff_cleanupSemantic(diffs);
|
||||
return [diffs, truncated];
|
||||
}
|
||||
|
||||
function getCommonMessage(message, options) {
|
||||
const { commonColor } = normalizeDiffOptions(options);
|
||||
return commonColor(message);
|
||||
}
|
||||
const {
|
||||
AsymmetricMatcher,
|
||||
DOMCollection,
|
||||
DOMElement,
|
||||
Immutable,
|
||||
ReactElement,
|
||||
ReactTestComponent
|
||||
} = plugins;
|
||||
const PLUGINS = [
|
||||
ReactTestComponent,
|
||||
ReactElement,
|
||||
DOMElement,
|
||||
DOMCollection,
|
||||
Immutable,
|
||||
AsymmetricMatcher
|
||||
];
|
||||
const FORMAT_OPTIONS = {
|
||||
plugins: PLUGINS
|
||||
};
|
||||
const FALLBACK_FORMAT_OPTIONS = {
|
||||
callToJSON: false,
|
||||
maxDepth: 10,
|
||||
plugins: PLUGINS
|
||||
};
|
||||
function diff(a, b, options) {
|
||||
if (Object.is(a, b))
|
||||
return "";
|
||||
const aType = getType(a);
|
||||
let expectedType = aType;
|
||||
let omitDifference = false;
|
||||
if (aType === "object" && typeof a.asymmetricMatch === "function") {
|
||||
if (a.$$typeof !== Symbol.for("jest.asymmetricMatcher")) {
|
||||
return null;
|
||||
}
|
||||
if (typeof a.getExpectedType !== "function") {
|
||||
return null;
|
||||
}
|
||||
expectedType = a.getExpectedType();
|
||||
omitDifference = expectedType === "string";
|
||||
}
|
||||
if (expectedType !== getType(b)) {
|
||||
const { aAnnotation, aColor, aIndicator, bAnnotation, bColor, bIndicator } = normalizeDiffOptions(options);
|
||||
const formatOptions = getFormatOptions(FALLBACK_FORMAT_OPTIONS, options);
|
||||
const aDisplay = format(a, formatOptions);
|
||||
const bDisplay = format(b, formatOptions);
|
||||
const aDiff = `${aColor(`${aIndicator} ${aAnnotation}:`)}
|
||||
${aDisplay}`;
|
||||
const bDiff = `${bColor(`${bIndicator} ${bAnnotation}:`)}
|
||||
${bDisplay}`;
|
||||
return `${aDiff}
|
||||
|
||||
${bDiff}`;
|
||||
}
|
||||
if (omitDifference)
|
||||
return null;
|
||||
switch (aType) {
|
||||
case "string":
|
||||
return diffLinesUnified(a.split("\n"), b.split("\n"), options);
|
||||
case "boolean":
|
||||
case "number":
|
||||
return comparePrimitive(a, b, options);
|
||||
case "map":
|
||||
return compareObjects(sortMap(a), sortMap(b), options);
|
||||
case "set":
|
||||
return compareObjects(sortSet(a), sortSet(b), options);
|
||||
default:
|
||||
return compareObjects(a, b, options);
|
||||
}
|
||||
}
|
||||
function comparePrimitive(a, b, options) {
|
||||
const aFormat = format(a, FORMAT_OPTIONS);
|
||||
const bFormat = format(b, FORMAT_OPTIONS);
|
||||
return aFormat === bFormat ? "" : diffLinesUnified(aFormat.split("\n"), bFormat.split("\n"), options);
|
||||
}
|
||||
function sortMap(map) {
|
||||
return new Map(Array.from(map.entries()).sort());
|
||||
}
|
||||
function sortSet(set) {
|
||||
return new Set(Array.from(set.values()).sort());
|
||||
}
|
||||
function compareObjects(a, b, options) {
|
||||
let difference;
|
||||
let hasThrown = false;
|
||||
try {
|
||||
const formatOptions = getFormatOptions(FORMAT_OPTIONS, options);
|
||||
difference = getObjectsDifference(a, b, formatOptions, options);
|
||||
} catch {
|
||||
hasThrown = true;
|
||||
}
|
||||
const noDiffMessage = getCommonMessage(NO_DIFF_MESSAGE, options);
|
||||
if (difference === void 0 || difference === noDiffMessage) {
|
||||
const formatOptions = getFormatOptions(FALLBACK_FORMAT_OPTIONS, options);
|
||||
difference = getObjectsDifference(a, b, formatOptions, options);
|
||||
if (difference !== noDiffMessage && !hasThrown) {
|
||||
difference = `${getCommonMessage(
|
||||
SIMILAR_MESSAGE,
|
||||
options
|
||||
)}
|
||||
|
||||
${difference}`;
|
||||
}
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
function getFormatOptions(formatOptions, options) {
|
||||
const { compareKeys } = normalizeDiffOptions(options);
|
||||
return {
|
||||
...formatOptions,
|
||||
compareKeys
|
||||
};
|
||||
}
|
||||
function getObjectsDifference(a, b, formatOptions, options) {
|
||||
const formatOptionsZeroIndent = { ...formatOptions, indent: 0 };
|
||||
const aCompare = format(a, formatOptionsZeroIndent);
|
||||
const bCompare = format(b, formatOptionsZeroIndent);
|
||||
if (aCompare === bCompare) {
|
||||
return getCommonMessage(NO_DIFF_MESSAGE, options);
|
||||
} else {
|
||||
const aDisplay = format(a, formatOptions);
|
||||
const bDisplay = format(b, formatOptions);
|
||||
return diffLinesUnified2(
|
||||
aDisplay.split("\n"),
|
||||
bDisplay.split("\n"),
|
||||
aCompare.split("\n"),
|
||||
bCompare.split("\n"),
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff, diff, diffLinesRaw, diffLinesUnified, diffLinesUnified2, diffStringsRaw, diffStringsUnified };
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { D as DiffOptions } from './types-9l4niLY8.js';
|
||||
import 'pretty-format';
|
||||
|
||||
declare function serializeError(val: any, seen?: WeakMap<WeakKey, any>): any;
|
||||
declare function processError(err: any, diffOptions?: DiffOptions): any;
|
||||
declare function replaceAsymmetricMatcher(actual: any, expected: any, actualReplaced?: WeakSet<WeakKey>, expectedReplaced?: WeakSet<WeakKey>): {
|
||||
replacedActual: any;
|
||||
replacedExpected: any;
|
||||
};
|
||||
|
||||
export { processError, replaceAsymmetricMatcher, serializeError };
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
import { diff } from './diff.js';
|
||||
import { f as format, s as stringify } from './chunk-display.js';
|
||||
import { deepClone, getOwnProperties, getType } from './helpers.js';
|
||||
import 'pretty-format';
|
||||
import 'diff-sequences';
|
||||
import './chunk-colors.js';
|
||||
import 'loupe';
|
||||
|
||||
const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@";
|
||||
const IS_COLLECTION_SYMBOL = "@@__IMMUTABLE_ITERABLE__@@";
|
||||
function isImmutable(v) {
|
||||
return v && (v[IS_COLLECTION_SYMBOL] || v[IS_RECORD_SYMBOL]);
|
||||
}
|
||||
const OBJECT_PROTO = Object.getPrototypeOf({});
|
||||
function getUnserializableMessage(err) {
|
||||
if (err instanceof Error)
|
||||
return `<unserializable>: ${err.message}`;
|
||||
if (typeof err === "string")
|
||||
return `<unserializable>: ${err}`;
|
||||
return "<unserializable>";
|
||||
}
|
||||
function serializeError(val, seen = /* @__PURE__ */ new WeakMap()) {
|
||||
if (!val || typeof val === "string")
|
||||
return val;
|
||||
if (typeof val === "function")
|
||||
return `Function<${val.name || "anonymous"}>`;
|
||||
if (typeof val === "symbol")
|
||||
return val.toString();
|
||||
if (typeof val !== "object")
|
||||
return val;
|
||||
if (isImmutable(val))
|
||||
return serializeError(val.toJSON(), seen);
|
||||
if (val instanceof Promise || val.constructor && val.constructor.prototype === "AsyncFunction")
|
||||
return "Promise";
|
||||
if (typeof Element !== "undefined" && val instanceof Element)
|
||||
return val.tagName;
|
||||
if (typeof val.asymmetricMatch === "function")
|
||||
return `${val.toString()} ${format(val.sample)}`;
|
||||
if (typeof val.toJSON === "function")
|
||||
return val.toJSON();
|
||||
if (seen.has(val))
|
||||
return seen.get(val);
|
||||
if (Array.isArray(val)) {
|
||||
const clone = new Array(val.length);
|
||||
seen.set(val, clone);
|
||||
val.forEach((e, i) => {
|
||||
try {
|
||||
clone[i] = serializeError(e, seen);
|
||||
} catch (err) {
|
||||
clone[i] = getUnserializableMessage(err);
|
||||
}
|
||||
});
|
||||
return clone;
|
||||
} else {
|
||||
const clone = /* @__PURE__ */ Object.create(null);
|
||||
seen.set(val, clone);
|
||||
let obj = val;
|
||||
while (obj && obj !== OBJECT_PROTO) {
|
||||
Object.getOwnPropertyNames(obj).forEach((key) => {
|
||||
if (key in clone)
|
||||
return;
|
||||
try {
|
||||
clone[key] = serializeError(val[key], seen);
|
||||
} catch (err) {
|
||||
delete clone[key];
|
||||
clone[key] = getUnserializableMessage(err);
|
||||
}
|
||||
});
|
||||
obj = Object.getPrototypeOf(obj);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
function normalizeErrorMessage(message) {
|
||||
return message.replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "");
|
||||
}
|
||||
function processError(err, diffOptions) {
|
||||
if (!err || typeof err !== "object")
|
||||
return { message: err };
|
||||
if (err.stack)
|
||||
err.stackStr = String(err.stack);
|
||||
if (err.name)
|
||||
err.nameStr = String(err.name);
|
||||
if (err.showDiff || err.showDiff === void 0 && err.expected !== void 0 && err.actual !== void 0) {
|
||||
const clonedActual = deepClone(err.actual, { forceWritable: true });
|
||||
const clonedExpected = deepClone(err.expected, { forceWritable: true });
|
||||
const { replacedActual, replacedExpected } = replaceAsymmetricMatcher(clonedActual, clonedExpected);
|
||||
err.diff = diff(replacedExpected, replacedActual, { ...diffOptions, ...err.diffOptions });
|
||||
}
|
||||
if (typeof err.expected !== "string")
|
||||
err.expected = stringify(err.expected, 10);
|
||||
if (typeof err.actual !== "string")
|
||||
err.actual = stringify(err.actual, 10);
|
||||
try {
|
||||
if (typeof err.message === "string")
|
||||
err.message = normalizeErrorMessage(err.message);
|
||||
if (typeof err.cause === "object" && typeof err.cause.message === "string")
|
||||
err.cause.message = normalizeErrorMessage(err.cause.message);
|
||||
} catch {
|
||||
}
|
||||
try {
|
||||
return serializeError(err);
|
||||
} catch (e) {
|
||||
return serializeError(new Error(`Failed to fully serialize error: ${e == null ? void 0 : e.message}
|
||||
Inner error message: ${err == null ? void 0 : err.message}`));
|
||||
}
|
||||
}
|
||||
function isAsymmetricMatcher(data) {
|
||||
const type = getType(data);
|
||||
return type === "Object" && typeof data.asymmetricMatch === "function";
|
||||
}
|
||||
function isReplaceable(obj1, obj2) {
|
||||
const obj1Type = getType(obj1);
|
||||
const obj2Type = getType(obj2);
|
||||
return obj1Type === obj2Type && (obj1Type === "Object" || obj1Type === "Array");
|
||||
}
|
||||
function replaceAsymmetricMatcher(actual, expected, actualReplaced = /* @__PURE__ */ new WeakSet(), expectedReplaced = /* @__PURE__ */ new WeakSet()) {
|
||||
if (!isReplaceable(actual, expected))
|
||||
return { replacedActual: actual, replacedExpected: expected };
|
||||
if (actualReplaced.has(actual) || expectedReplaced.has(expected))
|
||||
return { replacedActual: actual, replacedExpected: expected };
|
||||
actualReplaced.add(actual);
|
||||
expectedReplaced.add(expected);
|
||||
getOwnProperties(expected).forEach((key) => {
|
||||
const expectedValue = expected[key];
|
||||
const actualValue = actual[key];
|
||||
if (isAsymmetricMatcher(expectedValue)) {
|
||||
if (expectedValue.asymmetricMatch(actualValue))
|
||||
actual[key] = expectedValue;
|
||||
} else if (isAsymmetricMatcher(actualValue)) {
|
||||
if (actualValue.asymmetricMatch(expectedValue))
|
||||
expected[key] = actualValue;
|
||||
} else if (isReplaceable(actualValue, expectedValue)) {
|
||||
const replaced = replaceAsymmetricMatcher(
|
||||
actualValue,
|
||||
expectedValue,
|
||||
actualReplaced,
|
||||
expectedReplaced
|
||||
);
|
||||
actual[key] = replaced.replacedActual;
|
||||
expected[key] = replaced.replacedExpected;
|
||||
}
|
||||
});
|
||||
return {
|
||||
replacedActual: actual,
|
||||
replacedExpected: expected
|
||||
};
|
||||
}
|
||||
|
||||
export { processError, replaceAsymmetricMatcher, serializeError };
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { Nullable, Arrayable } from './types.js';
|
||||
|
||||
interface CloneOptions {
|
||||
forceWritable?: boolean;
|
||||
}
|
||||
declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
|
||||
declare function assertTypes(value: unknown, name: string, types: string[]): void;
|
||||
declare function isPrimitive(value: unknown): boolean;
|
||||
declare function slash(path: string): string;
|
||||
declare function parseRegexp(input: string): RegExp;
|
||||
declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
|
||||
declare function isObject(item: unknown): boolean;
|
||||
declare function getType(value: unknown): string;
|
||||
declare function getOwnProperties(obj: any): (string | symbol)[];
|
||||
declare function deepClone<T>(val: T, options?: CloneOptions): T;
|
||||
declare function clone<T>(val: T, seen: WeakMap<any, any>, options?: CloneOptions): T;
|
||||
declare function noop(): void;
|
||||
declare function objectAttr(source: any, path: string, defaultValue?: undefined): any;
|
||||
type DeferPromise<T> = Promise<T> & {
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
};
|
||||
declare function createDefer<T>(): DeferPromise<T>;
|
||||
/**
|
||||
* If code starts with a function call, will return its last index, respecting arguments.
|
||||
* This will return 25 - last ending character of toMatch ")"
|
||||
* Also works with callbacks
|
||||
* ```
|
||||
* toMatch({ test: '123' });
|
||||
* toBeAliased('123')
|
||||
* ```
|
||||
*/
|
||||
declare function getCallLastIndex(code: string): number | null;
|
||||
|
||||
export { type DeferPromise, assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray };
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
function notNullish(v) {
|
||||
return v != null;
|
||||
}
|
||||
function assertTypes(value, name, types) {
|
||||
const receivedType = typeof value;
|
||||
const pass = types.includes(receivedType);
|
||||
if (!pass)
|
||||
throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`);
|
||||
}
|
||||
function isPrimitive(value) {
|
||||
return value === null || typeof value !== "function" && typeof value !== "object";
|
||||
}
|
||||
function slash(path) {
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
function parseRegexp(input) {
|
||||
const m = input.match(/(\/?)(.+)\1([a-z]*)/i);
|
||||
if (!m)
|
||||
return /$^/;
|
||||
if (m[3] && !/^(?!.*?(.).*?\1)[gmixXsuUAJ]+$/.test(m[3]))
|
||||
return RegExp(input);
|
||||
return new RegExp(m[2], m[3]);
|
||||
}
|
||||
function toArray(array) {
|
||||
if (array === null || array === void 0)
|
||||
array = [];
|
||||
if (Array.isArray(array))
|
||||
return array;
|
||||
return [array];
|
||||
}
|
||||
function isObject(item) {
|
||||
return item != null && typeof item === "object" && !Array.isArray(item);
|
||||
}
|
||||
function isFinalObj(obj) {
|
||||
return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype;
|
||||
}
|
||||
function getType(value) {
|
||||
return Object.prototype.toString.apply(value).slice(8, -1);
|
||||
}
|
||||
function collectOwnProperties(obj, collector) {
|
||||
const collect = typeof collector === "function" ? collector : (key) => collector.add(key);
|
||||
Object.getOwnPropertyNames(obj).forEach(collect);
|
||||
Object.getOwnPropertySymbols(obj).forEach(collect);
|
||||
}
|
||||
function getOwnProperties(obj) {
|
||||
const ownProps = /* @__PURE__ */ new Set();
|
||||
if (isFinalObj(obj))
|
||||
return [];
|
||||
collectOwnProperties(obj, ownProps);
|
||||
return Array.from(ownProps);
|
||||
}
|
||||
const defaultCloneOptions = { forceWritable: false };
|
||||
function deepClone(val, options = defaultCloneOptions) {
|
||||
const seen = /* @__PURE__ */ new WeakMap();
|
||||
return clone(val, seen, options);
|
||||
}
|
||||
function clone(val, seen, options = defaultCloneOptions) {
|
||||
let k, out;
|
||||
if (seen.has(val))
|
||||
return seen.get(val);
|
||||
if (Array.isArray(val)) {
|
||||
out = Array(k = val.length);
|
||||
seen.set(val, out);
|
||||
while (k--)
|
||||
out[k] = clone(val[k], seen, options);
|
||||
return out;
|
||||
}
|
||||
if (Object.prototype.toString.call(val) === "[object Object]") {
|
||||
out = Object.create(Object.getPrototypeOf(val));
|
||||
seen.set(val, out);
|
||||
const props = getOwnProperties(val);
|
||||
for (const k2 of props) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(val, k2);
|
||||
if (!descriptor)
|
||||
continue;
|
||||
const cloned = clone(val[k2], seen, options);
|
||||
if (options.forceWritable) {
|
||||
Object.defineProperty(out, k2, {
|
||||
enumerable: descriptor.enumerable,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: cloned
|
||||
});
|
||||
} else if ("get" in descriptor) {
|
||||
Object.defineProperty(out, k2, {
|
||||
...descriptor,
|
||||
get() {
|
||||
return cloned;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(out, k2, {
|
||||
...descriptor,
|
||||
value: cloned
|
||||
});
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
function noop() {
|
||||
}
|
||||
function objectAttr(source, path, defaultValue = void 0) {
|
||||
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
||||
let result = source;
|
||||
for (const p of paths) {
|
||||
result = Object(result)[p];
|
||||
if (result === void 0)
|
||||
return defaultValue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function createDefer() {
|
||||
let resolve = null;
|
||||
let reject = null;
|
||||
const p = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
p.resolve = resolve;
|
||||
p.reject = reject;
|
||||
return p;
|
||||
}
|
||||
function getCallLastIndex(code) {
|
||||
let charIndex = -1;
|
||||
let inString = null;
|
||||
let startedBracers = 0;
|
||||
let endedBracers = 0;
|
||||
let beforeChar = null;
|
||||
while (charIndex <= code.length) {
|
||||
beforeChar = code[charIndex];
|
||||
charIndex++;
|
||||
const char = code[charIndex];
|
||||
const isCharString = char === '"' || char === "'" || char === "`";
|
||||
if (isCharString && beforeChar !== "\\") {
|
||||
if (inString === char)
|
||||
inString = null;
|
||||
else if (!inString)
|
||||
inString = char;
|
||||
}
|
||||
if (!inString) {
|
||||
if (char === "(")
|
||||
startedBracers++;
|
||||
if (char === ")")
|
||||
endedBracers++;
|
||||
}
|
||||
if (startedBracers && endedBracers && startedBracers === endedBracers)
|
||||
return charIndex;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray };
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
export { DeferPromise, assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
|
||||
export { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, ErrorWithDiff, MergeInsertions, MutableArray, Nullable, ParsedStack } from './types.js';
|
||||
import { PrettyFormatOptions } from 'pretty-format';
|
||||
|
||||
declare function stringify(object: unknown, maxDepth?: number, { maxLength, ...options }?: PrettyFormatOptions & {
|
||||
maxLength?: number;
|
||||
}): string;
|
||||
|
||||
declare function getSafeTimers(): {
|
||||
nextTick: any;
|
||||
setTimeout: any;
|
||||
setInterval: any;
|
||||
clearInterval: any;
|
||||
clearTimeout: any;
|
||||
setImmediate: any;
|
||||
clearImmediate: any;
|
||||
};
|
||||
declare function setSafeTimers(): void;
|
||||
|
||||
declare function shuffle<T>(array: T[], seed?: number): T[];
|
||||
|
||||
interface LoupeOptions {
|
||||
showHidden?: boolean | undefined;
|
||||
depth?: number | null | undefined;
|
||||
colors?: boolean | undefined;
|
||||
customInspect?: boolean | undefined;
|
||||
showProxy?: boolean | undefined;
|
||||
maxArrayLength?: number | null | undefined;
|
||||
maxStringLength?: number | null | undefined;
|
||||
breakLength?: number | undefined;
|
||||
compact?: boolean | number | undefined;
|
||||
sorted?: boolean | ((a: string, b: string) => number) | undefined;
|
||||
getters?: 'get' | 'set' | boolean | undefined;
|
||||
numericSeparator?: boolean | undefined;
|
||||
truncate?: number;
|
||||
}
|
||||
declare function format(...args: unknown[]): string;
|
||||
declare function inspect(obj: unknown, options?: LoupeOptions): string;
|
||||
declare function objDisplay(obj: unknown, options?: LoupeOptions): string;
|
||||
|
||||
declare const SAFE_TIMERS_SYMBOL: unique symbol;
|
||||
declare const SAFE_COLORS_SYMBOL: unique symbol;
|
||||
|
||||
declare const colorsMap: {
|
||||
readonly bold: readonly ["\u001B[1m", "\u001B[22m", "\u001B[22m\u001B[1m"];
|
||||
readonly dim: readonly ["\u001B[2m", "\u001B[22m", "\u001B[22m\u001B[2m"];
|
||||
readonly italic: readonly ["\u001B[3m", "\u001B[23m"];
|
||||
readonly underline: readonly ["\u001B[4m", "\u001B[24m"];
|
||||
readonly inverse: readonly ["\u001B[7m", "\u001B[27m"];
|
||||
readonly hidden: readonly ["\u001B[8m", "\u001B[28m"];
|
||||
readonly strikethrough: readonly ["\u001B[9m", "\u001B[29m"];
|
||||
readonly black: readonly ["\u001B[30m", "\u001B[39m"];
|
||||
readonly red: readonly ["\u001B[31m", "\u001B[39m"];
|
||||
readonly green: readonly ["\u001B[32m", "\u001B[39m"];
|
||||
readonly yellow: readonly ["\u001B[33m", "\u001B[39m"];
|
||||
readonly blue: readonly ["\u001B[34m", "\u001B[39m"];
|
||||
readonly magenta: readonly ["\u001B[35m", "\u001B[39m"];
|
||||
readonly cyan: readonly ["\u001B[36m", "\u001B[39m"];
|
||||
readonly white: readonly ["\u001B[37m", "\u001B[39m"];
|
||||
readonly gray: readonly ["\u001B[90m", "\u001B[39m"];
|
||||
readonly bgBlack: readonly ["\u001B[40m", "\u001B[49m"];
|
||||
readonly bgRed: readonly ["\u001B[41m", "\u001B[49m"];
|
||||
readonly bgGreen: readonly ["\u001B[42m", "\u001B[49m"];
|
||||
readonly bgYellow: readonly ["\u001B[43m", "\u001B[49m"];
|
||||
readonly bgBlue: readonly ["\u001B[44m", "\u001B[49m"];
|
||||
readonly bgMagenta: readonly ["\u001B[45m", "\u001B[49m"];
|
||||
readonly bgCyan: readonly ["\u001B[46m", "\u001B[49m"];
|
||||
readonly bgWhite: readonly ["\u001B[47m", "\u001B[49m"];
|
||||
};
|
||||
type ColorName = keyof typeof colorsMap;
|
||||
interface ColorMethod {
|
||||
(input: unknown): string;
|
||||
open: string;
|
||||
close: string;
|
||||
}
|
||||
type ColorsMethods = {
|
||||
[Key in ColorName]: ColorMethod;
|
||||
};
|
||||
type Colors$1 = ColorsMethods & {
|
||||
isColorSupported: boolean;
|
||||
reset: (input: unknown) => string;
|
||||
};
|
||||
declare function getDefaultColors(): Colors$1;
|
||||
declare function getColors(): Colors$1;
|
||||
declare function createColors(isTTY?: boolean): Colors$1;
|
||||
declare function setupColors(colors: Colors$1): void;
|
||||
|
||||
interface ErrorOptions {
|
||||
message?: string;
|
||||
stackTraceLimit?: number;
|
||||
}
|
||||
/**
|
||||
* Get original stacktrace without source map support the most performant way.
|
||||
* - Create only 1 stack frame.
|
||||
* - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms).
|
||||
*/
|
||||
declare function createSimpleStackTrace(options?: ErrorOptions): string;
|
||||
|
||||
declare const lineSplitRE: RegExp;
|
||||
declare function positionToOffset(source: string, lineNumber: number, columnNumber: number): number;
|
||||
declare function offsetToLineNumber(source: string, offset: number): number;
|
||||
|
||||
type Colors = Record<ColorName, (input: string) => string>;
|
||||
interface HighlightOptions {
|
||||
jsx?: boolean;
|
||||
colors?: Colors;
|
||||
}
|
||||
declare function highlight(code: string, options?: HighlightOptions): string;
|
||||
|
||||
export { type ColorMethod, type ColorName, type Colors$1 as Colors, type ColorsMethods, SAFE_COLORS_SYMBOL, SAFE_TIMERS_SYMBOL, createColors, createSimpleStackTrace, format, getColors, getDefaultColors, getSafeTimers, highlight, inspect, lineSplitRE, objDisplay, offsetToLineNumber, positionToOffset, setSafeTimers, setupColors, shuffle, stringify };
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
export { assertTypes, clone, createDefer, deepClone, getCallLastIndex, getOwnProperties, getType, isObject, isPrimitive, noop, notNullish, objectAttr, parseRegexp, slash, toArray } from './helpers.js';
|
||||
export { f as format, i as inspect, o as objDisplay, s as stringify } from './chunk-display.js';
|
||||
import { S as SAFE_TIMERS_SYMBOL, g as getColors } from './chunk-colors.js';
|
||||
export { a as SAFE_COLORS_SYMBOL, c as createColors, b as getDefaultColors, s as setupColors } from './chunk-colors.js';
|
||||
import 'pretty-format';
|
||||
import 'loupe';
|
||||
|
||||
function getSafeTimers() {
|
||||
const {
|
||||
setTimeout: safeSetTimeout,
|
||||
setInterval: safeSetInterval,
|
||||
clearInterval: safeClearInterval,
|
||||
clearTimeout: safeClearTimeout,
|
||||
setImmediate: safeSetImmediate,
|
||||
clearImmediate: safeClearImmediate
|
||||
} = globalThis[SAFE_TIMERS_SYMBOL] || globalThis;
|
||||
const {
|
||||
nextTick: safeNextTick
|
||||
} = globalThis[SAFE_TIMERS_SYMBOL] || globalThis.process || { nextTick: (cb) => cb() };
|
||||
return {
|
||||
nextTick: safeNextTick,
|
||||
setTimeout: safeSetTimeout,
|
||||
setInterval: safeSetInterval,
|
||||
clearInterval: safeClearInterval,
|
||||
clearTimeout: safeClearTimeout,
|
||||
setImmediate: safeSetImmediate,
|
||||
clearImmediate: safeClearImmediate
|
||||
};
|
||||
}
|
||||
function setSafeTimers() {
|
||||
const {
|
||||
setTimeout: safeSetTimeout,
|
||||
setInterval: safeSetInterval,
|
||||
clearInterval: safeClearInterval,
|
||||
clearTimeout: safeClearTimeout,
|
||||
setImmediate: safeSetImmediate,
|
||||
clearImmediate: safeClearImmediate
|
||||
} = globalThis;
|
||||
const {
|
||||
nextTick: safeNextTick
|
||||
} = globalThis.process || { nextTick: (cb) => cb() };
|
||||
const timers = {
|
||||
nextTick: safeNextTick,
|
||||
setTimeout: safeSetTimeout,
|
||||
setInterval: safeSetInterval,
|
||||
clearInterval: safeClearInterval,
|
||||
clearTimeout: safeClearTimeout,
|
||||
setImmediate: safeSetImmediate,
|
||||
clearImmediate: safeClearImmediate
|
||||
};
|
||||
globalThis[SAFE_TIMERS_SYMBOL] = timers;
|
||||
}
|
||||
|
||||
const RealDate = Date;
|
||||
function random(seed) {
|
||||
const x = Math.sin(seed++) * 1e4;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
function shuffle(array, seed = RealDate.now()) {
|
||||
let length = array.length;
|
||||
while (length) {
|
||||
const index = Math.floor(random(seed) * length--);
|
||||
const previous = array[length];
|
||||
array[length] = array[index];
|
||||
array[index] = previous;
|
||||
++seed;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function createSimpleStackTrace(options) {
|
||||
const { message = "error", stackTraceLimit = 1 } = options || {};
|
||||
const limit = Error.stackTraceLimit;
|
||||
const prepareStackTrace = Error.prepareStackTrace;
|
||||
Error.stackTraceLimit = stackTraceLimit;
|
||||
Error.prepareStackTrace = (e) => e.stack;
|
||||
const err = new Error(message);
|
||||
const stackTrace = err.stack || "";
|
||||
Error.prepareStackTrace = prepareStackTrace;
|
||||
Error.stackTraceLimit = limit;
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
const lineSplitRE = /\r?\n/;
|
||||
function positionToOffset(source, lineNumber, columnNumber) {
|
||||
const lines = source.split(lineSplitRE);
|
||||
const nl = /\r\n/.test(source) ? 2 : 1;
|
||||
let start = 0;
|
||||
if (lineNumber > lines.length)
|
||||
return source.length;
|
||||
for (let i = 0; i < lineNumber - 1; i++)
|
||||
start += lines[i].length + nl;
|
||||
return start + columnNumber;
|
||||
}
|
||||
function offsetToLineNumber(source, offset) {
|
||||
if (offset > source.length) {
|
||||
throw new Error(
|
||||
`offset is longer than source length! offset ${offset} > length ${source.length}`
|
||||
);
|
||||
}
|
||||
const lines = source.split(lineSplitRE);
|
||||
const nl = /\r\n/.test(source) ? 2 : 1;
|
||||
let counted = 0;
|
||||
let line = 0;
|
||||
for (; line < lines.length; line++) {
|
||||
const lineLength = lines[line].length + nl;
|
||||
if (counted + lineLength >= offset)
|
||||
break;
|
||||
counted += lineLength;
|
||||
}
|
||||
return line + 1;
|
||||
}
|
||||
|
||||
function getDefaultExportFromCjs (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
// Copyright 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Simon Lydell
|
||||
// License: MIT.
|
||||
var Identifier, JSXIdentifier, JSXPunctuator, JSXString, JSXText, KeywordsWithExpressionAfter, KeywordsWithNoLineTerminatorAfter, LineTerminatorSequence, MultiLineComment, Newline, NumericLiteral, Punctuator, RegularExpressionLiteral, SingleLineComment, StringLiteral, Template, TokensNotPrecedingObjectLiteral, TokensPrecedingExpression, WhiteSpace;
|
||||
RegularExpressionLiteral = /\/(?![*\/])(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\\]).|\\.)*(\/[$_\u200C\u200D\p{ID_Continue}]*|\\)?/yu;
|
||||
Punctuator = /--|\+\+|=>|\.{3}|\??\.(?!\d)|(?:&&|\|\||\?\?|[+\-%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2}|\/(?![\/*]))=?|[?~,:;[\](){}]/y;
|
||||
Identifier = /(\x23?)(?=[$_\p{ID_Start}\\])(?:[$_\u200C\u200D\p{ID_Continue}]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+/yu;
|
||||
StringLiteral = /(['"])(?:(?!\1)[^\\\n\r]|\\(?:\r\n|[^]))*(\1)?/y;
|
||||
NumericLiteral = /(?:0[xX][\da-fA-F](?:_?[\da-fA-F])*|0[oO][0-7](?:_?[0-7])*|0[bB][01](?:_?[01])*)n?|0n|[1-9](?:_?\d)*n|(?:(?:0(?!\d)|0\d*[89]\d*|[1-9](?:_?\d)*)(?:\.(?:\d(?:_?\d)*)?)?|\.\d(?:_?\d)*)(?:[eE][+-]?\d(?:_?\d)*)?|0[0-7]+/y;
|
||||
Template = /[`}](?:[^`\\$]|\\[^]|\$(?!\{))*(`|\$\{)?/y;
|
||||
WhiteSpace = /[\t\v\f\ufeff\p{Zs}]+/yu;
|
||||
LineTerminatorSequence = /\r?\n|[\r\u2028\u2029]/y;
|
||||
MultiLineComment = /\/\*(?:[^*]|\*(?!\/))*(\*\/)?/y;
|
||||
SingleLineComment = /\/\/.*/y;
|
||||
JSXPunctuator = /[<>.:={}]|\/(?![\/*])/y;
|
||||
JSXIdentifier = /[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}-]*/yu;
|
||||
JSXString = /(['"])(?:(?!\1)[^])*(\1)?/y;
|
||||
JSXText = /[^<>{}]+/y;
|
||||
TokensPrecedingExpression = /^(?:[\/+-]|\.{3}|\?(?:InterpolationIn(?:JSX|Template)|NoLineTerminatorHere|NonExpressionParenEnd|UnaryIncDec))?$|[{}([,;<>=*%&|^!~?:]$/;
|
||||
TokensNotPrecedingObjectLiteral = /^(?:=>|[;\]){}]|else|\?(?:NoLineTerminatorHere|NonExpressionParenEnd))?$/;
|
||||
KeywordsWithExpressionAfter = /^(?:await|case|default|delete|do|else|instanceof|new|return|throw|typeof|void|yield)$/;
|
||||
KeywordsWithNoLineTerminatorAfter = /^(?:return|throw|yield)$/;
|
||||
Newline = RegExp(LineTerminatorSequence.source);
|
||||
var jsTokens_1 = function*(input, {jsx = false} = {}) {
|
||||
var braces, firstCodePoint, isExpression, lastIndex, lastSignificantToken, length, match, mode, nextLastIndex, nextLastSignificantToken, parenNesting, postfixIncDec, punctuator, stack;
|
||||
({length} = input);
|
||||
lastIndex = 0;
|
||||
lastSignificantToken = "";
|
||||
stack = [
|
||||
{tag: "JS"}
|
||||
];
|
||||
braces = [];
|
||||
parenNesting = 0;
|
||||
postfixIncDec = false;
|
||||
while (lastIndex < length) {
|
||||
mode = stack[stack.length - 1];
|
||||
switch (mode.tag) {
|
||||
case "JS":
|
||||
case "JSNonExpressionParen":
|
||||
case "InterpolationInTemplate":
|
||||
case "InterpolationInJSX":
|
||||
if (input[lastIndex] === "/" && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken))) {
|
||||
RegularExpressionLiteral.lastIndex = lastIndex;
|
||||
if (match = RegularExpressionLiteral.exec(input)) {
|
||||
lastIndex = RegularExpressionLiteral.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
postfixIncDec = true;
|
||||
yield ({
|
||||
type: "RegularExpressionLiteral",
|
||||
value: match[0],
|
||||
closed: match[1] !== void 0 && match[1] !== "\\"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Punctuator.lastIndex = lastIndex;
|
||||
if (match = Punctuator.exec(input)) {
|
||||
punctuator = match[0];
|
||||
nextLastIndex = Punctuator.lastIndex;
|
||||
nextLastSignificantToken = punctuator;
|
||||
switch (punctuator) {
|
||||
case "(":
|
||||
if (lastSignificantToken === "?NonExpressionParenKeyword") {
|
||||
stack.push({
|
||||
tag: "JSNonExpressionParen",
|
||||
nesting: parenNesting
|
||||
});
|
||||
}
|
||||
parenNesting++;
|
||||
postfixIncDec = false;
|
||||
break;
|
||||
case ")":
|
||||
parenNesting--;
|
||||
postfixIncDec = true;
|
||||
if (mode.tag === "JSNonExpressionParen" && parenNesting === mode.nesting) {
|
||||
stack.pop();
|
||||
nextLastSignificantToken = "?NonExpressionParenEnd";
|
||||
postfixIncDec = false;
|
||||
}
|
||||
break;
|
||||
case "{":
|
||||
Punctuator.lastIndex = 0;
|
||||
isExpression = !TokensNotPrecedingObjectLiteral.test(lastSignificantToken) && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken));
|
||||
braces.push(isExpression);
|
||||
postfixIncDec = false;
|
||||
break;
|
||||
case "}":
|
||||
switch (mode.tag) {
|
||||
case "InterpolationInTemplate":
|
||||
if (braces.length === mode.nesting) {
|
||||
Template.lastIndex = lastIndex;
|
||||
match = Template.exec(input);
|
||||
lastIndex = Template.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
if (match[1] === "${") {
|
||||
lastSignificantToken = "?InterpolationInTemplate";
|
||||
postfixIncDec = false;
|
||||
yield ({
|
||||
type: "TemplateMiddle",
|
||||
value: match[0]
|
||||
});
|
||||
} else {
|
||||
stack.pop();
|
||||
postfixIncDec = true;
|
||||
yield ({
|
||||
type: "TemplateTail",
|
||||
value: match[0],
|
||||
closed: match[1] === "`"
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case "InterpolationInJSX":
|
||||
if (braces.length === mode.nesting) {
|
||||
stack.pop();
|
||||
lastIndex += 1;
|
||||
lastSignificantToken = "}";
|
||||
yield ({
|
||||
type: "JSXPunctuator",
|
||||
value: "}"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
postfixIncDec = braces.pop();
|
||||
nextLastSignificantToken = postfixIncDec ? "?ExpressionBraceEnd" : "}";
|
||||
break;
|
||||
case "]":
|
||||
postfixIncDec = true;
|
||||
break;
|
||||
case "++":
|
||||
case "--":
|
||||
nextLastSignificantToken = postfixIncDec ? "?PostfixIncDec" : "?UnaryIncDec";
|
||||
break;
|
||||
case "<":
|
||||
if (jsx && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken))) {
|
||||
stack.push({tag: "JSXTag"});
|
||||
lastIndex += 1;
|
||||
lastSignificantToken = "<";
|
||||
yield ({
|
||||
type: "JSXPunctuator",
|
||||
value: punctuator
|
||||
});
|
||||
continue;
|
||||
}
|
||||
postfixIncDec = false;
|
||||
break;
|
||||
default:
|
||||
postfixIncDec = false;
|
||||
}
|
||||
lastIndex = nextLastIndex;
|
||||
lastSignificantToken = nextLastSignificantToken;
|
||||
yield ({
|
||||
type: "Punctuator",
|
||||
value: punctuator
|
||||
});
|
||||
continue;
|
||||
}
|
||||
Identifier.lastIndex = lastIndex;
|
||||
if (match = Identifier.exec(input)) {
|
||||
lastIndex = Identifier.lastIndex;
|
||||
nextLastSignificantToken = match[0];
|
||||
switch (match[0]) {
|
||||
case "for":
|
||||
case "if":
|
||||
case "while":
|
||||
case "with":
|
||||
if (lastSignificantToken !== "." && lastSignificantToken !== "?.") {
|
||||
nextLastSignificantToken = "?NonExpressionParenKeyword";
|
||||
}
|
||||
}
|
||||
lastSignificantToken = nextLastSignificantToken;
|
||||
postfixIncDec = !KeywordsWithExpressionAfter.test(match[0]);
|
||||
yield ({
|
||||
type: match[1] === "#" ? "PrivateIdentifier" : "IdentifierName",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
StringLiteral.lastIndex = lastIndex;
|
||||
if (match = StringLiteral.exec(input)) {
|
||||
lastIndex = StringLiteral.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
postfixIncDec = true;
|
||||
yield ({
|
||||
type: "StringLiteral",
|
||||
value: match[0],
|
||||
closed: match[2] !== void 0
|
||||
});
|
||||
continue;
|
||||
}
|
||||
NumericLiteral.lastIndex = lastIndex;
|
||||
if (match = NumericLiteral.exec(input)) {
|
||||
lastIndex = NumericLiteral.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
postfixIncDec = true;
|
||||
yield ({
|
||||
type: "NumericLiteral",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
Template.lastIndex = lastIndex;
|
||||
if (match = Template.exec(input)) {
|
||||
lastIndex = Template.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
if (match[1] === "${") {
|
||||
lastSignificantToken = "?InterpolationInTemplate";
|
||||
stack.push({
|
||||
tag: "InterpolationInTemplate",
|
||||
nesting: braces.length
|
||||
});
|
||||
postfixIncDec = false;
|
||||
yield ({
|
||||
type: "TemplateHead",
|
||||
value: match[0]
|
||||
});
|
||||
} else {
|
||||
postfixIncDec = true;
|
||||
yield ({
|
||||
type: "NoSubstitutionTemplate",
|
||||
value: match[0],
|
||||
closed: match[1] === "`"
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case "JSXTag":
|
||||
case "JSXTagEnd":
|
||||
JSXPunctuator.lastIndex = lastIndex;
|
||||
if (match = JSXPunctuator.exec(input)) {
|
||||
lastIndex = JSXPunctuator.lastIndex;
|
||||
nextLastSignificantToken = match[0];
|
||||
switch (match[0]) {
|
||||
case "<":
|
||||
stack.push({tag: "JSXTag"});
|
||||
break;
|
||||
case ">":
|
||||
stack.pop();
|
||||
if (lastSignificantToken === "/" || mode.tag === "JSXTagEnd") {
|
||||
nextLastSignificantToken = "?JSX";
|
||||
postfixIncDec = true;
|
||||
} else {
|
||||
stack.push({tag: "JSXChildren"});
|
||||
}
|
||||
break;
|
||||
case "{":
|
||||
stack.push({
|
||||
tag: "InterpolationInJSX",
|
||||
nesting: braces.length
|
||||
});
|
||||
nextLastSignificantToken = "?InterpolationInJSX";
|
||||
postfixIncDec = false;
|
||||
break;
|
||||
case "/":
|
||||
if (lastSignificantToken === "<") {
|
||||
stack.pop();
|
||||
if (stack[stack.length - 1].tag === "JSXChildren") {
|
||||
stack.pop();
|
||||
}
|
||||
stack.push({tag: "JSXTagEnd"});
|
||||
}
|
||||
}
|
||||
lastSignificantToken = nextLastSignificantToken;
|
||||
yield ({
|
||||
type: "JSXPunctuator",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
JSXIdentifier.lastIndex = lastIndex;
|
||||
if (match = JSXIdentifier.exec(input)) {
|
||||
lastIndex = JSXIdentifier.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
yield ({
|
||||
type: "JSXIdentifier",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
JSXString.lastIndex = lastIndex;
|
||||
if (match = JSXString.exec(input)) {
|
||||
lastIndex = JSXString.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
yield ({
|
||||
type: "JSXString",
|
||||
value: match[0],
|
||||
closed: match[2] !== void 0
|
||||
});
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case "JSXChildren":
|
||||
JSXText.lastIndex = lastIndex;
|
||||
if (match = JSXText.exec(input)) {
|
||||
lastIndex = JSXText.lastIndex;
|
||||
lastSignificantToken = match[0];
|
||||
yield ({
|
||||
type: "JSXText",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
switch (input[lastIndex]) {
|
||||
case "<":
|
||||
stack.push({tag: "JSXTag"});
|
||||
lastIndex++;
|
||||
lastSignificantToken = "<";
|
||||
yield ({
|
||||
type: "JSXPunctuator",
|
||||
value: "<"
|
||||
});
|
||||
continue;
|
||||
case "{":
|
||||
stack.push({
|
||||
tag: "InterpolationInJSX",
|
||||
nesting: braces.length
|
||||
});
|
||||
lastIndex++;
|
||||
lastSignificantToken = "?InterpolationInJSX";
|
||||
postfixIncDec = false;
|
||||
yield ({
|
||||
type: "JSXPunctuator",
|
||||
value: "{"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
WhiteSpace.lastIndex = lastIndex;
|
||||
if (match = WhiteSpace.exec(input)) {
|
||||
lastIndex = WhiteSpace.lastIndex;
|
||||
yield ({
|
||||
type: "WhiteSpace",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
LineTerminatorSequence.lastIndex = lastIndex;
|
||||
if (match = LineTerminatorSequence.exec(input)) {
|
||||
lastIndex = LineTerminatorSequence.lastIndex;
|
||||
postfixIncDec = false;
|
||||
if (KeywordsWithNoLineTerminatorAfter.test(lastSignificantToken)) {
|
||||
lastSignificantToken = "?NoLineTerminatorHere";
|
||||
}
|
||||
yield ({
|
||||
type: "LineTerminatorSequence",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
MultiLineComment.lastIndex = lastIndex;
|
||||
if (match = MultiLineComment.exec(input)) {
|
||||
lastIndex = MultiLineComment.lastIndex;
|
||||
if (Newline.test(match[0])) {
|
||||
postfixIncDec = false;
|
||||
if (KeywordsWithNoLineTerminatorAfter.test(lastSignificantToken)) {
|
||||
lastSignificantToken = "?NoLineTerminatorHere";
|
||||
}
|
||||
}
|
||||
yield ({
|
||||
type: "MultiLineComment",
|
||||
value: match[0],
|
||||
closed: match[1] !== void 0
|
||||
});
|
||||
continue;
|
||||
}
|
||||
SingleLineComment.lastIndex = lastIndex;
|
||||
if (match = SingleLineComment.exec(input)) {
|
||||
lastIndex = SingleLineComment.lastIndex;
|
||||
postfixIncDec = false;
|
||||
yield ({
|
||||
type: "SingleLineComment",
|
||||
value: match[0]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
firstCodePoint = String.fromCodePoint(input.codePointAt(lastIndex));
|
||||
lastIndex += firstCodePoint.length;
|
||||
lastSignificantToken = firstCodePoint;
|
||||
postfixIncDec = false;
|
||||
yield ({
|
||||
type: mode.tag.startsWith("JSX") ? "JSXInvalid" : "Invalid",
|
||||
value: firstCodePoint
|
||||
});
|
||||
}
|
||||
return void 0;
|
||||
};
|
||||
|
||||
var jsTokens = /*@__PURE__*/getDefaultExportFromCjs(jsTokens_1);
|
||||
|
||||
// src/index.ts
|
||||
var reservedWords = {
|
||||
keyword: [
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"do",
|
||||
"else",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"return",
|
||||
"switch",
|
||||
"throw",
|
||||
"try",
|
||||
"var",
|
||||
"const",
|
||||
"while",
|
||||
"with",
|
||||
"new",
|
||||
"this",
|
||||
"super",
|
||||
"class",
|
||||
"extends",
|
||||
"export",
|
||||
"import",
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"in",
|
||||
"instanceof",
|
||||
"typeof",
|
||||
"void",
|
||||
"delete"
|
||||
],
|
||||
strict: [
|
||||
"implements",
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield"
|
||||
]
|
||||
}, keywords = new Set(reservedWords.keyword), reservedWordsStrictSet = new Set(reservedWords.strict), sometimesKeywords = /* @__PURE__ */ new Set(["as", "async", "from", "get", "of", "set"]);
|
||||
function isReservedWord(word) {
|
||||
return word === "await" || word === "enum";
|
||||
}
|
||||
function isStrictReservedWord(word) {
|
||||
return isReservedWord(word) || reservedWordsStrictSet.has(word);
|
||||
}
|
||||
function isKeyword(word) {
|
||||
return keywords.has(word);
|
||||
}
|
||||
var BRACKET = /^[()[\]{}]$/, getTokenType = function(token) {
|
||||
if (token.type === "IdentifierName") {
|
||||
if (isKeyword(token.value) || isStrictReservedWord(token.value) || sometimesKeywords.has(token.value))
|
||||
return "Keyword";
|
||||
if (token.value[0] && token.value[0] !== token.value[0].toLowerCase())
|
||||
return "IdentifierCapitalized";
|
||||
}
|
||||
return token.type === "Punctuator" && BRACKET.test(token.value) ? "Bracket" : token.type === "Invalid" && (token.value === "@" || token.value === "#") ? "Punctuator" : token.type;
|
||||
};
|
||||
function getCallableType(token) {
|
||||
if (token.type === "IdentifierName")
|
||||
return "IdentifierCallable";
|
||||
if (token.type === "PrivateIdentifier")
|
||||
return "PrivateIdentifierCallable";
|
||||
throw new Error("Not a callable token");
|
||||
}
|
||||
var colorize = (defs, type, value) => {
|
||||
let colorize2 = defs[type];
|
||||
return colorize2 ? colorize2(value) : value;
|
||||
}, highlightTokens = (defs, text, jsx) => {
|
||||
let highlighted = "", lastPotentialCallable = null, stackedHighlight = "";
|
||||
for (let token of jsTokens(text, { jsx })) {
|
||||
let type = getTokenType(token);
|
||||
if (type === "IdentifierName" || type === "PrivateIdentifier") {
|
||||
lastPotentialCallable && (highlighted += colorize(defs, getTokenType(lastPotentialCallable), lastPotentialCallable.value) + stackedHighlight, stackedHighlight = ""), lastPotentialCallable = token;
|
||||
continue;
|
||||
}
|
||||
if (lastPotentialCallable && (token.type === "WhiteSpace" || token.type === "LineTerminatorSequence" || token.type === "Punctuator" && (token.value === "?." || token.value === "!"))) {
|
||||
stackedHighlight += colorize(defs, type, token.value);
|
||||
continue;
|
||||
}
|
||||
if (stackedHighlight && !lastPotentialCallable && (highlighted += stackedHighlight, stackedHighlight = ""), lastPotentialCallable) {
|
||||
let type2 = token.type === "Punctuator" && token.value === "(" ? getCallableType(lastPotentialCallable) : getTokenType(lastPotentialCallable);
|
||||
highlighted += colorize(defs, type2, lastPotentialCallable.value) + stackedHighlight, stackedHighlight = "", lastPotentialCallable = null;
|
||||
}
|
||||
highlighted += colorize(defs, type, token.value);
|
||||
}
|
||||
return highlighted;
|
||||
};
|
||||
function highlight$1(code, options = { jsx: !1, colors: {} }) {
|
||||
return code && highlightTokens(options.colors || {}, code, options.jsx);
|
||||
}
|
||||
|
||||
function getDefs(c) {
|
||||
const Invalid = (text) => c.white(c.bgRed(c.bold(text)));
|
||||
return {
|
||||
Keyword: c.magenta,
|
||||
IdentifierCapitalized: c.yellow,
|
||||
Punctuator: c.yellow,
|
||||
StringLiteral: c.green,
|
||||
NoSubstitutionTemplate: c.green,
|
||||
MultiLineComment: c.gray,
|
||||
SingleLineComment: c.gray,
|
||||
RegularExpressionLiteral: c.cyan,
|
||||
NumericLiteral: c.blue,
|
||||
TemplateHead: (text) => c.green(text.slice(0, text.length - 2)) + c.cyan(text.slice(-2)),
|
||||
TemplateTail: (text) => c.cyan(text.slice(0, 1)) + c.green(text.slice(1)),
|
||||
TemplateMiddle: (text) => c.cyan(text.slice(0, 1)) + c.green(text.slice(1, text.length - 2)) + c.cyan(text.slice(-2)),
|
||||
IdentifierCallable: c.blue,
|
||||
PrivateIdentifierCallable: (text) => `#${c.blue(text.slice(1))}`,
|
||||
Invalid,
|
||||
JSXString: c.green,
|
||||
JSXIdentifier: c.yellow,
|
||||
JSXInvalid: Invalid,
|
||||
JSXPunctuator: c.yellow
|
||||
};
|
||||
}
|
||||
function highlight(code, options = { jsx: false }) {
|
||||
return highlight$1(code, {
|
||||
jsx: options.jsx,
|
||||
colors: getDefs(options.colors || getColors())
|
||||
});
|
||||
}
|
||||
|
||||
export { SAFE_TIMERS_SYMBOL, createSimpleStackTrace, getColors, getSafeTimers, highlight, lineSplitRE, offsetToLineNumber, positionToOffset, setSafeTimers, shuffle };
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import { ParsedStack, ErrorWithDiff } from './types.js';
|
||||
|
||||
type GeneratedColumn = number;
|
||||
type SourcesIndex = number;
|
||||
type SourceLine = number;
|
||||
type SourceColumn = number;
|
||||
type NamesIndex = number;
|
||||
type SourceMapSegment = [GeneratedColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
|
||||
|
||||
interface SourceMapV3 {
|
||||
file?: string | null;
|
||||
names: string[];
|
||||
sourceRoot?: string;
|
||||
sources: (string | null)[];
|
||||
sourcesContent?: (string | null)[];
|
||||
version: 3;
|
||||
}
|
||||
interface EncodedSourceMap extends SourceMapV3 {
|
||||
mappings: string;
|
||||
}
|
||||
interface DecodedSourceMap extends SourceMapV3 {
|
||||
mappings: SourceMapSegment[][];
|
||||
}
|
||||
type OriginalMapping = {
|
||||
source: string | null;
|
||||
line: number;
|
||||
column: number;
|
||||
name: string | null;
|
||||
};
|
||||
type InvalidOriginalMapping = {
|
||||
source: null;
|
||||
line: null;
|
||||
column: null;
|
||||
name: null;
|
||||
};
|
||||
type GeneratedMapping = {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
type InvalidGeneratedMapping = {
|
||||
line: null;
|
||||
column: null;
|
||||
};
|
||||
type Bias = typeof GREATEST_LOWER_BOUND | typeof LEAST_UPPER_BOUND;
|
||||
type SourceMapInput = string | Ro<EncodedSourceMap> | Ro<DecodedSourceMap> | TraceMap;
|
||||
type Needle = {
|
||||
line: number;
|
||||
column: number;
|
||||
bias?: Bias;
|
||||
};
|
||||
type SourceNeedle = {
|
||||
source: string;
|
||||
line: number;
|
||||
column: number;
|
||||
bias?: Bias;
|
||||
};
|
||||
declare abstract class SourceMap {
|
||||
version: SourceMapV3['version'];
|
||||
file: SourceMapV3['file'];
|
||||
names: SourceMapV3['names'];
|
||||
sourceRoot: SourceMapV3['sourceRoot'];
|
||||
sources: SourceMapV3['sources'];
|
||||
sourcesContent: SourceMapV3['sourcesContent'];
|
||||
resolvedSources: SourceMapV3['sources'];
|
||||
}
|
||||
type Ro<T> = T extends Array<infer V> ? V[] | Readonly<V[]> | RoArray<V> | Readonly<RoArray<V>> : T extends object ? T | Readonly<T> | RoObject<T> | Readonly<RoObject<T>> : T;
|
||||
type RoArray<T> = Ro<T>[];
|
||||
type RoObject<T> = {
|
||||
[K in keyof T]: T[K] | Ro<T[K]>;
|
||||
};
|
||||
|
||||
declare const LEAST_UPPER_BOUND = -1;
|
||||
declare const GREATEST_LOWER_BOUND = 1;
|
||||
/**
|
||||
* A higher-level API to find the source/line/column associated with a generated line/column
|
||||
* (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in
|
||||
* `source-map` library.
|
||||
*/
|
||||
declare let originalPositionFor: (map: TraceMap, needle: Needle) => OriginalMapping | InvalidOriginalMapping;
|
||||
/**
|
||||
* Finds the generated line/column position of the provided source/line/column source position.
|
||||
*/
|
||||
declare let generatedPositionFor: (map: TraceMap, needle: SourceNeedle) => GeneratedMapping | InvalidGeneratedMapping;
|
||||
declare class TraceMap implements SourceMap {
|
||||
version: SourceMapV3['version'];
|
||||
file: SourceMapV3['file'];
|
||||
names: SourceMapV3['names'];
|
||||
sourceRoot: SourceMapV3['sourceRoot'];
|
||||
sources: SourceMapV3['sources'];
|
||||
sourcesContent: SourceMapV3['sourcesContent'];
|
||||
resolvedSources: string[];
|
||||
private _encoded;
|
||||
private _decoded;
|
||||
private _decodedMemo;
|
||||
private _bySources;
|
||||
private _bySourceMemos;
|
||||
constructor(map: SourceMapInput, mapUrl?: string | null);
|
||||
}
|
||||
|
||||
interface StackTraceParserOptions {
|
||||
ignoreStackEntries?: (RegExp | string)[];
|
||||
getSourceMap?: (file: string) => unknown;
|
||||
frameFilter?: (error: Error, frame: ParsedStack) => boolean | void;
|
||||
}
|
||||
declare function parseSingleFFOrSafariStack(raw: string): ParsedStack | null;
|
||||
declare function parseSingleStack(raw: string): ParsedStack | null;
|
||||
declare function parseSingleV8Stack(raw: string): ParsedStack | null;
|
||||
declare function parseStacktrace(stack: string, options?: StackTraceParserOptions): ParsedStack[];
|
||||
declare function parseErrorStacktrace(e: ErrorWithDiff, options?: StackTraceParserOptions): ParsedStack[];
|
||||
|
||||
export { type SourceMapInput, type StackTraceParserOptions, TraceMap, generatedPositionFor, originalPositionFor, parseErrorStacktrace, parseSingleFFOrSafariStack, parseSingleStack, parseSingleV8Stack, parseStacktrace };
|
||||
+878
@@ -0,0 +1,878 @@
|
||||
import { notNullish, isPrimitive } from './helpers.js';
|
||||
|
||||
function normalizeWindowsPath(input = "") {
|
||||
if (!input || !input.includes("\\")) {
|
||||
return input;
|
||||
}
|
||||
return input.replace(/\\/g, "/");
|
||||
}
|
||||
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
||||
function cwd() {
|
||||
if (typeof process !== "undefined") {
|
||||
return process.cwd().replace(/\\/g, "/");
|
||||
}
|
||||
return "/";
|
||||
}
|
||||
const resolve$2 = function(...arguments_) {
|
||||
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
||||
let resolvedPath = "";
|
||||
let resolvedAbsolute = false;
|
||||
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
||||
const path = index >= 0 ? arguments_[index] : cwd();
|
||||
if (!path || path.length === 0) {
|
||||
continue;
|
||||
}
|
||||
resolvedPath = `${path}/${resolvedPath}`;
|
||||
resolvedAbsolute = isAbsolute(path);
|
||||
}
|
||||
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
||||
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
|
||||
return `/${resolvedPath}`;
|
||||
}
|
||||
return resolvedPath.length > 0 ? resolvedPath : ".";
|
||||
};
|
||||
function normalizeString(path, allowAboveRoot) {
|
||||
let res = "";
|
||||
let lastSegmentLength = 0;
|
||||
let lastSlash = -1;
|
||||
let dots = 0;
|
||||
let char = null;
|
||||
for (let index = 0; index <= path.length; ++index) {
|
||||
if (index < path.length) {
|
||||
char = path[index];
|
||||
} else if (char === "/") {
|
||||
break;
|
||||
} else {
|
||||
char = "/";
|
||||
}
|
||||
if (char === "/") {
|
||||
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
|
||||
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
||||
if (res.length > 2) {
|
||||
const lastSlashIndex = res.lastIndexOf("/");
|
||||
if (lastSlashIndex === -1) {
|
||||
res = "";
|
||||
lastSegmentLength = 0;
|
||||
} else {
|
||||
res = res.slice(0, lastSlashIndex);
|
||||
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
||||
}
|
||||
lastSlash = index;
|
||||
dots = 0;
|
||||
continue;
|
||||
} else if (res.length > 0) {
|
||||
res = "";
|
||||
lastSegmentLength = 0;
|
||||
lastSlash = index;
|
||||
dots = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (allowAboveRoot) {
|
||||
res += res.length > 0 ? "/.." : "..";
|
||||
lastSegmentLength = 2;
|
||||
}
|
||||
} else {
|
||||
if (res.length > 0) {
|
||||
res += `/${path.slice(lastSlash + 1, index)}`;
|
||||
} else {
|
||||
res = path.slice(lastSlash + 1, index);
|
||||
}
|
||||
lastSegmentLength = index - lastSlash - 1;
|
||||
}
|
||||
lastSlash = index;
|
||||
dots = 0;
|
||||
} else if (char === "." && dots !== -1) {
|
||||
++dots;
|
||||
} else {
|
||||
dots = -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
const isAbsolute = function(p) {
|
||||
return _IS_ABSOLUTE_RE.test(p);
|
||||
};
|
||||
|
||||
const comma = ','.charCodeAt(0);
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
const intToChar = new Uint8Array(64); // 64 possible chars.
|
||||
const charToInt = new Uint8Array(128); // z is 122 in ASCII
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const c = chars.charCodeAt(i);
|
||||
intToChar[i] = c;
|
||||
charToInt[c] = i;
|
||||
}
|
||||
function decode(mappings) {
|
||||
const state = new Int32Array(5);
|
||||
const decoded = [];
|
||||
let index = 0;
|
||||
do {
|
||||
const semi = indexOf(mappings, index);
|
||||
const line = [];
|
||||
let sorted = true;
|
||||
let lastCol = 0;
|
||||
state[0] = 0;
|
||||
for (let i = index; i < semi; i++) {
|
||||
let seg;
|
||||
i = decodeInteger(mappings, i, state, 0); // genColumn
|
||||
const col = state[0];
|
||||
if (col < lastCol)
|
||||
sorted = false;
|
||||
lastCol = col;
|
||||
if (hasMoreVlq(mappings, i, semi)) {
|
||||
i = decodeInteger(mappings, i, state, 1); // sourcesIndex
|
||||
i = decodeInteger(mappings, i, state, 2); // sourceLine
|
||||
i = decodeInteger(mappings, i, state, 3); // sourceColumn
|
||||
if (hasMoreVlq(mappings, i, semi)) {
|
||||
i = decodeInteger(mappings, i, state, 4); // namesIndex
|
||||
seg = [col, state[1], state[2], state[3], state[4]];
|
||||
}
|
||||
else {
|
||||
seg = [col, state[1], state[2], state[3]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
seg = [col];
|
||||
}
|
||||
line.push(seg);
|
||||
}
|
||||
if (!sorted)
|
||||
sort(line);
|
||||
decoded.push(line);
|
||||
index = semi + 1;
|
||||
} while (index <= mappings.length);
|
||||
return decoded;
|
||||
}
|
||||
function indexOf(mappings, index) {
|
||||
const idx = mappings.indexOf(';', index);
|
||||
return idx === -1 ? mappings.length : idx;
|
||||
}
|
||||
function decodeInteger(mappings, pos, state, j) {
|
||||
let value = 0;
|
||||
let shift = 0;
|
||||
let integer = 0;
|
||||
do {
|
||||
const c = mappings.charCodeAt(pos++);
|
||||
integer = charToInt[c];
|
||||
value |= (integer & 31) << shift;
|
||||
shift += 5;
|
||||
} while (integer & 32);
|
||||
const shouldNegate = value & 1;
|
||||
value >>>= 1;
|
||||
if (shouldNegate) {
|
||||
value = -0x80000000 | -value;
|
||||
}
|
||||
state[j] += value;
|
||||
return pos;
|
||||
}
|
||||
function hasMoreVlq(mappings, i, length) {
|
||||
if (i >= length)
|
||||
return false;
|
||||
return mappings.charCodeAt(i) !== comma;
|
||||
}
|
||||
function sort(line) {
|
||||
line.sort(sortComparator$1);
|
||||
}
|
||||
function sortComparator$1(a, b) {
|
||||
return a[0] - b[0];
|
||||
}
|
||||
|
||||
// Matches the scheme of a URL, eg "http://"
|
||||
const schemeRegex = /^[\w+.-]+:\/\//;
|
||||
/**
|
||||
* Matches the parts of a URL:
|
||||
* 1. Scheme, including ":", guaranteed.
|
||||
* 2. User/password, including "@", optional.
|
||||
* 3. Host, guaranteed.
|
||||
* 4. Port, including ":", optional.
|
||||
* 5. Path, including "/", optional.
|
||||
* 6. Query, including "?", optional.
|
||||
* 7. Hash, including "#", optional.
|
||||
*/
|
||||
const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
|
||||
/**
|
||||
* File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
|
||||
* with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
|
||||
*
|
||||
* 1. Host, optional.
|
||||
* 2. Path, which may include "/", guaranteed.
|
||||
* 3. Query, including "?", optional.
|
||||
* 4. Hash, including "#", optional.
|
||||
*/
|
||||
const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
|
||||
var UrlType;
|
||||
(function (UrlType) {
|
||||
UrlType[UrlType["Empty"] = 1] = "Empty";
|
||||
UrlType[UrlType["Hash"] = 2] = "Hash";
|
||||
UrlType[UrlType["Query"] = 3] = "Query";
|
||||
UrlType[UrlType["RelativePath"] = 4] = "RelativePath";
|
||||
UrlType[UrlType["AbsolutePath"] = 5] = "AbsolutePath";
|
||||
UrlType[UrlType["SchemeRelative"] = 6] = "SchemeRelative";
|
||||
UrlType[UrlType["Absolute"] = 7] = "Absolute";
|
||||
})(UrlType || (UrlType = {}));
|
||||
function isAbsoluteUrl(input) {
|
||||
return schemeRegex.test(input);
|
||||
}
|
||||
function isSchemeRelativeUrl(input) {
|
||||
return input.startsWith('//');
|
||||
}
|
||||
function isAbsolutePath(input) {
|
||||
return input.startsWith('/');
|
||||
}
|
||||
function isFileUrl(input) {
|
||||
return input.startsWith('file:');
|
||||
}
|
||||
function isRelative(input) {
|
||||
return /^[.?#]/.test(input);
|
||||
}
|
||||
function parseAbsoluteUrl(input) {
|
||||
const match = urlRegex.exec(input);
|
||||
return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
|
||||
}
|
||||
function parseFileUrl(input) {
|
||||
const match = fileRegex.exec(input);
|
||||
const path = match[2];
|
||||
return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
|
||||
}
|
||||
function makeUrl(scheme, user, host, port, path, query, hash) {
|
||||
return {
|
||||
scheme,
|
||||
user,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
query,
|
||||
hash,
|
||||
type: UrlType.Absolute,
|
||||
};
|
||||
}
|
||||
function parseUrl(input) {
|
||||
if (isSchemeRelativeUrl(input)) {
|
||||
const url = parseAbsoluteUrl('http:' + input);
|
||||
url.scheme = '';
|
||||
url.type = UrlType.SchemeRelative;
|
||||
return url;
|
||||
}
|
||||
if (isAbsolutePath(input)) {
|
||||
const url = parseAbsoluteUrl('http://foo.com' + input);
|
||||
url.scheme = '';
|
||||
url.host = '';
|
||||
url.type = UrlType.AbsolutePath;
|
||||
return url;
|
||||
}
|
||||
if (isFileUrl(input))
|
||||
return parseFileUrl(input);
|
||||
if (isAbsoluteUrl(input))
|
||||
return parseAbsoluteUrl(input);
|
||||
const url = parseAbsoluteUrl('http://foo.com/' + input);
|
||||
url.scheme = '';
|
||||
url.host = '';
|
||||
url.type = input
|
||||
? input.startsWith('?')
|
||||
? UrlType.Query
|
||||
: input.startsWith('#')
|
||||
? UrlType.Hash
|
||||
: UrlType.RelativePath
|
||||
: UrlType.Empty;
|
||||
return url;
|
||||
}
|
||||
function stripPathFilename(path) {
|
||||
// If a path ends with a parent directory "..", then it's a relative path with excess parent
|
||||
// paths. It's not a file, so we can't strip it.
|
||||
if (path.endsWith('/..'))
|
||||
return path;
|
||||
const index = path.lastIndexOf('/');
|
||||
return path.slice(0, index + 1);
|
||||
}
|
||||
function mergePaths(url, base) {
|
||||
normalizePath(base, base.type);
|
||||
// If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
|
||||
// path).
|
||||
if (url.path === '/') {
|
||||
url.path = base.path;
|
||||
}
|
||||
else {
|
||||
// Resolution happens relative to the base path's directory, not the file.
|
||||
url.path = stripPathFilename(base.path) + url.path;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The path can have empty directories "//", unneeded parents "foo/..", or current directory
|
||||
* "foo/.". We need to normalize to a standard representation.
|
||||
*/
|
||||
function normalizePath(url, type) {
|
||||
const rel = type <= UrlType.RelativePath;
|
||||
const pieces = url.path.split('/');
|
||||
// We need to preserve the first piece always, so that we output a leading slash. The item at
|
||||
// pieces[0] is an empty string.
|
||||
let pointer = 1;
|
||||
// Positive is the number of real directories we've output, used for popping a parent directory.
|
||||
// Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
|
||||
let positive = 0;
|
||||
// We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
|
||||
// generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
|
||||
// real directory, we won't need to append, unless the other conditions happen again.
|
||||
let addTrailingSlash = false;
|
||||
for (let i = 1; i < pieces.length; i++) {
|
||||
const piece = pieces[i];
|
||||
// An empty directory, could be a trailing slash, or just a double "//" in the path.
|
||||
if (!piece) {
|
||||
addTrailingSlash = true;
|
||||
continue;
|
||||
}
|
||||
// If we encounter a real directory, then we don't need to append anymore.
|
||||
addTrailingSlash = false;
|
||||
// A current directory, which we can always drop.
|
||||
if (piece === '.')
|
||||
continue;
|
||||
// A parent directory, we need to see if there are any real directories we can pop. Else, we
|
||||
// have an excess of parents, and we'll need to keep the "..".
|
||||
if (piece === '..') {
|
||||
if (positive) {
|
||||
addTrailingSlash = true;
|
||||
positive--;
|
||||
pointer--;
|
||||
}
|
||||
else if (rel) {
|
||||
// If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
|
||||
// URL, protocol relative URL, or an absolute path, we don't need to keep excess.
|
||||
pieces[pointer++] = piece;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// We've encountered a real directory. Move it to the next insertion pointer, which accounts for
|
||||
// any popped or dropped directories.
|
||||
pieces[pointer++] = piece;
|
||||
positive++;
|
||||
}
|
||||
let path = '';
|
||||
for (let i = 1; i < pointer; i++) {
|
||||
path += '/' + pieces[i];
|
||||
}
|
||||
if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
|
||||
path += '/';
|
||||
}
|
||||
url.path = path;
|
||||
}
|
||||
/**
|
||||
* Attempts to resolve `input` URL/path relative to `base`.
|
||||
*/
|
||||
function resolve$1(input, base) {
|
||||
if (!input && !base)
|
||||
return '';
|
||||
const url = parseUrl(input);
|
||||
let inputType = url.type;
|
||||
if (base && inputType !== UrlType.Absolute) {
|
||||
const baseUrl = parseUrl(base);
|
||||
const baseType = baseUrl.type;
|
||||
switch (inputType) {
|
||||
case UrlType.Empty:
|
||||
url.hash = baseUrl.hash;
|
||||
// fall through
|
||||
case UrlType.Hash:
|
||||
url.query = baseUrl.query;
|
||||
// fall through
|
||||
case UrlType.Query:
|
||||
case UrlType.RelativePath:
|
||||
mergePaths(url, baseUrl);
|
||||
// fall through
|
||||
case UrlType.AbsolutePath:
|
||||
// The host, user, and port are joined, you can't copy one without the others.
|
||||
url.user = baseUrl.user;
|
||||
url.host = baseUrl.host;
|
||||
url.port = baseUrl.port;
|
||||
// fall through
|
||||
case UrlType.SchemeRelative:
|
||||
// The input doesn't have a schema at least, so we need to copy at least that over.
|
||||
url.scheme = baseUrl.scheme;
|
||||
}
|
||||
if (baseType > inputType)
|
||||
inputType = baseType;
|
||||
}
|
||||
normalizePath(url, inputType);
|
||||
const queryHash = url.query + url.hash;
|
||||
switch (inputType) {
|
||||
// This is impossible, because of the empty checks at the start of the function.
|
||||
// case UrlType.Empty:
|
||||
case UrlType.Hash:
|
||||
case UrlType.Query:
|
||||
return queryHash;
|
||||
case UrlType.RelativePath: {
|
||||
// The first char is always a "/", and we need it to be relative.
|
||||
const path = url.path.slice(1);
|
||||
if (!path)
|
||||
return queryHash || '.';
|
||||
if (isRelative(base || input) && !isRelative(path)) {
|
||||
// If base started with a leading ".", or there is no base and input started with a ".",
|
||||
// then we need to ensure that the relative path starts with a ".". We don't know if
|
||||
// relative starts with a "..", though, so check before prepending.
|
||||
return './' + path + queryHash;
|
||||
}
|
||||
return path + queryHash;
|
||||
}
|
||||
case UrlType.AbsolutePath:
|
||||
return url.path + queryHash;
|
||||
default:
|
||||
return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
|
||||
}
|
||||
}
|
||||
|
||||
function resolve(input, base) {
|
||||
// The base is always treated as a directory, if it's not empty.
|
||||
// https://github.com/mozilla/source-map/blob/8cb3ee57/lib/util.js#L327
|
||||
// https://github.com/chromium/chromium/blob/da4adbb3/third_party/blink/renderer/devtools/front_end/sdk/SourceMap.js#L400-L401
|
||||
if (base && !base.endsWith('/'))
|
||||
base += '/';
|
||||
return resolve$1(input, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes everything after the last "/", but leaves the slash.
|
||||
*/
|
||||
function stripFilename(path) {
|
||||
if (!path)
|
||||
return '';
|
||||
const index = path.lastIndexOf('/');
|
||||
return path.slice(0, index + 1);
|
||||
}
|
||||
|
||||
const COLUMN = 0;
|
||||
const SOURCES_INDEX = 1;
|
||||
const SOURCE_LINE = 2;
|
||||
const SOURCE_COLUMN = 3;
|
||||
const NAMES_INDEX = 4;
|
||||
const REV_GENERATED_LINE = 1;
|
||||
const REV_GENERATED_COLUMN = 2;
|
||||
|
||||
function maybeSort(mappings, owned) {
|
||||
const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
|
||||
if (unsortedIndex === mappings.length)
|
||||
return mappings;
|
||||
// If we own the array (meaning we parsed it from JSON), then we're free to directly mutate it. If
|
||||
// not, we do not want to modify the consumer's input array.
|
||||
if (!owned)
|
||||
mappings = mappings.slice();
|
||||
for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) {
|
||||
mappings[i] = sortSegments(mappings[i], owned);
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
function nextUnsortedSegmentLine(mappings, start) {
|
||||
for (let i = start; i < mappings.length; i++) {
|
||||
if (!isSorted(mappings[i]))
|
||||
return i;
|
||||
}
|
||||
return mappings.length;
|
||||
}
|
||||
function isSorted(line) {
|
||||
for (let j = 1; j < line.length; j++) {
|
||||
if (line[j][COLUMN] < line[j - 1][COLUMN]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function sortSegments(line, owned) {
|
||||
if (!owned)
|
||||
line = line.slice();
|
||||
return line.sort(sortComparator);
|
||||
}
|
||||
function sortComparator(a, b) {
|
||||
return a[COLUMN] - b[COLUMN];
|
||||
}
|
||||
|
||||
let found = false;
|
||||
/**
|
||||
* A binary search implementation that returns the index if a match is found.
|
||||
* If no match is found, then the left-index (the index associated with the item that comes just
|
||||
* before the desired index) is returned. To maintain proper sort order, a splice would happen at
|
||||
* the next index:
|
||||
*
|
||||
* ```js
|
||||
* const array = [1, 3];
|
||||
* const needle = 2;
|
||||
* const index = binarySearch(array, needle, (item, needle) => item - needle);
|
||||
*
|
||||
* assert.equal(index, 0);
|
||||
* array.splice(index + 1, 0, needle);
|
||||
* assert.deepEqual(array, [1, 2, 3]);
|
||||
* ```
|
||||
*/
|
||||
function binarySearch(haystack, needle, low, high) {
|
||||
while (low <= high) {
|
||||
const mid = low + ((high - low) >> 1);
|
||||
const cmp = haystack[mid][COLUMN] - needle;
|
||||
if (cmp === 0) {
|
||||
found = true;
|
||||
return mid;
|
||||
}
|
||||
if (cmp < 0) {
|
||||
low = mid + 1;
|
||||
}
|
||||
else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
found = false;
|
||||
return low - 1;
|
||||
}
|
||||
function upperBound(haystack, needle, index) {
|
||||
for (let i = index + 1; i < haystack.length; index = i++) {
|
||||
if (haystack[i][COLUMN] !== needle)
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function lowerBound(haystack, needle, index) {
|
||||
for (let i = index - 1; i >= 0; index = i--) {
|
||||
if (haystack[i][COLUMN] !== needle)
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
function memoizedState() {
|
||||
return {
|
||||
lastKey: -1,
|
||||
lastNeedle: -1,
|
||||
lastIndex: -1,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This overly complicated beast is just to record the last tested line/column and the resulting
|
||||
* index, allowing us to skip a few tests if mappings are monotonically increasing.
|
||||
*/
|
||||
function memoizedBinarySearch(haystack, needle, state, key) {
|
||||
const { lastKey, lastNeedle, lastIndex } = state;
|
||||
let low = 0;
|
||||
let high = haystack.length - 1;
|
||||
if (key === lastKey) {
|
||||
if (needle === lastNeedle) {
|
||||
found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
|
||||
return lastIndex;
|
||||
}
|
||||
if (needle >= lastNeedle) {
|
||||
// lastIndex may be -1 if the previous needle was not found.
|
||||
low = lastIndex === -1 ? 0 : lastIndex;
|
||||
}
|
||||
else {
|
||||
high = lastIndex;
|
||||
}
|
||||
}
|
||||
state.lastKey = key;
|
||||
state.lastNeedle = needle;
|
||||
return (state.lastIndex = binarySearch(haystack, needle, low, high));
|
||||
}
|
||||
|
||||
// Rebuilds the original source files, with mappings that are ordered by source line/column instead
|
||||
// of generated line/column.
|
||||
function buildBySources(decoded, memos) {
|
||||
const sources = memos.map(buildNullArray);
|
||||
for (let i = 0; i < decoded.length; i++) {
|
||||
const line = decoded[i];
|
||||
for (let j = 0; j < line.length; j++) {
|
||||
const seg = line[j];
|
||||
if (seg.length === 1)
|
||||
continue;
|
||||
const sourceIndex = seg[SOURCES_INDEX];
|
||||
const sourceLine = seg[SOURCE_LINE];
|
||||
const sourceColumn = seg[SOURCE_COLUMN];
|
||||
const originalSource = sources[sourceIndex];
|
||||
const originalLine = (originalSource[sourceLine] || (originalSource[sourceLine] = []));
|
||||
const memo = memos[sourceIndex];
|
||||
// The binary search either found a match, or it found the left-index just before where the
|
||||
// segment should go. Either way, we want to insert after that. And there may be multiple
|
||||
// generated segments associated with an original location, so there may need to move several
|
||||
// indexes before we find where we need to insert.
|
||||
const index = upperBound(originalLine, sourceColumn, memoizedBinarySearch(originalLine, sourceColumn, memo, sourceLine));
|
||||
insert(originalLine, (memo.lastIndex = index + 1), [sourceColumn, i, seg[COLUMN]]);
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
function insert(array, index, value) {
|
||||
for (let i = array.length; i > index; i--) {
|
||||
array[i] = array[i - 1];
|
||||
}
|
||||
array[index] = value;
|
||||
}
|
||||
// Null arrays allow us to use ordered index keys without actually allocating contiguous memory like
|
||||
// a real array. We use a null-prototype object to avoid prototype pollution and deoptimizations.
|
||||
// Numeric properties on objects are magically sorted in ascending order by the engine regardless of
|
||||
// the insertion order. So, by setting any numeric keys, even out of order, we'll get ascending
|
||||
// order when iterating with for-in.
|
||||
function buildNullArray() {
|
||||
return { __proto__: null };
|
||||
}
|
||||
|
||||
const LINE_GTR_ZERO = '`line` must be greater than 0 (lines start at line 1)';
|
||||
const COL_GTR_EQ_ZERO = '`column` must be greater than or equal to 0 (columns start at column 0)';
|
||||
const LEAST_UPPER_BOUND = -1;
|
||||
const GREATEST_LOWER_BOUND = 1;
|
||||
/**
|
||||
* Returns the decoded (array of lines of segments) form of the SourceMap's mappings field.
|
||||
*/
|
||||
let decodedMappings;
|
||||
/**
|
||||
* A higher-level API to find the source/line/column associated with a generated line/column
|
||||
* (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in
|
||||
* `source-map` library.
|
||||
*/
|
||||
let originalPositionFor;
|
||||
/**
|
||||
* Finds the generated line/column position of the provided source/line/column source position.
|
||||
*/
|
||||
let generatedPositionFor;
|
||||
class TraceMap {
|
||||
constructor(map, mapUrl) {
|
||||
const isString = typeof map === 'string';
|
||||
if (!isString && map._decodedMemo)
|
||||
return map;
|
||||
const parsed = (isString ? JSON.parse(map) : map);
|
||||
const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
|
||||
this.version = version;
|
||||
this.file = file;
|
||||
this.names = names || [];
|
||||
this.sourceRoot = sourceRoot;
|
||||
this.sources = sources;
|
||||
this.sourcesContent = sourcesContent;
|
||||
const from = resolve(sourceRoot || '', stripFilename(mapUrl));
|
||||
this.resolvedSources = sources.map((s) => resolve(s || '', from));
|
||||
const { mappings } = parsed;
|
||||
if (typeof mappings === 'string') {
|
||||
this._encoded = mappings;
|
||||
this._decoded = undefined;
|
||||
}
|
||||
else {
|
||||
this._encoded = undefined;
|
||||
this._decoded = maybeSort(mappings, isString);
|
||||
}
|
||||
this._decodedMemo = memoizedState();
|
||||
this._bySources = undefined;
|
||||
this._bySourceMemos = undefined;
|
||||
}
|
||||
}
|
||||
(() => {
|
||||
decodedMappings = (map) => {
|
||||
return (map._decoded || (map._decoded = decode(map._encoded)));
|
||||
};
|
||||
originalPositionFor = (map, { line, column, bias }) => {
|
||||
line--;
|
||||
if (line < 0)
|
||||
throw new Error(LINE_GTR_ZERO);
|
||||
if (column < 0)
|
||||
throw new Error(COL_GTR_EQ_ZERO);
|
||||
const decoded = decodedMappings(map);
|
||||
// It's common for parent source maps to have pointers to lines that have no
|
||||
// mapping (like a "//# sourceMappingURL=") at the end of the child file.
|
||||
if (line >= decoded.length)
|
||||
return OMapping(null, null, null, null);
|
||||
const segments = decoded[line];
|
||||
const index = traceSegmentInternal(segments, map._decodedMemo, line, column, bias || GREATEST_LOWER_BOUND);
|
||||
if (index === -1)
|
||||
return OMapping(null, null, null, null);
|
||||
const segment = segments[index];
|
||||
if (segment.length === 1)
|
||||
return OMapping(null, null, null, null);
|
||||
const { names, resolvedSources } = map;
|
||||
return OMapping(resolvedSources[segment[SOURCES_INDEX]], segment[SOURCE_LINE] + 1, segment[SOURCE_COLUMN], segment.length === 5 ? names[segment[NAMES_INDEX]] : null);
|
||||
};
|
||||
generatedPositionFor = (map, { source, line, column, bias }) => {
|
||||
return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND, false);
|
||||
};
|
||||
function generatedPosition(map, source, line, column, bias, all) {
|
||||
line--;
|
||||
if (line < 0)
|
||||
throw new Error(LINE_GTR_ZERO);
|
||||
if (column < 0)
|
||||
throw new Error(COL_GTR_EQ_ZERO);
|
||||
const { sources, resolvedSources } = map;
|
||||
let sourceIndex = sources.indexOf(source);
|
||||
if (sourceIndex === -1)
|
||||
sourceIndex = resolvedSources.indexOf(source);
|
||||
if (sourceIndex === -1)
|
||||
return all ? [] : GMapping(null, null);
|
||||
const generated = (map._bySources || (map._bySources = buildBySources(decodedMappings(map), (map._bySourceMemos = sources.map(memoizedState)))));
|
||||
const segments = generated[sourceIndex][line];
|
||||
if (segments == null)
|
||||
return all ? [] : GMapping(null, null);
|
||||
const memo = map._bySourceMemos[sourceIndex];
|
||||
if (all)
|
||||
return sliceGeneratedPositions(segments, memo, line, column, bias);
|
||||
const index = traceSegmentInternal(segments, memo, line, column, bias);
|
||||
if (index === -1)
|
||||
return GMapping(null, null);
|
||||
const segment = segments[index];
|
||||
return GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]);
|
||||
}
|
||||
})();
|
||||
function OMapping(source, line, column, name) {
|
||||
return { source, line, column, name };
|
||||
}
|
||||
function GMapping(line, column) {
|
||||
return { line, column };
|
||||
}
|
||||
function traceSegmentInternal(segments, memo, line, column, bias) {
|
||||
let index = memoizedBinarySearch(segments, column, memo, line);
|
||||
if (found) {
|
||||
index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
|
||||
}
|
||||
else if (bias === LEAST_UPPER_BOUND)
|
||||
index++;
|
||||
if (index === -1 || index === segments.length)
|
||||
return -1;
|
||||
return index;
|
||||
}
|
||||
function sliceGeneratedPositions(segments, memo, line, column, bias) {
|
||||
let min = traceSegmentInternal(segments, memo, line, column, GREATEST_LOWER_BOUND);
|
||||
// We ignored the bias when tracing the segment so that we're guarnateed to find the first (in
|
||||
// insertion order) segment that matched. Even if we did respect the bias when tracing, we would
|
||||
// still need to call `lowerBound()` to find the first segment, which is slower than just looking
|
||||
// for the GREATEST_LOWER_BOUND to begin with. The only difference that matters for us is when the
|
||||
// binary search didn't match, in which case GREATEST_LOWER_BOUND just needs to increment to
|
||||
// match LEAST_UPPER_BOUND.
|
||||
if (!found && bias === LEAST_UPPER_BOUND)
|
||||
min++;
|
||||
if (min === -1 || min === segments.length)
|
||||
return [];
|
||||
// We may have found the segment that started at an earlier column. If this is the case, then we
|
||||
// need to slice all generated segments that match _that_ column, because all such segments span
|
||||
// to our desired column.
|
||||
const matchedColumn = found ? column : segments[min][COLUMN];
|
||||
// The binary search is not guaranteed to find the lower bound when a match wasn't found.
|
||||
if (!found)
|
||||
min = lowerBound(segments, matchedColumn, min);
|
||||
const max = upperBound(segments, matchedColumn, min);
|
||||
const result = [];
|
||||
for (; min <= max; min++) {
|
||||
const segment = segments[min];
|
||||
result.push(GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
|
||||
const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
|
||||
const stackIgnorePatterns = [
|
||||
"node:internal",
|
||||
/\/packages\/\w+\/dist\//,
|
||||
/\/@vitest\/\w+\/dist\//,
|
||||
"/vitest/dist/",
|
||||
"/vitest/src/",
|
||||
"/vite-node/dist/",
|
||||
"/vite-node/src/",
|
||||
"/node_modules/chai/",
|
||||
"/node_modules/tinypool/",
|
||||
"/node_modules/tinyspy/",
|
||||
"/deps/chai.js",
|
||||
/__vitest_browser__/
|
||||
];
|
||||
function extractLocation(urlLike) {
|
||||
if (!urlLike.includes(":"))
|
||||
return [urlLike];
|
||||
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
|
||||
const parts = regExp.exec(urlLike.replace(/^\(|\)$/g, ""));
|
||||
if (!parts)
|
||||
return [urlLike];
|
||||
let url = parts[1];
|
||||
if (url.startsWith("http:") || url.startsWith("https:")) {
|
||||
const urlObj = new URL(url);
|
||||
url = urlObj.pathname;
|
||||
}
|
||||
if (url.startsWith("/@fs/")) {
|
||||
url = url.slice(typeof process !== "undefined" && process.platform === "win32" ? 5 : 4);
|
||||
}
|
||||
return [url, parts[2] || void 0, parts[3] || void 0];
|
||||
}
|
||||
function parseSingleFFOrSafariStack(raw) {
|
||||
let line = raw.trim();
|
||||
if (SAFARI_NATIVE_CODE_REGEXP.test(line))
|
||||
return null;
|
||||
if (line.includes(" > eval"))
|
||||
line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
|
||||
if (!line.includes("@") && !line.includes(":"))
|
||||
return null;
|
||||
const functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
|
||||
const matches = line.match(functionNameRegex);
|
||||
const functionName = matches && matches[1] ? matches[1] : void 0;
|
||||
const [url, lineNumber, columnNumber] = extractLocation(line.replace(functionNameRegex, ""));
|
||||
if (!url || !lineNumber || !columnNumber)
|
||||
return null;
|
||||
return {
|
||||
file: url,
|
||||
method: functionName || "",
|
||||
line: Number.parseInt(lineNumber),
|
||||
column: Number.parseInt(columnNumber)
|
||||
};
|
||||
}
|
||||
function parseSingleStack(raw) {
|
||||
const line = raw.trim();
|
||||
if (!CHROME_IE_STACK_REGEXP.test(line))
|
||||
return parseSingleFFOrSafariStack(line);
|
||||
return parseSingleV8Stack(line);
|
||||
}
|
||||
function parseSingleV8Stack(raw) {
|
||||
let line = raw.trim();
|
||||
if (!CHROME_IE_STACK_REGEXP.test(line))
|
||||
return null;
|
||||
if (line.includes("(eval "))
|
||||
line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
|
||||
let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
|
||||
const location = sanitizedLine.match(/ (\(.+\)$)/);
|
||||
sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
|
||||
const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
|
||||
let method = location && sanitizedLine || "";
|
||||
let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
|
||||
if (!file || !lineNumber || !columnNumber)
|
||||
return null;
|
||||
if (method.startsWith("async "))
|
||||
method = method.slice(6);
|
||||
if (file.startsWith("file://"))
|
||||
file = file.slice(7);
|
||||
file = resolve$2(file);
|
||||
if (method)
|
||||
method = method.replace(/__vite_ssr_import_\d+__\./g, "");
|
||||
return {
|
||||
method,
|
||||
file,
|
||||
line: Number.parseInt(lineNumber),
|
||||
column: Number.parseInt(columnNumber)
|
||||
};
|
||||
}
|
||||
function parseStacktrace(stack, options = {}) {
|
||||
const { ignoreStackEntries = stackIgnorePatterns } = options;
|
||||
let stacks = !CHROME_IE_STACK_REGEXP.test(stack) ? parseFFOrSafariStackTrace(stack) : parseV8Stacktrace(stack);
|
||||
if (ignoreStackEntries.length)
|
||||
stacks = stacks.filter((stack2) => !ignoreStackEntries.some((p) => stack2.file.match(p)));
|
||||
return stacks.map((stack2) => {
|
||||
var _a;
|
||||
const map = (_a = options.getSourceMap) == null ? void 0 : _a.call(options, stack2.file);
|
||||
if (!map || typeof map !== "object" || !map.version)
|
||||
return stack2;
|
||||
const traceMap = new TraceMap(map);
|
||||
const { line, column } = originalPositionFor(traceMap, stack2);
|
||||
if (line != null && column != null)
|
||||
return { ...stack2, line, column };
|
||||
return stack2;
|
||||
});
|
||||
}
|
||||
function parseFFOrSafariStackTrace(stack) {
|
||||
return stack.split("\n").map((line) => parseSingleFFOrSafariStack(line)).filter(notNullish);
|
||||
}
|
||||
function parseV8Stacktrace(stack) {
|
||||
return stack.split("\n").map((line) => parseSingleV8Stack(line)).filter(notNullish);
|
||||
}
|
||||
function parseErrorStacktrace(e, options = {}) {
|
||||
if (!e || isPrimitive(e))
|
||||
return [];
|
||||
if (e.stacks)
|
||||
return e.stacks;
|
||||
const stackStr = e.stack || e.stackStr || "";
|
||||
let stackFrames = parseStacktrace(stackStr, options);
|
||||
if (options.frameFilter)
|
||||
stackFrames = stackFrames.filter((f) => options.frameFilter(e, f) !== false);
|
||||
e.stacks = stackFrames;
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
export { TraceMap, generatedPositionFor, originalPositionFor, parseErrorStacktrace, parseSingleFFOrSafariStack, parseSingleStack, parseSingleV8Stack, parseStacktrace };
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { CompareKeys } from 'pretty-format';
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
type DiffOptionsColor = (arg: string) => string;
|
||||
interface DiffOptions {
|
||||
aAnnotation?: string;
|
||||
aColor?: DiffOptionsColor;
|
||||
aIndicator?: string;
|
||||
bAnnotation?: string;
|
||||
bColor?: DiffOptionsColor;
|
||||
bIndicator?: string;
|
||||
changeColor?: DiffOptionsColor;
|
||||
changeLineTrailingSpaceColor?: DiffOptionsColor;
|
||||
commonColor?: DiffOptionsColor;
|
||||
commonIndicator?: string;
|
||||
commonLineTrailingSpaceColor?: DiffOptionsColor;
|
||||
contextLines?: number;
|
||||
emptyFirstOrLastLinePlaceholder?: string;
|
||||
expand?: boolean;
|
||||
includeChangeCounts?: boolean;
|
||||
omitAnnotationLines?: boolean;
|
||||
patchColor?: DiffOptionsColor;
|
||||
compareKeys?: CompareKeys;
|
||||
truncateThreshold?: number;
|
||||
truncateAnnotation?: string;
|
||||
truncateAnnotationColor?: DiffOptionsColor;
|
||||
}
|
||||
|
||||
export type { DiffOptions as D, DiffOptionsColor as a };
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
type Awaitable<T> = T | PromiseLike<T>;
|
||||
type Nullable<T> = T | null | undefined;
|
||||
type Arrayable<T> = T | Array<T>;
|
||||
type ArgumentsType<T> = T extends (...args: infer U) => any ? U : never;
|
||||
type MergeInsertions<T> = T extends object ? {
|
||||
[K in keyof T]: MergeInsertions<T[K]>;
|
||||
} : T;
|
||||
type DeepMerge<F, S> = MergeInsertions<{
|
||||
[K in keyof F | keyof S]: K extends keyof S & keyof F ? DeepMerge<F[K], S[K]> : K extends keyof S ? S[K] : K extends keyof F ? F[K] : never;
|
||||
}>;
|
||||
type MutableArray<T extends readonly any[]> = {
|
||||
-readonly [k in keyof T]: T[k];
|
||||
};
|
||||
interface Constructable {
|
||||
new (...args: any[]): any;
|
||||
}
|
||||
interface ParsedStack {
|
||||
method: string;
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
interface ErrorWithDiff extends Error {
|
||||
name: string;
|
||||
nameStr?: string;
|
||||
stack?: string;
|
||||
stackStr?: string;
|
||||
stacks?: ParsedStack[];
|
||||
showDiff?: boolean;
|
||||
actual?: any;
|
||||
expected?: any;
|
||||
operator?: string;
|
||||
type?: string;
|
||||
frame?: string;
|
||||
diff?: string;
|
||||
codeFrame?: string;
|
||||
}
|
||||
|
||||
export type { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, ErrorWithDiff, MergeInsertions, MutableArray, Nullable, ParsedStack };
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/error.js'
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './dist/helpers.js'
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "@vitest/utils",
|
||||
"type": "module",
|
||||
"version": "1.6.1",
|
||||
"description": "Shared Vitest utility functions",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/utils#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/utils"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./diff": {
|
||||
"types": "./dist/diff.d.ts",
|
||||
"default": "./dist/diff.js"
|
||||
},
|
||||
"./ast": {
|
||||
"types": "./dist/ast.d.ts",
|
||||
"default": "./dist/ast.js"
|
||||
},
|
||||
"./error": {
|
||||
"types": "./dist/error.d.ts",
|
||||
"default": "./dist/error.js"
|
||||
},
|
||||
"./helpers": {
|
||||
"types": "./dist/helpers.d.ts",
|
||||
"default": "./dist/helpers.js"
|
||||
},
|
||||
"./source-map": {
|
||||
"types": "./dist/source-map.d.ts",
|
||||
"default": "./dist/source-map.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"source-map": [
|
||||
"dist/source-map.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"diff-sequences": "^29.6.3",
|
||||
"estree-walker": "^3.0.3",
|
||||
"loupe": "^2.3.7",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.22",
|
||||
"@types/estree": "^1.0.5",
|
||||
"tinyhighlight": "^0.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c",
|
||||
"dev": "rollup -c --watch"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user