@jsenv/snapshot 2.6.5 → 2.6.7
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.
- package/package.json +7 -5
- package/src/filesystem_well_known_values.js +18 -26
- package/src/get_caller_location.js +22 -0
- package/src/main.js +4 -1
- package/src/replace_fluctuating_values.js +1 -1
- package/src/side_effects/capture_side_effects.js +6 -0
- package/src/side_effects/create_capture_side_effects.js +305 -0
- package/src/side_effects/filesystem/filesystem_side_effects.js +302 -0
- package/src/side_effects/filesystem/group_file_side_effects_per_directory.js +30 -0
- package/src/{function_side_effects → side_effects/filesystem}/spy_filesystem_calls.js +48 -25
- package/src/side_effects/log/group_log_side_effects.js +29 -0
- package/src/side_effects/log/log_side_effects.js +156 -0
- package/src/side_effects/render_logs_gif.js +18 -0
- package/src/side_effects/render_side_effects.js +435 -0
- package/src/side_effects/snapshot_side_effects.js +43 -0
- package/src/side_effects/snapshot_tests.js +115 -0
- package/src/side_effects/utils/group_side_effects.js +89 -0
- package/src/function_side_effects/function_side_effects_collector.js +0 -160
- package/src/function_side_effects/function_side_effects_renderer.js +0 -29
- package/src/function_side_effects/function_side_effects_snapshot.js +0 -302
- package/src/function_side_effects/group_file_side_effects_per_directory.js +0 -114
- package/src/function_side_effects/spy_console_calls.js +0 -89
- /package/src/{function_side_effects → side_effects/filesystem}/common_ancestor_path.js +0 -0
- /package/src/{function_side_effects → side_effects/filesystem}/common_ancestor_path.test.mjs +0 -0
- /package/src/{function_side_effects → side_effects}/hook_into_method.js +0 -0
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { createException } from "@jsenv/exception";
|
|
2
|
-
import { replaceFluctuatingValues } from "../replace_fluctuating_values.js";
|
|
3
|
-
import { wrapIntoMarkdownBlock } from "./function_side_effects_renderer.js";
|
|
4
|
-
|
|
5
|
-
const RETURN_PROMISE = {};
|
|
6
|
-
|
|
7
|
-
let functionExecutingCount = 0;
|
|
8
|
-
|
|
9
|
-
export const collectFunctionSideEffects = (
|
|
10
|
-
fn,
|
|
11
|
-
sideEffectDetectors,
|
|
12
|
-
{ rootDirectoryUrl },
|
|
13
|
-
) => {
|
|
14
|
-
const sideEffects = [];
|
|
15
|
-
const addSideEffect = (sideEffect) => {
|
|
16
|
-
sideEffects.push(sideEffect);
|
|
17
|
-
return sideEffect;
|
|
18
|
-
};
|
|
19
|
-
const finallyCallbackSet = new Set();
|
|
20
|
-
for (const sideEffectDetector of sideEffectDetectors) {
|
|
21
|
-
const uninstall = sideEffectDetector.install(addSideEffect);
|
|
22
|
-
finallyCallbackSet.add(() => {
|
|
23
|
-
uninstall();
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
if (functionExecutingCount) {
|
|
27
|
-
// The reason for this warning:
|
|
28
|
-
// 1. fs side effect detectors is not yet fully compatible with that because
|
|
29
|
-
// callback.oncomplete redefinition might be wrong for open, mkdir etc
|
|
30
|
-
// (at least this is to be tested)
|
|
31
|
-
// 2. It's usually a sign code forgets to put await in front of
|
|
32
|
-
// collectFunctionSideEffects or snapshotFunctionSideEffects
|
|
33
|
-
// 3. collectFunctionSideEffects is meant to collect a function side effect
|
|
34
|
-
// during unit test. So in unit test the function being tested should be analyized
|
|
35
|
-
// and should not in turn analyze an other one
|
|
36
|
-
console.warn(
|
|
37
|
-
`collectFunctionSideEffects called while other function(s) side effects are collected`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const onCatch = (valueThrow) => {
|
|
42
|
-
sideEffects.push({
|
|
43
|
-
type: "throw",
|
|
44
|
-
value: valueThrow,
|
|
45
|
-
label: "throw",
|
|
46
|
-
text: wrapIntoMarkdownBlock(
|
|
47
|
-
renderValueThrownOrRejected(
|
|
48
|
-
createException(valueThrow, { rootDirectoryUrl }),
|
|
49
|
-
{ rootDirectoryUrl },
|
|
50
|
-
),
|
|
51
|
-
),
|
|
52
|
-
});
|
|
53
|
-
};
|
|
54
|
-
const onReturn = (valueReturned) => {
|
|
55
|
-
if (valueReturned === RETURN_PROMISE) {
|
|
56
|
-
sideEffects.push({
|
|
57
|
-
type: "return",
|
|
58
|
-
value: valueReturned,
|
|
59
|
-
label: "return promise",
|
|
60
|
-
text: null,
|
|
61
|
-
});
|
|
62
|
-
} else {
|
|
63
|
-
sideEffects.push({
|
|
64
|
-
type: "return",
|
|
65
|
-
value: valueReturned,
|
|
66
|
-
label: "return",
|
|
67
|
-
text: wrapIntoMarkdownBlock(
|
|
68
|
-
renderReturnValueOrResolveValue(valueReturned, {
|
|
69
|
-
rootDirectoryUrl,
|
|
70
|
-
}),
|
|
71
|
-
"js",
|
|
72
|
-
),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const onResolve = (value) => {
|
|
77
|
-
sideEffects.push({
|
|
78
|
-
type: "resolve",
|
|
79
|
-
value,
|
|
80
|
-
label: "resolve",
|
|
81
|
-
text: wrapIntoMarkdownBlock(
|
|
82
|
-
renderReturnValueOrResolveValue(value, { rootDirectoryUrl }),
|
|
83
|
-
"js",
|
|
84
|
-
),
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
const onReject = (reason) => {
|
|
88
|
-
sideEffects.push({
|
|
89
|
-
type: "reject",
|
|
90
|
-
value: reason,
|
|
91
|
-
label: "reject",
|
|
92
|
-
text: wrapIntoMarkdownBlock(
|
|
93
|
-
renderValueThrownOrRejected(
|
|
94
|
-
createException(reason, { rootDirectoryUrl }),
|
|
95
|
-
{ rootDirectoryUrl },
|
|
96
|
-
),
|
|
97
|
-
),
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
const onFinally = () => {
|
|
101
|
-
delete process.env.SNAPSHOTING_FUNCTION_SIDE_EFFECTS;
|
|
102
|
-
functionExecutingCount--;
|
|
103
|
-
for (const finallyCallback of finallyCallbackSet) {
|
|
104
|
-
finallyCallback();
|
|
105
|
-
}
|
|
106
|
-
finallyCallbackSet.clear();
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
process.env.SNAPSHOTING_FUNCTION_SIDE_EFFECTS = "1";
|
|
110
|
-
functionExecutingCount++;
|
|
111
|
-
let returnedPromise = false;
|
|
112
|
-
try {
|
|
113
|
-
const valueReturned = fn();
|
|
114
|
-
if (valueReturned && typeof valueReturned.then === "function") {
|
|
115
|
-
onReturn(RETURN_PROMISE);
|
|
116
|
-
returnedPromise = valueReturned.then(
|
|
117
|
-
(value) => {
|
|
118
|
-
onResolve(value);
|
|
119
|
-
onFinally();
|
|
120
|
-
return sideEffects;
|
|
121
|
-
},
|
|
122
|
-
(e) => {
|
|
123
|
-
onReject(e);
|
|
124
|
-
onFinally();
|
|
125
|
-
return sideEffects;
|
|
126
|
-
},
|
|
127
|
-
);
|
|
128
|
-
return returnedPromise;
|
|
129
|
-
}
|
|
130
|
-
onReturn(valueReturned);
|
|
131
|
-
return sideEffects;
|
|
132
|
-
} catch (e) {
|
|
133
|
-
onCatch(e);
|
|
134
|
-
return sideEffects;
|
|
135
|
-
} finally {
|
|
136
|
-
if (!returnedPromise) {
|
|
137
|
-
onFinally();
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const renderReturnValueOrResolveValue = (value, { rootDirectoryUrl }) => {
|
|
143
|
-
if (value === undefined) {
|
|
144
|
-
return "undefined";
|
|
145
|
-
}
|
|
146
|
-
return replaceFluctuatingValues(JSON.stringify(value, null, " "), {
|
|
147
|
-
stringType: "json",
|
|
148
|
-
rootDirectoryUrl,
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const renderValueThrownOrRejected = (value, { rootDirectoryUrl }) => {
|
|
153
|
-
return replaceFluctuatingValues(
|
|
154
|
-
value ? value.stack || value.message || value : String(value),
|
|
155
|
-
{
|
|
156
|
-
stringType: "error",
|
|
157
|
-
rootDirectoryUrl,
|
|
158
|
-
},
|
|
159
|
-
);
|
|
160
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export const renderSideEffects = (sideEffects) => {
|
|
2
|
-
let string = "";
|
|
3
|
-
let index = 0;
|
|
4
|
-
for (const sideEffect of sideEffects) {
|
|
5
|
-
if (sideEffect.skippable) {
|
|
6
|
-
continue;
|
|
7
|
-
}
|
|
8
|
-
if (string) {
|
|
9
|
-
string += "\n\n";
|
|
10
|
-
}
|
|
11
|
-
let label = `${index + 1}. ${sideEffect.label}`;
|
|
12
|
-
let text = sideEffect.text;
|
|
13
|
-
string += label;
|
|
14
|
-
if (text) {
|
|
15
|
-
string += "\n";
|
|
16
|
-
string += text;
|
|
17
|
-
}
|
|
18
|
-
index++;
|
|
19
|
-
}
|
|
20
|
-
return string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const wrapIntoMarkdownBlock = (value, blockName = "") => {
|
|
24
|
-
const start = "```";
|
|
25
|
-
const end = "```";
|
|
26
|
-
return `${start}${blockName}
|
|
27
|
-
${value}
|
|
28
|
-
${end}`;
|
|
29
|
-
};
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import { readDirectorySync, writeFileSync } from "@jsenv/filesystem";
|
|
2
|
-
import {
|
|
3
|
-
ensurePathnameTrailingSlash,
|
|
4
|
-
urlIsInsideOf,
|
|
5
|
-
urlToExtension,
|
|
6
|
-
urlToRelativeUrl,
|
|
7
|
-
} from "@jsenv/urls";
|
|
8
|
-
import {
|
|
9
|
-
takeDirectorySnapshot,
|
|
10
|
-
takeFileSnapshot,
|
|
11
|
-
} from "../filesystem_snapshot.js";
|
|
12
|
-
import {
|
|
13
|
-
createReplaceFilesystemWellKnownValues,
|
|
14
|
-
createWellKnown,
|
|
15
|
-
} from "../filesystem_well_known_values.js";
|
|
16
|
-
import { replaceFluctuatingValues } from "../replace_fluctuating_values.js";
|
|
17
|
-
import { collectFunctionSideEffects } from "./function_side_effects_collector.js";
|
|
18
|
-
import {
|
|
19
|
-
renderSideEffects,
|
|
20
|
-
wrapIntoMarkdownBlock,
|
|
21
|
-
} from "./function_side_effects_renderer.js";
|
|
22
|
-
import { groupFileSideEffectsPerDirectory } from "./group_file_side_effects_per_directory.js";
|
|
23
|
-
import { spyConsoleCalls } from "./spy_console_calls.js";
|
|
24
|
-
import { spyFilesystemCalls } from "./spy_filesystem_calls.js";
|
|
25
|
-
|
|
26
|
-
const filesystemEffectsDefault = {
|
|
27
|
-
outDirectory: null,
|
|
28
|
-
preserve: false,
|
|
29
|
-
};
|
|
30
|
-
const consoleEffectsDefault = {
|
|
31
|
-
prevent: true,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const snapshotFunctionSideEffects = (
|
|
35
|
-
fn,
|
|
36
|
-
sideEffectFileUrl,
|
|
37
|
-
{ consoleEffects = true, filesystemEffects = true, rootDirectoryUrl } = {},
|
|
38
|
-
) => {
|
|
39
|
-
if (consoleEffects === true) {
|
|
40
|
-
consoleEffects = {};
|
|
41
|
-
}
|
|
42
|
-
if (filesystemEffects === true) {
|
|
43
|
-
filesystemEffects = {};
|
|
44
|
-
}
|
|
45
|
-
const replaceFilesystemWellKnownValues =
|
|
46
|
-
createReplaceFilesystemWellKnownValues({
|
|
47
|
-
rootDirectoryUrl,
|
|
48
|
-
});
|
|
49
|
-
const sideEffectFileSnapshot = takeFileSnapshot(sideEffectFileUrl);
|
|
50
|
-
const callbackSet = new Set();
|
|
51
|
-
const sideEffectDetectors = [
|
|
52
|
-
...(consoleEffects
|
|
53
|
-
? [
|
|
54
|
-
{
|
|
55
|
-
name: "console",
|
|
56
|
-
install: (addSideEffect) => {
|
|
57
|
-
consoleEffects = { ...consoleEffectsDefault, ...consoleEffects };
|
|
58
|
-
const { prevent } = consoleEffects;
|
|
59
|
-
const onConsole = (methodName, message) => {
|
|
60
|
-
addSideEffect({
|
|
61
|
-
type: `console:${methodName}`,
|
|
62
|
-
value: message,
|
|
63
|
-
label: `console.${methodName}`,
|
|
64
|
-
text: wrapIntoMarkdownBlock(
|
|
65
|
-
replaceFluctuatingValues(message, {
|
|
66
|
-
stringType: "console",
|
|
67
|
-
replaceFilesystemWellKnownValues,
|
|
68
|
-
}),
|
|
69
|
-
"console",
|
|
70
|
-
),
|
|
71
|
-
});
|
|
72
|
-
};
|
|
73
|
-
const consoleSpy = spyConsoleCalls(
|
|
74
|
-
{
|
|
75
|
-
error: (message) => {
|
|
76
|
-
onConsole("error", message);
|
|
77
|
-
},
|
|
78
|
-
warn: (message) => {
|
|
79
|
-
onConsole("warn", message);
|
|
80
|
-
},
|
|
81
|
-
info: (message) => {
|
|
82
|
-
onConsole("info", message);
|
|
83
|
-
},
|
|
84
|
-
log: (message) => {
|
|
85
|
-
onConsole("log", message);
|
|
86
|
-
},
|
|
87
|
-
stdout: (message) => {
|
|
88
|
-
addSideEffect({
|
|
89
|
-
type: `process:stdout`,
|
|
90
|
-
value: message,
|
|
91
|
-
label: `process.stdout`,
|
|
92
|
-
text: wrapIntoMarkdownBlock(
|
|
93
|
-
replaceFluctuatingValues(message, {
|
|
94
|
-
stringType: "console",
|
|
95
|
-
replaceFilesystemWellKnownValues,
|
|
96
|
-
}),
|
|
97
|
-
"console",
|
|
98
|
-
),
|
|
99
|
-
});
|
|
100
|
-
},
|
|
101
|
-
stderr: (message) => {
|
|
102
|
-
addSideEffect({
|
|
103
|
-
type: `process:stderr`,
|
|
104
|
-
value: message,
|
|
105
|
-
label: `process.stderr`,
|
|
106
|
-
text: wrapIntoMarkdownBlock(
|
|
107
|
-
replaceFluctuatingValues(message, {
|
|
108
|
-
stringType: "console",
|
|
109
|
-
replaceFilesystemWellKnownValues,
|
|
110
|
-
}),
|
|
111
|
-
"console",
|
|
112
|
-
),
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
preventConsoleSideEffects: prevent,
|
|
118
|
-
},
|
|
119
|
-
);
|
|
120
|
-
return () => {
|
|
121
|
-
consoleSpy.restore();
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
]
|
|
126
|
-
: []),
|
|
127
|
-
...(filesystemEffects
|
|
128
|
-
? [
|
|
129
|
-
{
|
|
130
|
-
name: "filesystem",
|
|
131
|
-
install: (addSideEffect) => {
|
|
132
|
-
filesystemEffects = {
|
|
133
|
-
...filesystemEffectsDefault,
|
|
134
|
-
...filesystemEffects,
|
|
135
|
-
};
|
|
136
|
-
let writeFile;
|
|
137
|
-
const { include, preserve, baseDirectory, outDirectory } =
|
|
138
|
-
filesystemEffects;
|
|
139
|
-
if (baseDirectory) {
|
|
140
|
-
replaceFilesystemWellKnownValues.addWellKnownFileUrl(
|
|
141
|
-
baseDirectory,
|
|
142
|
-
createWellKnown("base"),
|
|
143
|
-
{ position: "start" },
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
const renderLabel = (label) => {
|
|
147
|
-
return replaceFluctuatingValues(label, {
|
|
148
|
-
replaceFilesystemWellKnownValues,
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
if (outDirectory) {
|
|
153
|
-
const fsEffectsOutDirectoryUrl = ensurePathnameTrailingSlash(
|
|
154
|
-
new URL(outDirectory, sideEffectFileUrl),
|
|
155
|
-
);
|
|
156
|
-
const fsEffectsOutDirectorySnapshot = takeDirectorySnapshot(
|
|
157
|
-
fsEffectsOutDirectoryUrl,
|
|
158
|
-
);
|
|
159
|
-
const writeFileCallbackSet = new Set();
|
|
160
|
-
const getFilesystemActionInfo = (action, url) => {
|
|
161
|
-
let toUrl;
|
|
162
|
-
let urlDisplayed = url;
|
|
163
|
-
if (baseDirectory) {
|
|
164
|
-
urlDisplayed = urlToRelativeUrl(url, baseDirectory, {
|
|
165
|
-
preferRelativeNotation: true,
|
|
166
|
-
});
|
|
167
|
-
if (
|
|
168
|
-
url.href === baseDirectory.href ||
|
|
169
|
-
urlIsInsideOf(url, baseDirectory)
|
|
170
|
-
) {
|
|
171
|
-
const toRelativeUrl = urlToRelativeUrl(
|
|
172
|
-
url,
|
|
173
|
-
baseDirectory,
|
|
174
|
-
);
|
|
175
|
-
toUrl = new URL(toRelativeUrl, fsEffectsOutDirectoryUrl);
|
|
176
|
-
} else {
|
|
177
|
-
const toRelativeUrl =
|
|
178
|
-
replaceFilesystemWellKnownValues(url);
|
|
179
|
-
toUrl = new URL(toRelativeUrl, fsEffectsOutDirectoryUrl);
|
|
180
|
-
}
|
|
181
|
-
// otherwise we need to replace the url with well known
|
|
182
|
-
} else {
|
|
183
|
-
const toRelativeUrl = replaceFilesystemWellKnownValues(url);
|
|
184
|
-
toUrl = new URL(toRelativeUrl, fsEffectsOutDirectoryUrl);
|
|
185
|
-
}
|
|
186
|
-
const toUrlDisplayed = urlToRelativeUrl(
|
|
187
|
-
toUrl,
|
|
188
|
-
sideEffectFileUrl,
|
|
189
|
-
{ preferRelativeNotation: true },
|
|
190
|
-
);
|
|
191
|
-
return {
|
|
192
|
-
toUrl,
|
|
193
|
-
label: renderLabel(
|
|
194
|
-
`${action} "${urlDisplayed}" (see ${toUrlDisplayed})`,
|
|
195
|
-
),
|
|
196
|
-
};
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
callbackSet.add((sideEffects) => {
|
|
200
|
-
// gather all file side effect next to each other
|
|
201
|
-
// collapse them if they have a shared ancestor
|
|
202
|
-
groupFileSideEffectsPerDirectory(sideEffects, {
|
|
203
|
-
baseDirectory,
|
|
204
|
-
getFilesystemActionInfo,
|
|
205
|
-
});
|
|
206
|
-
for (const writeFileCallback of writeFileCallbackSet) {
|
|
207
|
-
writeFileCallback();
|
|
208
|
-
}
|
|
209
|
-
writeFileCallbackSet.clear();
|
|
210
|
-
fsEffectsOutDirectorySnapshot.compare();
|
|
211
|
-
});
|
|
212
|
-
writeFile = (url, content) => {
|
|
213
|
-
const { toUrl, label } = getFilesystemActionInfo(
|
|
214
|
-
"write file",
|
|
215
|
-
url,
|
|
216
|
-
);
|
|
217
|
-
writeFileCallbackSet.add(() => {
|
|
218
|
-
writeFileSync(toUrl, content);
|
|
219
|
-
});
|
|
220
|
-
addSideEffect({
|
|
221
|
-
type: "fs:write_file",
|
|
222
|
-
value: { url: String(url), content },
|
|
223
|
-
label,
|
|
224
|
-
text: null,
|
|
225
|
-
});
|
|
226
|
-
};
|
|
227
|
-
} else {
|
|
228
|
-
writeFile = (url, content) => {
|
|
229
|
-
let urlDisplayed = url;
|
|
230
|
-
if (baseDirectory) {
|
|
231
|
-
urlDisplayed = urlToRelativeUrl(url, baseDirectory, {
|
|
232
|
-
preferRelativeNotation: true,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
addSideEffect({
|
|
236
|
-
type: "fs:write_file",
|
|
237
|
-
value: { url: String(url), content },
|
|
238
|
-
label: renderLabel(`write file "${urlDisplayed}"`),
|
|
239
|
-
text: wrapIntoMarkdownBlock(
|
|
240
|
-
content,
|
|
241
|
-
urlToExtension(url).slice(1),
|
|
242
|
-
),
|
|
243
|
-
});
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
const filesystemSpy = spyFilesystemCalls(
|
|
247
|
-
{
|
|
248
|
-
writeFile,
|
|
249
|
-
writeDirectory: (url) => {
|
|
250
|
-
const writeDirectorySideEffect = addSideEffect({
|
|
251
|
-
type: "fs:write_directory",
|
|
252
|
-
value: { url: String(url) },
|
|
253
|
-
label: renderLabel(`write directory "${url}"`),
|
|
254
|
-
text: null,
|
|
255
|
-
});
|
|
256
|
-
// if directory ends up with something inside we'll not report
|
|
257
|
-
// this side effect because:
|
|
258
|
-
// - it was likely created to write the file
|
|
259
|
-
// - the file creation will be reported and implies directory creation
|
|
260
|
-
filesystemSpy.addBeforeUndoCallback(() => {
|
|
261
|
-
try {
|
|
262
|
-
const dirContent = readDirectorySync(url);
|
|
263
|
-
if (dirContent.length) {
|
|
264
|
-
writeDirectorySideEffect.skippable = true;
|
|
265
|
-
}
|
|
266
|
-
} catch (e) {}
|
|
267
|
-
});
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
include,
|
|
272
|
-
undoFilesystemSideEffects: !preserve,
|
|
273
|
-
},
|
|
274
|
-
);
|
|
275
|
-
return () => {
|
|
276
|
-
filesystemSpy.restore();
|
|
277
|
-
};
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
]
|
|
281
|
-
: []),
|
|
282
|
-
];
|
|
283
|
-
const onSideEffectsCollected = (sideEffects) => {
|
|
284
|
-
for (const callback of callbackSet) {
|
|
285
|
-
callback(sideEffects);
|
|
286
|
-
}
|
|
287
|
-
callbackSet.clear();
|
|
288
|
-
sideEffectFileSnapshot.update(renderSideEffects(sideEffects), {
|
|
289
|
-
mockFluctuatingValues: false,
|
|
290
|
-
});
|
|
291
|
-
};
|
|
292
|
-
const returnValue = collectFunctionSideEffects(fn, sideEffectDetectors, {
|
|
293
|
-
rootDirectoryUrl,
|
|
294
|
-
});
|
|
295
|
-
if (returnValue && typeof returnValue.then === "function") {
|
|
296
|
-
return returnValue.then((sideEffects) => {
|
|
297
|
-
onSideEffectsCollected(sideEffects);
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
onSideEffectsCollected(returnValue);
|
|
301
|
-
return undefined;
|
|
302
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { pathToFileURL } from "node:url";
|
|
2
|
-
import { findCommonAncestorPath } from "./common_ancestor_path.js";
|
|
3
|
-
|
|
4
|
-
export const groupFileSideEffectsPerDirectory = (
|
|
5
|
-
sideEffects,
|
|
6
|
-
{ getFilesystemActionInfo },
|
|
7
|
-
) => {
|
|
8
|
-
const groupArray = groupFileTogether(sideEffects);
|
|
9
|
-
|
|
10
|
-
const convertToPathname = (writeFileSideEffect) => {
|
|
11
|
-
return new URL(writeFileSideEffect.value.url).pathname;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
for (const group of groupArray) {
|
|
15
|
-
if (group.id !== "file") {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
const fileEffectArray = group.values;
|
|
19
|
-
if (fileEffectArray.length < 2) {
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const commonAncestorPath = findCommonAncestorPath(
|
|
23
|
-
fileEffectArray,
|
|
24
|
-
convertToPathname,
|
|
25
|
-
);
|
|
26
|
-
const firstEffect = fileEffectArray[0];
|
|
27
|
-
const firstEffectIndex = sideEffects.indexOf(firstEffect);
|
|
28
|
-
const commonAncestorUrl = pathToFileURL(commonAncestorPath);
|
|
29
|
-
const numberOfFiles = fileEffectArray.length;
|
|
30
|
-
const { label } = getFilesystemActionInfo(
|
|
31
|
-
`write ${numberOfFiles} files into`,
|
|
32
|
-
commonAncestorUrl,
|
|
33
|
-
);
|
|
34
|
-
for (const fileEffect of fileEffectArray) {
|
|
35
|
-
sideEffects.splice(sideEffects.indexOf(fileEffect), 1);
|
|
36
|
-
}
|
|
37
|
-
sideEffects.splice(firstEffectIndex, 0, {
|
|
38
|
-
type: "fs:write_file",
|
|
39
|
-
label,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const groupBy = (array, groupCallback) => {
|
|
45
|
-
let i = 0;
|
|
46
|
-
const groupArray = [];
|
|
47
|
-
let currentGroup = null;
|
|
48
|
-
while (i < array.length) {
|
|
49
|
-
const value = array[i];
|
|
50
|
-
i++;
|
|
51
|
-
let ignoreCalled = false;
|
|
52
|
-
let ignore = () => {
|
|
53
|
-
ignoreCalled = true;
|
|
54
|
-
};
|
|
55
|
-
const groupId = groupCallback(value, { ignore });
|
|
56
|
-
if (ignoreCalled) {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (currentGroup === null) {
|
|
60
|
-
currentGroup = {
|
|
61
|
-
id: groupId,
|
|
62
|
-
values: [value],
|
|
63
|
-
};
|
|
64
|
-
groupArray.push(currentGroup);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (groupId === currentGroup.id) {
|
|
68
|
-
currentGroup.values.push(value);
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
currentGroup = {
|
|
72
|
-
id: groupId,
|
|
73
|
-
values: [value],
|
|
74
|
-
};
|
|
75
|
-
groupArray.push(currentGroup);
|
|
76
|
-
}
|
|
77
|
-
return groupArray;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const groupFileTogether = (sideEffects) =>
|
|
81
|
-
groupBy(sideEffects, (sideEffect, { ignore }) => {
|
|
82
|
-
if (sideEffect.type === "fs:write_directory") {
|
|
83
|
-
ignore();
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
if (sideEffect.type === "fs:write_file") {
|
|
87
|
-
return "file";
|
|
88
|
-
}
|
|
89
|
-
return "other";
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// const groups = groupFileTogether([
|
|
93
|
-
// {
|
|
94
|
-
// name: "a",
|
|
95
|
-
// type: "fs:write_file",
|
|
96
|
-
// },
|
|
97
|
-
// {
|
|
98
|
-
// name: "b",
|
|
99
|
-
// type: "fs:write_directory",
|
|
100
|
-
// },
|
|
101
|
-
// {
|
|
102
|
-
// name: "c",
|
|
103
|
-
// type: "fs:write_file",
|
|
104
|
-
// },
|
|
105
|
-
// {
|
|
106
|
-
// name: "d",
|
|
107
|
-
// type: "other",
|
|
108
|
-
// },
|
|
109
|
-
// {
|
|
110
|
-
// name: "e",
|
|
111
|
-
// type: "fs:write_file",
|
|
112
|
-
// },
|
|
113
|
-
// ]);
|
|
114
|
-
// debugger;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { hookIntoMethod } from "./hook_into_method.js";
|
|
2
|
-
|
|
3
|
-
export const spyConsoleCalls = (
|
|
4
|
-
{ error, warn, info, log, trace, stdout, stderr },
|
|
5
|
-
{ preventConsoleSideEffects },
|
|
6
|
-
) => {
|
|
7
|
-
const restoreCallbackSet = new Set();
|
|
8
|
-
const errorHook = hookIntoMethod(console, "error", (message) => {
|
|
9
|
-
return {
|
|
10
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
11
|
-
return: () => {
|
|
12
|
-
error(message);
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
const warnHook = hookIntoMethod(console, "warn", (message) => {
|
|
17
|
-
return {
|
|
18
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
19
|
-
return: () => {
|
|
20
|
-
warn(message);
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
const infoHook = hookIntoMethod(console, "info", (message) => {
|
|
25
|
-
return {
|
|
26
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
27
|
-
return: () => {
|
|
28
|
-
info(message);
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
const logHook = hookIntoMethod(console, "log", (message) => {
|
|
33
|
-
return {
|
|
34
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
35
|
-
return: () => {
|
|
36
|
-
log(message);
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
const traceHook = hookIntoMethod(console, "trace", (message) => {
|
|
41
|
-
return {
|
|
42
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
43
|
-
return: () => {
|
|
44
|
-
trace(message);
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
const processStdouthook = hookIntoMethod(
|
|
49
|
-
process.stdout,
|
|
50
|
-
"write",
|
|
51
|
-
(message) => {
|
|
52
|
-
return {
|
|
53
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
54
|
-
return: () => {
|
|
55
|
-
stdout(message);
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
);
|
|
60
|
-
const processStderrHhook = hookIntoMethod(
|
|
61
|
-
process.stderr,
|
|
62
|
-
"write",
|
|
63
|
-
(message) => {
|
|
64
|
-
return {
|
|
65
|
-
preventOriginalCall: preventConsoleSideEffects,
|
|
66
|
-
return: () => {
|
|
67
|
-
stderr(message);
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
},
|
|
71
|
-
);
|
|
72
|
-
restoreCallbackSet.add(() => {
|
|
73
|
-
errorHook.remove();
|
|
74
|
-
warnHook.remove();
|
|
75
|
-
infoHook.remove();
|
|
76
|
-
logHook.remove();
|
|
77
|
-
traceHook.remove();
|
|
78
|
-
processStdouthook.remove();
|
|
79
|
-
processStderrHhook.remove();
|
|
80
|
-
});
|
|
81
|
-
return {
|
|
82
|
-
restore: () => {
|
|
83
|
-
for (const restoreCallback of restoreCallbackSet) {
|
|
84
|
-
restoreCallback();
|
|
85
|
-
}
|
|
86
|
-
restoreCallbackSet.clear();
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
};
|
|
File without changes
|
/package/src/{function_side_effects → side_effects/filesystem}/common_ancestor_path.test.mjs
RENAMED
|
File without changes
|
|
File without changes
|