@systemfsoftware/vitest 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,118 @@
1
1
 
2
- import { a as booleanRefusal, b as refusedText, c as negatedText, m as refusePiecewise, s as neededText, t as authorize, u as refusalOf } from "./guard-BocOhXt2.mjs";
2
+ import { a as booleanRefusal, c as negatedText, m as refusePiecewise, s as neededText, t as authorize, u as refusalOf, x as refusedText } from "./guard-p_8ARgPm.mjs";
3
3
  import * as V from "vitest";
4
4
  import * as Effect from "effect/Effect";
5
+ import * as Function from "effect/Function";
5
6
  import * as Schema from "effect/Schema";
7
+ import * as Option from "effect/Option";
6
8
  import * as Context from "effect/Context";
9
+ //#region src/internal/call-site.ts
10
+ /**
11
+ * The one rule that reads a frame out of a stack: which frame a failure is raised from, and which frame an author
12
+ * wrote the thing that raised it on (R2, KTD6).
13
+ *
14
+ * A deferred check and a declared property both fail inside the fork, where the stack holds only the fork's frames
15
+ * and the author's line is long gone. Each captures its call site while the author's frame is still on the stack
16
+ * and hands it to the failure as its first frame, so the renderer's R2 walk leads with the author's line.
17
+ *
18
+ * The rule is the same everywhere it applies: the first frame outside the vendored install, node internals, and
19
+ * every spec library's own `src` and `dist` — {@link LIBRARY_DIRS}. A caller may name its own extra frames with a
20
+ * {@link RegExp}, and a value that captured a raw stack records it whole, resolved here rather than by a second
21
+ * copy of the rule. `effect-spec-runtime`, `effect-cell-types`, `effect-gherkin-spec` and `trace-spec` read it from
22
+ * here.
23
+ *
24
+ * @since 4.0.0
25
+ */
26
+ const FRAME = /\(?((?:file:\/\/)?[^()\s]+):(\d+):(\d+)\)?\s*$/u;
27
+ const FILE_PROTOCOL = "file://";
28
+ const FRAME_AT = "at ";
29
+ const FRAME_PAREN = " (";
30
+ const and = (left, right) => left && right;
31
+ const not = (value) => !value;
32
+ const LIBRARY_DIRS = [
33
+ "runner/vitest",
34
+ "effect-spec-runtime",
35
+ "effect-cell-types",
36
+ "effect-gherkin-spec",
37
+ "storybook-gherkin",
38
+ "conformance-spec",
39
+ "differential-spec",
40
+ "trace-spec",
41
+ "effect-daemon-spec"
42
+ ];
43
+ const isVendoredPath = (path) => path.includes("node_modules") || path.startsWith("node:");
44
+ const inOwnDir = (path) => (dir) => path.includes(`/${dir}/src/`) || path.includes(`/${dir}/dist/`);
45
+ const isLibraryPath = (path) => LIBRARY_DIRS.some(inOwnDir(path));
46
+ /** @internal */
47
+ const isUserPath = (path) => and(not(isVendoredPath(path)), not(isLibraryPath(path)));
48
+ const stripProtocol = (path) => path.startsWith(FILE_PROTOCOL) ? path.slice(7) : path;
49
+ const isFrame = (frame) => frame !== void 0;
50
+ const partsOf = (match) => Option.flatMap(Option.fromNullishOr(match), (found) => Option.map(Option.all([
51
+ Option.fromNullishOr(found[1]),
52
+ Option.fromNullishOr(found[2]),
53
+ Option.fromNullishOr(found[3])
54
+ ]), ([path, line, column]) => ({
55
+ path: stripProtocol(path),
56
+ line,
57
+ column
58
+ })));
59
+ const beforeParen = (rest) => Option.flatMap(Option.fromNullishOr(rest.indexOf(FRAME_PAREN)), (paren) => Option.some(rest.slice(0, paren)));
60
+ const afterAt = (line) => line.startsWith(FRAME_AT) ? Option.some(line.slice(3)) : Option.none();
61
+ const nonEmpty = (text) => text.length > 0;
62
+ const fnIn = (line) => Option.getOrUndefined(Option.flatMap(afterAt(line), (rest) => Option.filter(beforeParen(rest), nonEmpty)));
63
+ const frameOfLine = (line) => {
64
+ const raw = line.trim();
65
+ return Option.match(partsOf(FRAME.exec(raw)), {
66
+ onNone: () => void 0,
67
+ onSome: (parts) => ({
68
+ ...parts,
69
+ fn: fnIn(raw),
70
+ raw
71
+ })
72
+ });
73
+ };
74
+ /** @internal */
75
+ const framesOf = (stack) => stack.split("\n").map(frameOfLine).filter(isFrame);
76
+ const frameSiteOf = (frame) => `${frame.path}:${frame.line}:${frame.column}`;
77
+ const isUserFrame = (frame) => isUserPath(frame.path);
78
+ const firstUserFrameOf = (stack) => framesOf(stack).find(isUserFrame);
79
+ /** @internal */
80
+ const firstUserSiteOf = (stack) => {
81
+ const frame = firstUserFrameOf(stack);
82
+ return frame === void 0 ? void 0 : frameSiteOf(frame);
83
+ };
84
+ const NO_LIBRARY = /$^/u;
85
+ const currentStack = () => (/* @__PURE__ */ new Error()).stack ?? "";
86
+ const isSiteOf = (library) => (frame) => and(isUserPath(frame.path), not(library.test(frame.raw)));
87
+ const firstFrameOf = (stack, library) => framesOf(stack).find(isSiteOf(library));
88
+ /** @internal */
89
+ const callFrameOutside = (library) => {
90
+ const frame = firstFrameOf(currentStack(), library);
91
+ return frame === void 0 ? void 0 : frame.raw;
92
+ };
93
+ /** @internal */
94
+ const callFrame = () => callFrameOutside(NO_LIBRARY);
95
+ const callSiteOutside = (library) => {
96
+ const frame = firstFrameOf(currentStack(), library);
97
+ return frame === void 0 ? void 0 : frameSiteOf(frame);
98
+ };
99
+ /** @internal */
100
+ const callSite = () => callSiteOutside(NO_LIBRARY);
101
+ const firstLineOf = (lines) => lines[0] ?? "";
102
+ const stackOf = (error) => error.stack ?? "";
103
+ const withRaisingFrameImpl = (error, frame) => {
104
+ if (frame === void 0) return error;
105
+ const lines = stackOf(error).split("\n");
106
+ error.stack = [
107
+ firstLineOf(lines),
108
+ frame,
109
+ ...lines.slice(1)
110
+ ].join("\n");
111
+ return error;
112
+ };
113
+ /** @internal */
114
+ const withRaisingFrame = Function.dual(2, withRaisingFrameImpl);
115
+ //#endregion
7
116
  //#region src/internal/checks.ts
