@jsenv/snapshot 2.5.3 → 2.6.1
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 +4 -4
- package/src/replace_fluctuating_values.js +146 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/snapshot",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Snapshot testing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@jsenv/assert": "4.1.6",
|
|
38
|
-
"@jsenv/ast": "6.2.
|
|
38
|
+
"@jsenv/ast": "6.2.8",
|
|
39
39
|
"@jsenv/exception": "1.0.0",
|
|
40
|
-
"@jsenv/filesystem": "4.9.
|
|
41
|
-
"@jsenv/urls": "2.
|
|
40
|
+
"@jsenv/filesystem": "4.9.4",
|
|
41
|
+
"@jsenv/urls": "2.4.0",
|
|
42
42
|
"@jsenv/utils": "2.1.2",
|
|
43
43
|
"pixelmatch": "6.0.0",
|
|
44
44
|
"prettier": "3.3.3",
|
|
@@ -11,8 +11,14 @@ import {
|
|
|
11
11
|
stringifyHtmlAst,
|
|
12
12
|
visitHtmlNodes,
|
|
13
13
|
} from "@jsenv/ast";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
ensurePathnameTrailingSlash,
|
|
16
|
+
removePathnameTrailingSlash,
|
|
17
|
+
urlToExtension,
|
|
18
|
+
} from "@jsenv/urls";
|
|
15
19
|
import { escapeRegexpSpecialChars } from "@jsenv/utils/src/string/escape_regexp_special_chars.js";
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { homedir } from "node:os";
|
|
16
22
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
17
23
|
import stripAnsi from "strip-ansi";
|
|
18
24
|
|
|
@@ -22,35 +28,133 @@ export const replaceFluctuatingValues = (
|
|
|
22
28
|
stringType,
|
|
23
29
|
fileUrl,
|
|
24
30
|
removeAnsi = true,
|
|
25
|
-
rootDirectoryUrl
|
|
31
|
+
rootDirectoryUrl,
|
|
26
32
|
// for unit tests
|
|
27
|
-
|
|
33
|
+
ancestorPackagesDisabled,
|
|
34
|
+
ancestorPackagesRootDirectoryUrl = "file:///",
|
|
35
|
+
homedirDisabled,
|
|
36
|
+
cwdPath = process.cwd(),
|
|
37
|
+
cwdUrl,
|
|
28
38
|
isWindows = process.platform === "win32",
|
|
29
39
|
} = {},
|
|
30
40
|
) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
const wellKownUrlArray = [];
|
|
42
|
+
const wellKnownPathArray = [];
|
|
43
|
+
const addWellKnownFileUrl = (url, replacement) => {
|
|
44
|
+
const urlWithoutTrailingSlash = removePathnameTrailingSlash(url);
|
|
45
|
+
wellKownUrlArray.push({
|
|
46
|
+
url: urlWithoutTrailingSlash,
|
|
47
|
+
replacement,
|
|
48
|
+
replace: (string) =>
|
|
49
|
+
string.replaceAll(urlWithoutTrailingSlash, replacement),
|
|
50
|
+
});
|
|
51
|
+
const path =
|
|
52
|
+
url === cwdUrl ? cwdPath : fileURLToPath(urlWithoutTrailingSlash);
|
|
53
|
+
const windowPathRegex = new RegExp(
|
|
54
|
+
`${escapeRegexpSpecialChars(path)}(((?:\\\\(?:[\\w !#()-]+|[.]{1,2})+)*)(?:\\\\)?)`,
|
|
55
|
+
"gm",
|
|
56
|
+
);
|
|
57
|
+
const pathReplacement = replacement.startsWith("file:///")
|
|
58
|
+
? replacement.slice("file:///".length)
|
|
59
|
+
: replacement;
|
|
60
|
+
wellKnownPathArray.push({
|
|
61
|
+
path,
|
|
62
|
+
replacement: pathReplacement,
|
|
63
|
+
replace: isWindows
|
|
64
|
+
? (string) => {
|
|
65
|
+
return string.replaceAll(windowPathRegex, (match, after) => {
|
|
66
|
+
return `${pathReplacement}${after.replaceAll("\\", "/")}`;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
: (string) => string.replaceAll(path, pathReplacement),
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
if (rootDirectoryUrl) {
|
|
73
|
+
addWellKnownFileUrl(rootDirectoryUrl, "file:///<root>");
|
|
74
|
+
}
|
|
75
|
+
/*
|
|
76
|
+
* When running code inside a node project ancestor packages
|
|
77
|
+
* should make things super predictible because
|
|
78
|
+
* it will use a package.json name field
|
|
79
|
+
* to replace files urls
|
|
80
|
+
* And uses the highest ancestor package so that even if the file
|
|
81
|
+
* is executed once within a package then outside that package
|
|
82
|
+
* the replace value remains predictible as the highest package is used
|
|
83
|
+
* The highest package is used because it's pushed first by
|
|
84
|
+
* addWellKnownFileUrl
|
|
85
|
+
*/
|
|
86
|
+
ancestor_packages: {
|
|
87
|
+
if (ancestorPackagesDisabled) {
|
|
88
|
+
break ancestor_packages;
|
|
89
|
+
}
|
|
90
|
+
const ancestorPackages = [];
|
|
91
|
+
const cwd = cwdPath || process.cwd();
|
|
92
|
+
const cwdUrl = ensurePathnameTrailingSlash(pathToFileURL(cwd));
|
|
93
|
+
let currentUrl = cwdUrl;
|
|
94
|
+
while (currentUrl.href !== ancestorPackagesRootDirectoryUrl) {
|
|
95
|
+
const packageFileUrl = new URL("package.json", currentUrl);
|
|
96
|
+
const packageDirectoryUrl = currentUrl;
|
|
97
|
+
currentUrl = new URL(getParentUrl(currentUrl));
|
|
98
|
+
let packageFileContent;
|
|
99
|
+
try {
|
|
100
|
+
packageFileContent = readFileSync(packageFileUrl);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
if (e.code === "ENOENT") {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
throw e;
|
|
106
|
+
}
|
|
107
|
+
let packageObject;
|
|
108
|
+
try {
|
|
109
|
+
packageObject = JSON.parse(packageFileContent);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const packageName = packageObject.name;
|
|
114
|
+
ancestorPackages.unshift({
|
|
115
|
+
packageDirectoryUrl,
|
|
116
|
+
packageName,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
for (const ancestorPackage of ancestorPackages) {
|
|
120
|
+
addWellKnownFileUrl(
|
|
121
|
+
ancestorPackage.packageDirectoryUrl,
|
|
122
|
+
ancestorPackage.packageName,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
home_dir: {
|
|
127
|
+
if (!homedirDisabled) {
|
|
128
|
+
const homedirPath = homedir();
|
|
129
|
+
const homedirUrl = pathToFileURL(homedirPath);
|
|
130
|
+
addWellKnownFileUrl(homedirUrl, "file:///~");
|
|
131
|
+
}
|
|
34
132
|
}
|
|
35
|
-
|
|
36
|
-
|
|
133
|
+
process_cwd: {
|
|
134
|
+
// we fallback on process.cwd()
|
|
135
|
+
// but it's brittle because a file might be execute from anywhere
|
|
136
|
+
// so it should be the last resort
|
|
137
|
+
cwdUrl = cwdUrl || pathToFileURL(cwdPath);
|
|
138
|
+
addWellKnownFileUrl(cwdUrl, "file:///cwd()");
|
|
37
139
|
}
|
|
38
140
|
const replaceFileUrls = (value) => {
|
|
39
|
-
|
|
141
|
+
for (const wellKownUrl of wellKownUrlArray) {
|
|
142
|
+
const replaceResult = wellKownUrl.replace(value);
|
|
143
|
+
if (replaceResult !== value) {
|
|
144
|
+
return replaceResult;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
40
148
|
};
|
|
41
|
-
const replaceFilePaths =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
47
|
-
return value.replaceAll(windowPathRegex, (match, afterCwd) => {
|
|
48
|
-
return `cwd()${afterCwd.replaceAll("\\", "/")}`;
|
|
49
|
-
});
|
|
149
|
+
const replaceFilePaths = (value) => {
|
|
150
|
+
for (const wellKownPath of wellKnownPathArray) {
|
|
151
|
+
const replaceResult = wellKownPath.replace(value);
|
|
152
|
+
if (replaceResult !== value) {
|
|
153
|
+
return replaceResult;
|
|
50
154
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
};
|
|
54
158
|
const replaceThings = (value) => {
|
|
55
159
|
if (removeAnsi) {
|
|
56
160
|
value = stripAnsi(value);
|
|
@@ -100,6 +204,27 @@ export const replaceFluctuatingValues = (
|
|
|
100
204
|
return replaceThings(string);
|
|
101
205
|
};
|
|
102
206
|
|
|
207
|
+
const getParentUrl = (url) => {
|
|
208
|
+
url = String(url);
|
|
209
|
+
// With node.js new URL('../', 'file:///C:/').href
|
|
210
|
+
// returns "file:///C:/" instead of "file:///"
|
|
211
|
+
const resource = url.slice("file://".length);
|
|
212
|
+
const slashLastIndex = resource.lastIndexOf("/");
|
|
213
|
+
if (slashLastIndex === -1) {
|
|
214
|
+
return url;
|
|
215
|
+
}
|
|
216
|
+
const lastCharIndex = resource.length - 1;
|
|
217
|
+
if (slashLastIndex === lastCharIndex) {
|
|
218
|
+
const slashBeforeLastIndex = resource.lastIndexOf("/", slashLastIndex - 1);
|
|
219
|
+
if (slashBeforeLastIndex === -1) {
|
|
220
|
+
return url;
|
|
221
|
+
}
|
|
222
|
+
return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return `file://${resource.slice(0, slashLastIndex + 1)}`;
|
|
226
|
+
};
|
|
227
|
+
|
|
103
228
|
const replaceHttpUrls = (source) => {
|
|
104
229
|
return source.replace(/(?:https?|ftp):\/\/\S+[\w/]/g, (match) => {
|
|
105
230
|
const lastChar = match[match.length - 1];
|