8
117
  /**
9
118
  * The checks a test receives as its callback parameter. There is no importable `expect`: the only one in reach is
@@ -42,11 +151,12 @@ const forView = (view, negated) => ({
42
151
  negated
43
152
  });
44
153
  const notView = (view) => forView(view, !view.negated);
45
- const viewFor = (written, actual, message) => ({
154
+ const viewFor = (written, actual, message, site) => ({
46
155
  actual,
47
156
  message,
48
157
  negated: false,
49
- written
158
+ written,
159
+ site
50
160
  });
51
161
  const messageOf = (argument) => typeof argument === "string" ? argument : void 0;
52
162
  const refuseBooleanActual = (actual) => typeof actual === "boolean";
@@ -73,11 +183,23 @@ const invokeMember = (target, property, args) => {
73
183
  const member = memberAt(target, property);
74
184
  if (isCallable(member)) Reflect.apply(member, target, args);
75
185
  };
76
- const judgementOf = (view, property, args) => (make) => {
186
+ const judgedBy = (view, property, args, make) => {
77
187
  const assertion = make(view.actual, view.message);
78
188
  const target = view.negated ? assertion.not : assertion;
79
189
  invokeMember(target, property, args);
80
190
  };
191
+ const raisedFailure = (thrown, site) => thrown instanceof Error ? withRaisingFrame(thrown, site) : thrown;
192
+ /**
193
+ * A failed judgement is a failure the fork raised: by now the stack holds only the fork's frames, so the site the
194
+ * body called `expect` from is prepended to the error's stack (R2, KTD6).
195
+ */
196
+ const judgementOf = (view, property, args) => (make) => {
197
+ try {
198
+ judgedBy(view, property, args, make);
199
+ } catch (thrown) {
200
+ throw raisedFailure(thrown, view.site);
201
+ }
202
+ };
81
203
  const judgedCheck = (view, property, args) => makeCheck(judgementOf(view, property, args), view.written);
82
204
  const genericMember = (view, property) => (...args) => judgedCheck(view, property, args);
83
205
  const neededMember = (view, property) => (...args) => {
@@ -117,7 +239,7 @@ const viewHandler = (view) => ({ get: (_target, property) => memberFor(view, pro
117
239
  const matcherView = (view) => new Proxy({}, viewHandler(view));
118
240
  const assertionView = (written, args) => {
119
241
  if (refuseBooleanActual(args[0])) throw refusalOf(booleanRefusal);
120
- return matcherView(viewFor(written, args[0], messageOf(args[1])));
242
+ return matcherView(viewFor(written, args[0], messageOf(args[1]), callFrame()));
121
243
  };
122
244
  const isDecodingSchema = (value) => Schema.isSchema(value);
123
245
  const asMatcher = (value) => {
@@ -203,4 +325,4 @@ const vitestTestContextKey = "@systemfsoftware/vitest/TestContext";
203
325
  */
204
326
  const VitestTestContext = Context.Reference(vitestTestContextKey, { defaultValue: () => null });
205
327
  //#endregion
206
- export { isCheck as a, checksFor as i, vitestTestContextKey as n, makeLedger as o, Asserted as r, VitestTestContext as t };
328
+ export { isCheck as a, callFrameOutside as c, framesOf as d, isUserPath as f, checksFor as i, callSite as l, vitestTestContextKey as n, makeLedger as o, withRaisingFrame as p, Asserted as r, callFrame as s, VitestTestContext as t, firstUserSiteOf as u };
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@systemfsoftware/vitest",
3
3
  "license": "MIT",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "author": "Ryan Lee <drdgvhbh@gmail.com>",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
9
- "directory": "packages/vitest"
9
+ "directory": "packages/runner/vitest"
10
10
  },
11
- "homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/vitest#readme",
11
+ "homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/runner/vitest#readme",
12
12
  "bugs": "https://github.com/systemfsoftware/systemfsoftware/issues",
13
13
  "description": "A fork of @effect/vitest whose defaults make the lazy test a good test: expect bound to the test's own callback, one check per observed state, fresh layers, a second run that catches leaked state, virtual time, and properties refuted against a constant impostor.",
14
14
  "keywords": [
@@ -25,6 +25,10 @@
25
25
  "types": "./dist/index.d.ts",
26
26
  "default": "./dist/index.mjs"
27
27
  },
28
+ "./failure": {
29
+ "types": "./dist/failure.d.ts",
30
+ "default": "./dist/failure.mjs"
31
+ },
28
32
  "./guard": {
29
33
  "types": "./dist/guard.d.ts",
30
34
  "default": "./dist/guard.mjs"
@@ -44,7 +48,7 @@
44
48
  ],
45
49
  "devDependencies": {
46
50
  "@systemfsoftware/arethetypeswrong-cli": "^4.2.0",
47
- "@systemfsoftware/oxlint-config-recommended": "^3.1.0",
51
+ "@systemfsoftware/oxlint-config-recommended": "^3.2.0",
48
52
  "@systemfsoftware/tsconfig": "^2.0.1",
49
53
  "@systemfsoftware/tsdown-config": "^0.1.0",
50
54
  "@systemfsoftware/vitest-config": "^0.1.0",
@@ -61,6 +65,7 @@
61
65
  "vitest": "^5"
62
66
  },
63
67
  "publishConfig": {
68
+ "access": "public",
64
69
  "provenance": true
65
70
  },
66
71
  "scripts": {