@jsenv/test 3.6.12 → 3.7.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.
- package/dist/exception.js +955 -0
- package/dist/execute_using_dynamic_import.js +1 -1
- package/dist/js/node_child_process_controlled.mjs +5 -1
- package/dist/js/node_worker_thread_controlled.mjs +5 -1
- package/dist/jsenv_test.js +1613 -678
- package/dist/jsenv_test_node_modules.js +588 -1
- package/package.json +22 -17
- package/src/runtime_node/child_exec_options.js +1 -2
package/dist/jsenv_test.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, readFileSync, writeFileSync as writeFileSync$1, unlink, rmdir, existsSync } from "node:fs";
|
|
1
|
+
import { createSupportsColor, isUnicodeSupported, stripAnsi, emojiRegex, eastAsianWidth, clearTerminal, eraseLines } from "./jsenv_test_node_modules.js";
|
|
2
|
+
import { URL_META, createException } from "./exception.js";
|
|
3
|
+
import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, readFileSync, writeFileSync as writeFileSync$1, unlink, rmdir, existsSync, realpathSync } from "node:fs";
|
|
4
4
|
import { takeCoverage } from "node:v8";
|
|
5
|
-
import stripAnsi from "strip-ansi";
|
|
6
5
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
7
6
|
import { createRequire } from "node:module";
|
|
8
7
|
import { applyBabelPlugins } from "@jsenv/ast";
|
|
9
8
|
import { cpuUsage, memoryUsage } from "node:process";
|
|
10
|
-
import {
|
|
11
|
-
import { measureTextWidth } from "@jsenv/terminal-text-size";
|
|
12
|
-
import { createException } from "@jsenv/exception";
|
|
9
|
+
import { stripVTControlCharacters } from "node:util";
|
|
13
10
|
import crypto from "node:crypto";
|
|
14
11
|
import { dirname } from "node:path";
|
|
15
12
|
import { createServer } from "node:net";
|
|
@@ -17,7 +14,6 @@ import { spawn, spawnSync, fork } from "node:child_process";
|
|
|
17
14
|
import { availableParallelism, cpus, totalmem, release, freemem } from "node:os";
|
|
18
15
|
import { SOURCEMAP, generateSourcemapDataUrl } from "@jsenv/sourcemap";
|
|
19
16
|
import { injectSupervisorIntoHTML, supervisorFileUrl } from "@jsenv/plugin-supervisor";
|
|
20
|
-
import { findFreePort } from "@jsenv/server";
|
|
21
17
|
import { Worker } from "node:worker_threads";
|
|
22
18
|
import he from "he";
|
|
23
19
|
import "node:tty";
|
|
@@ -1333,6 +1329,101 @@ const error = (...args) => console.error(...args);
|
|
|
1333
1329
|
|
|
1334
1330
|
const errorDisabled = () => {};
|
|
1335
1331
|
|
|
1332
|
+
const createMeasureTextWidth = ({ stripAnsi }) => {
|
|
1333
|
+
const segmenter = new Intl.Segmenter();
|
|
1334
|
+
const defaultIgnorableCodePointRegex = /^\p{Default_Ignorable_Code_Point}$/u;
|
|
1335
|
+
|
|
1336
|
+
const measureTextWidth = (
|
|
1337
|
+
string,
|
|
1338
|
+
{
|
|
1339
|
+
ambiguousIsNarrow = true,
|
|
1340
|
+
countAnsiEscapeCodes = false,
|
|
1341
|
+
skipEmojis = false,
|
|
1342
|
+
} = {},
|
|
1343
|
+
) => {
|
|
1344
|
+
if (typeof string !== "string" || string.length === 0) {
|
|
1345
|
+
return 0;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
if (!countAnsiEscapeCodes) {
|
|
1349
|
+
string = stripAnsi(string);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
if (string.length === 0) {
|
|
1353
|
+
return 0;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
let width = 0;
|
|
1357
|
+
const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
|
|
1358
|
+
|
|
1359
|
+
for (const { segment: character } of segmenter.segment(string)) {
|
|
1360
|
+
const codePoint = character.codePointAt(0);
|
|
1361
|
+
|
|
1362
|
+
// Ignore control characters
|
|
1363
|
+
if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Ignore zero-width characters
|
|
1368
|
+
if (
|
|
1369
|
+
(codePoint >= 0x20_0b && codePoint <= 0x20_0f) || // Zero-width space, non-joiner, joiner, left-to-right mark, right-to-left mark
|
|
1370
|
+
codePoint === 0xfe_ff // Zero-width no-break space
|
|
1371
|
+
) {
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// Ignore combining characters
|
|
1376
|
+
if (
|
|
1377
|
+
(codePoint >= 0x3_00 && codePoint <= 0x3_6f) || // Combining diacritical marks
|
|
1378
|
+
(codePoint >= 0x1a_b0 && codePoint <= 0x1a_ff) || // Combining diacritical marks extended
|
|
1379
|
+
(codePoint >= 0x1d_c0 && codePoint <= 0x1d_ff) || // Combining diacritical marks supplement
|
|
1380
|
+
(codePoint >= 0x20_d0 && codePoint <= 0x20_ff) || // Combining diacritical marks for symbols
|
|
1381
|
+
(codePoint >= 0xfe_20 && codePoint <= 0xfe_2f) // Combining half marks
|
|
1382
|
+
) {
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Ignore surrogate pairs
|
|
1387
|
+
if (codePoint >= 0xd8_00 && codePoint <= 0xdf_ff) {
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// Ignore variation selectors
|
|
1392
|
+
if (codePoint >= 0xfe_00 && codePoint <= 0xfe_0f) {
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// This covers some of the above cases, but we still keep them for performance reasons.
|
|
1397
|
+
if (defaultIgnorableCodePointRegex.test(character)) {
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (!skipEmojis && emojiRegex().test(character)) {
|
|
1402
|
+
if (process.env.CAPTURING_SIDE_EFFECTS) {
|
|
1403
|
+
if (character === "✔️") {
|
|
1404
|
+
width += 2;
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
width += measureTextWidth(character, {
|
|
1409
|
+
skipEmojis: true,
|
|
1410
|
+
countAnsiEscapeCodes: true, // to skip call to stripAnsi
|
|
1411
|
+
});
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
width += eastAsianWidth(codePoint, eastAsianWidthOptions);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
return width;
|
|
1419
|
+
};
|
|
1420
|
+
return measureTextWidth;
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
const measureTextWidth = createMeasureTextWidth({
|
|
1424
|
+
stripAnsi: stripVTControlCharacters,
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1336
1427
|
/*
|
|
1337
1428
|
* see also https://github.com/vadimdemedes/ink
|
|
1338
1429
|
*/
|
|
@@ -2068,609 +2159,6 @@ const findSelfOrAncestorDirectoryUrl = (url, callback) => {
|
|
|
2068
2159
|
return null;
|
|
2069
2160
|
};
|
|
2070
2161
|
|
|
2071
|
-
/*
|
|
2072
|
-
* Link to things doing pattern matching:
|
|
2073
|
-
* https://git-scm.com/docs/gitignore
|
|
2074
|
-
* https://github.com/kaelzhang/node-ignore
|
|
2075
|
-
*/
|
|
2076
|
-
|
|
2077
|
-
/** @module jsenv_url_meta **/
|
|
2078
|
-
/**
|
|
2079
|
-
* An object representing the result of applying a pattern to an url
|
|
2080
|
-
* @typedef {Object} MatchResult
|
|
2081
|
-
* @property {boolean} matched Indicates if url matched pattern
|
|
2082
|
-
* @property {number} patternIndex Index where pattern stopped matching url, otherwise pattern.length
|
|
2083
|
-
* @property {number} urlIndex Index where url stopped matching pattern, otherwise url.length
|
|
2084
|
-
* @property {Array} matchGroups Array of strings captured during pattern matching
|
|
2085
|
-
*/
|
|
2086
|
-
|
|
2087
|
-
/**
|
|
2088
|
-
* Apply a pattern to an url
|
|
2089
|
-
* @param {Object} applyPatternMatchingParams
|
|
2090
|
-
* @param {string} applyPatternMatchingParams.pattern "*", "**" and trailing slash have special meaning
|
|
2091
|
-
* @param {string} applyPatternMatchingParams.url a string representing an url
|
|
2092
|
-
* @return {MatchResult}
|
|
2093
|
-
*/
|
|
2094
|
-
const applyPattern = ({ url, pattern }) => {
|
|
2095
|
-
const { matched, patternIndex, index, groups } = applyMatching(pattern, url);
|
|
2096
|
-
const matchGroups = [];
|
|
2097
|
-
let groupIndex = 0;
|
|
2098
|
-
for (const group of groups) {
|
|
2099
|
-
if (group.name) {
|
|
2100
|
-
matchGroups[group.name] = group.string;
|
|
2101
|
-
} else {
|
|
2102
|
-
matchGroups[groupIndex] = group.string;
|
|
2103
|
-
groupIndex++;
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
return {
|
|
2107
|
-
matched,
|
|
2108
|
-
patternIndex,
|
|
2109
|
-
urlIndex: index,
|
|
2110
|
-
matchGroups,
|
|
2111
|
-
};
|
|
2112
|
-
};
|
|
2113
|
-
|
|
2114
|
-
/**
|
|
2115
|
-
* Core pattern matching function that processes patterns against strings
|
|
2116
|
-
* @param {string} pattern - The pattern with special syntax (*,**,/) to match against
|
|
2117
|
-
* @param {string} string - The string to test against the pattern
|
|
2118
|
-
* @returns {Object} Result containing match status and capture groups
|
|
2119
|
-
* @private
|
|
2120
|
-
*/
|
|
2121
|
-
const applyMatching = (pattern, string) => {
|
|
2122
|
-
const groups = [];
|
|
2123
|
-
let patternIndex = 0;
|
|
2124
|
-
let index = 0;
|
|
2125
|
-
let remainingPattern = pattern;
|
|
2126
|
-
let remainingString = string;
|
|
2127
|
-
let restoreIndexes = true;
|
|
2128
|
-
|
|
2129
|
-
const consumePattern = (count) => {
|
|
2130
|
-
const subpattern = remainingPattern.slice(0, count);
|
|
2131
|
-
remainingPattern = remainingPattern.slice(count);
|
|
2132
|
-
patternIndex += count;
|
|
2133
|
-
return subpattern;
|
|
2134
|
-
};
|
|
2135
|
-
const consumeString = (count) => {
|
|
2136
|
-
const substring = remainingString.slice(0, count);
|
|
2137
|
-
remainingString = remainingString.slice(count);
|
|
2138
|
-
index += count;
|
|
2139
|
-
return substring;
|
|
2140
|
-
};
|
|
2141
|
-
const consumeRemainingString = () => {
|
|
2142
|
-
return consumeString(remainingString.length);
|
|
2143
|
-
};
|
|
2144
|
-
|
|
2145
|
-
const matchOne = () => {
|
|
2146
|
-
// pattern consumed
|
|
2147
|
-
if (remainingPattern === "") {
|
|
2148
|
-
if (remainingString === "") {
|
|
2149
|
-
return true; // string fully matched pattern
|
|
2150
|
-
}
|
|
2151
|
-
if (remainingString[0] === "?") {
|
|
2152
|
-
// match search params
|
|
2153
|
-
consumeRemainingString();
|
|
2154
|
-
return true;
|
|
2155
|
-
}
|
|
2156
|
-
return false; // fails because string longer than expect
|
|
2157
|
-
}
|
|
2158
|
-
|
|
2159
|
-
// -- from this point pattern is not consumed --
|
|
2160
|
-
// string consumed, pattern not consumed
|
|
2161
|
-
if (remainingString === "") {
|
|
2162
|
-
if (remainingPattern === "**") {
|
|
2163
|
-
// trailing "**" is optional
|
|
2164
|
-
consumePattern(2);
|
|
2165
|
-
return true;
|
|
2166
|
-
}
|
|
2167
|
-
if (remainingPattern === "*") {
|
|
2168
|
-
groups.push({ string: "" });
|
|
2169
|
-
}
|
|
2170
|
-
return false; // fail because string shorter than expect
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
// -- from this point pattern and string are not consumed --
|
|
2174
|
-
// fast path trailing slash
|
|
2175
|
-
if (remainingPattern === "/") {
|
|
2176
|
-
if (remainingString[0] === "/") {
|
|
2177
|
-
// trailing slash match remaining
|
|
2178
|
-
consumePattern(1);
|
|
2179
|
-
groups.push({ string: consumeRemainingString() });
|
|
2180
|
-
return true;
|
|
2181
|
-
}
|
|
2182
|
-
return false;
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
// fast path trailing '**'
|
|
2186
|
-
if (remainingPattern === "**") {
|
|
2187
|
-
consumePattern(2);
|
|
2188
|
-
consumeRemainingString();
|
|
2189
|
-
return true;
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
if (remainingPattern.slice(0, 4) === "/**/") {
|
|
2193
|
-
consumePattern(3); // consumes "/**/"
|
|
2194
|
-
const skipResult = skipUntilMatchIterative({
|
|
2195
|
-
pattern: remainingPattern,
|
|
2196
|
-
string: remainingString,
|
|
2197
|
-
canSkipSlash: true,
|
|
2198
|
-
});
|
|
2199
|
-
for (let i = 0; i < skipResult.groups.length; i++) {
|
|
2200
|
-
groups.push(skipResult.groups[i]);
|
|
2201
|
-
}
|
|
2202
|
-
consumePattern(skipResult.patternIndex);
|
|
2203
|
-
consumeRemainingString();
|
|
2204
|
-
restoreIndexes = false;
|
|
2205
|
-
return skipResult.matched;
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
// pattern leading **
|
|
2209
|
-
if (remainingPattern.slice(0, 2) === "**") {
|
|
2210
|
-
consumePattern(2); // consumes "**"
|
|
2211
|
-
let skipAllowed = true;
|
|
2212
|
-
if (remainingPattern[0] === "/") {
|
|
2213
|
-
consumePattern(1); // consumes "/"
|
|
2214
|
-
// when remainingPattern was preceeded by "**/"
|
|
2215
|
-
// and remainingString have no "/"
|
|
2216
|
-
// then skip is not allowed, a regular match will be performed
|
|
2217
|
-
if (remainingString.includes("/")) {
|
|
2218
|
-
skipAllowed = false;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
// pattern ending with "**" or "**/" match remaining string
|
|
2222
|
-
if (remainingPattern === "") {
|
|
2223
|
-
consumeRemainingString();
|
|
2224
|
-
return true;
|
|
2225
|
-
}
|
|
2226
|
-
if (skipAllowed) {
|
|
2227
|
-
const skipResult = skipUntilMatchIterative({
|
|
2228
|
-
pattern: remainingPattern,
|
|
2229
|
-
string: remainingString,
|
|
2230
|
-
canSkipSlash: true,
|
|
2231
|
-
});
|
|
2232
|
-
for (let i = 0; i < skipResult.groups.length; i++) {
|
|
2233
|
-
groups.push(skipResult.groups[i]);
|
|
2234
|
-
}
|
|
2235
|
-
consumePattern(skipResult.patternIndex);
|
|
2236
|
-
consumeRemainingString();
|
|
2237
|
-
restoreIndexes = false;
|
|
2238
|
-
return skipResult.matched;
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
if (remainingPattern[0] === "*") {
|
|
2243
|
-
consumePattern(1); // consumes "*"
|
|
2244
|
-
if (remainingPattern === "") {
|
|
2245
|
-
// matches everything except "/"
|
|
2246
|
-
const slashIndex = remainingString.indexOf("/");
|
|
2247
|
-
if (slashIndex === -1) {
|
|
2248
|
-
groups.push({ string: consumeRemainingString() });
|
|
2249
|
-
return true;
|
|
2250
|
-
}
|
|
2251
|
-
groups.push({ string: consumeString(slashIndex) });
|
|
2252
|
-
return false;
|
|
2253
|
-
}
|
|
2254
|
-
// the next char must not the one expect by remainingPattern[0]
|
|
2255
|
-
// because * is greedy and expect to skip at least one char
|
|
2256
|
-
if (remainingPattern[0] === remainingString[0]) {
|
|
2257
|
-
groups.push({ string: "" });
|
|
2258
|
-
patternIndex = patternIndex - 1;
|
|
2259
|
-
return false;
|
|
2260
|
-
}
|
|
2261
|
-
const skipResult = skipUntilMatchIterative({
|
|
2262
|
-
pattern: remainingPattern,
|
|
2263
|
-
string: remainingString,
|
|
2264
|
-
canSkipSlash: false,
|
|
2265
|
-
});
|
|
2266
|
-
groups.push(skipResult.group);
|
|
2267
|
-
for (let i = 0; i < skipResult.groups.length; i++) {
|
|
2268
|
-
groups.push(skipResult.groups[i]);
|
|
2269
|
-
}
|
|
2270
|
-
consumePattern(skipResult.patternIndex);
|
|
2271
|
-
consumeString(skipResult.index);
|
|
2272
|
-
restoreIndexes = false;
|
|
2273
|
-
return skipResult.matched;
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
if (remainingPattern[0] !== remainingString[0]) {
|
|
2277
|
-
return false;
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
return undefined;
|
|
2281
|
-
};
|
|
2282
|
-
|
|
2283
|
-
// Replace recursive iterate() with iterative approach
|
|
2284
|
-
let matched;
|
|
2285
|
-
let continueIteration = true;
|
|
2286
|
-
|
|
2287
|
-
while (continueIteration) {
|
|
2288
|
-
const patternIndexBefore = patternIndex;
|
|
2289
|
-
const indexBefore = index;
|
|
2290
|
-
|
|
2291
|
-
matched = matchOne();
|
|
2292
|
-
|
|
2293
|
-
if (matched === undefined) {
|
|
2294
|
-
consumePattern(1);
|
|
2295
|
-
consumeString(1);
|
|
2296
|
-
// Continue the loop instead of recursion
|
|
2297
|
-
continue;
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
if (matched === false && restoreIndexes) {
|
|
2301
|
-
patternIndex = patternIndexBefore;
|
|
2302
|
-
index = indexBefore;
|
|
2303
|
-
}
|
|
2304
|
-
|
|
2305
|
-
// End the loop
|
|
2306
|
-
continueIteration = false;
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
return {
|
|
2310
|
-
matched,
|
|
2311
|
-
patternIndex,
|
|
2312
|
-
index,
|
|
2313
|
-
groups,
|
|
2314
|
-
};
|
|
2315
|
-
};
|
|
2316
|
-
|
|
2317
|
-
/**
|
|
2318
|
-
* Iterative version of skipUntilMatch that avoids recursion
|
|
2319
|
-
* @param {Object} params
|
|
2320
|
-
* @param {string} params.pattern - The pattern to match
|
|
2321
|
-
* @param {string} params.string - The string to test against
|
|
2322
|
-
* @param {boolean} params.canSkipSlash - Whether slash characters can be skipped
|
|
2323
|
-
* @returns {Object} Result of the matching attempt
|
|
2324
|
-
*/
|
|
2325
|
-
const skipUntilMatchIterative = ({ pattern, string, canSkipSlash }) => {
|
|
2326
|
-
let index = 0;
|
|
2327
|
-
let remainingString = string;
|
|
2328
|
-
let longestAttemptRange = null;
|
|
2329
|
-
let isLastAttempt = false;
|
|
2330
|
-
|
|
2331
|
-
const failure = () => {
|
|
2332
|
-
return {
|
|
2333
|
-
matched: false,
|
|
2334
|
-
patternIndex: longestAttemptRange.patternIndex,
|
|
2335
|
-
index: longestAttemptRange.index + longestAttemptRange.length,
|
|
2336
|
-
groups: longestAttemptRange.groups,
|
|
2337
|
-
group: {
|
|
2338
|
-
string: string.slice(0, longestAttemptRange.index),
|
|
2339
|
-
},
|
|
2340
|
-
};
|
|
2341
|
-
};
|
|
2342
|
-
|
|
2343
|
-
// Loop until a match is found or all attempts fail
|
|
2344
|
-
while (true) {
|
|
2345
|
-
const matchAttempt = applyMatching(pattern, remainingString);
|
|
2346
|
-
|
|
2347
|
-
if (matchAttempt.matched) {
|
|
2348
|
-
return {
|
|
2349
|
-
matched: true,
|
|
2350
|
-
patternIndex: matchAttempt.patternIndex,
|
|
2351
|
-
index: index + matchAttempt.index,
|
|
2352
|
-
groups: matchAttempt.groups,
|
|
2353
|
-
group: {
|
|
2354
|
-
string:
|
|
2355
|
-
remainingString === ""
|
|
2356
|
-
? string
|
|
2357
|
-
: string.slice(0, -remainingString.length),
|
|
2358
|
-
},
|
|
2359
|
-
};
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
|
-
const attemptIndex = matchAttempt.index;
|
|
2363
|
-
const attemptRange = {
|
|
2364
|
-
patternIndex: matchAttempt.patternIndex,
|
|
2365
|
-
index,
|
|
2366
|
-
length: attemptIndex,
|
|
2367
|
-
groups: matchAttempt.groups,
|
|
2368
|
-
};
|
|
2369
|
-
|
|
2370
|
-
if (
|
|
2371
|
-
!longestAttemptRange ||
|
|
2372
|
-
longestAttemptRange.length < attemptRange.length
|
|
2373
|
-
) {
|
|
2374
|
-
longestAttemptRange = attemptRange;
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
|
-
if (isLastAttempt) {
|
|
2378
|
-
return failure();
|
|
2379
|
-
}
|
|
2380
|
-
|
|
2381
|
-
const nextIndex = attemptIndex + 1;
|
|
2382
|
-
if (nextIndex >= remainingString.length) {
|
|
2383
|
-
return failure();
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2386
|
-
if (remainingString[0] === "/") {
|
|
2387
|
-
if (!canSkipSlash) {
|
|
2388
|
-
return failure();
|
|
2389
|
-
}
|
|
2390
|
-
// when it's the last slash, the next attempt is the last
|
|
2391
|
-
if (remainingString.indexOf("/", 1) === -1) {
|
|
2392
|
-
isLastAttempt = true;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
// search against the next unattempted string
|
|
2397
|
-
index += nextIndex;
|
|
2398
|
-
remainingString = remainingString.slice(nextIndex);
|
|
2399
|
-
}
|
|
2400
|
-
};
|
|
2401
|
-
|
|
2402
|
-
const applyPatternMatching = ({ url, pattern }) => {
|
|
2403
|
-
assertUrlLike(pattern, "pattern");
|
|
2404
|
-
if (url && typeof url.href === "string") url = url.href;
|
|
2405
|
-
assertUrlLike(url, "url");
|
|
2406
|
-
return applyPattern({ url, pattern });
|
|
2407
|
-
};
|
|
2408
|
-
|
|
2409
|
-
const resolveAssociations = (associations, resolver) => {
|
|
2410
|
-
let resolve = () => {};
|
|
2411
|
-
if (typeof resolver === "function") {
|
|
2412
|
-
resolve = resolver;
|
|
2413
|
-
} else if (typeof resolver === "string") {
|
|
2414
|
-
const baseUrl = resolver;
|
|
2415
|
-
assertUrlLike(baseUrl, "baseUrl");
|
|
2416
|
-
resolve = (pattern) => new URL(pattern, baseUrl).href;
|
|
2417
|
-
} else if (resolver && typeof resolver.href === "string") {
|
|
2418
|
-
const baseUrl = resolver.href;
|
|
2419
|
-
assertUrlLike(baseUrl, "baseUrl");
|
|
2420
|
-
resolve = (pattern) => new URL(pattern, baseUrl).href;
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
const associationsResolved = {};
|
|
2424
|
-
for (const key of Object.keys(associations)) {
|
|
2425
|
-
const value = associations[key];
|
|
2426
|
-
if (typeof value === "object" && value !== null) {
|
|
2427
|
-
const valueMapResolved = {};
|
|
2428
|
-
for (const pattern of Object.keys(value)) {
|
|
2429
|
-
const valueAssociated = value[pattern];
|
|
2430
|
-
let patternResolved;
|
|
2431
|
-
try {
|
|
2432
|
-
patternResolved = resolve(pattern);
|
|
2433
|
-
} catch {
|
|
2434
|
-
// it's not really an url, no need to perform url resolution nor encoding
|
|
2435
|
-
patternResolved = pattern;
|
|
2436
|
-
}
|
|
2437
|
-
valueMapResolved[patternResolved] = valueAssociated;
|
|
2438
|
-
}
|
|
2439
|
-
associationsResolved[key] = valueMapResolved;
|
|
2440
|
-
} else {
|
|
2441
|
-
associationsResolved[key] = value;
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
return associationsResolved;
|
|
2445
|
-
};
|
|
2446
|
-
|
|
2447
|
-
const asFlatAssociations = (associations) => {
|
|
2448
|
-
if (!isPlainObject(associations)) {
|
|
2449
|
-
throw new TypeError(
|
|
2450
|
-
`associations must be a plain object, got ${associations}`,
|
|
2451
|
-
);
|
|
2452
|
-
}
|
|
2453
|
-
const flatAssociations = {};
|
|
2454
|
-
for (const associationName of Object.keys(associations)) {
|
|
2455
|
-
const associationValue = associations[associationName];
|
|
2456
|
-
if (!isPlainObject(associationValue)) {
|
|
2457
|
-
continue;
|
|
2458
|
-
}
|
|
2459
|
-
for (const pattern of Object.keys(associationValue)) {
|
|
2460
|
-
const patternValue = associationValue[pattern];
|
|
2461
|
-
const previousValue = flatAssociations[pattern];
|
|
2462
|
-
if (isPlainObject(previousValue)) {
|
|
2463
|
-
flatAssociations[pattern] = {
|
|
2464
|
-
...previousValue,
|
|
2465
|
-
[associationName]: patternValue,
|
|
2466
|
-
};
|
|
2467
|
-
} else {
|
|
2468
|
-
flatAssociations[pattern] = {
|
|
2469
|
-
[associationName]: patternValue,
|
|
2470
|
-
};
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
|
-
return flatAssociations;
|
|
2475
|
-
};
|
|
2476
|
-
|
|
2477
|
-
const applyAssociations = ({ url, associations }) => {
|
|
2478
|
-
if (url && typeof url.href === "string") url = url.href;
|
|
2479
|
-
assertUrlLike(url);
|
|
2480
|
-
const flatAssociations = asFlatAssociations(associations);
|
|
2481
|
-
let associatedValue = {};
|
|
2482
|
-
for (const pattern of Object.keys(flatAssociations)) {
|
|
2483
|
-
const { matched } = applyPatternMatching({
|
|
2484
|
-
pattern,
|
|
2485
|
-
url,
|
|
2486
|
-
});
|
|
2487
|
-
if (matched) {
|
|
2488
|
-
const value = flatAssociations[pattern];
|
|
2489
|
-
associatedValue = deepAssign(associatedValue, value);
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
return associatedValue;
|
|
2493
|
-
};
|
|
2494
|
-
|
|
2495
|
-
const deepAssign = (firstValue, secondValue) => {
|
|
2496
|
-
if (!isPlainObject(firstValue)) {
|
|
2497
|
-
if (isPlainObject(secondValue)) {
|
|
2498
|
-
return deepAssign({}, secondValue);
|
|
2499
|
-
}
|
|
2500
|
-
return secondValue;
|
|
2501
|
-
}
|
|
2502
|
-
if (!isPlainObject(secondValue)) {
|
|
2503
|
-
return secondValue;
|
|
2504
|
-
}
|
|
2505
|
-
for (const key of Object.keys(secondValue)) {
|
|
2506
|
-
const leftPopertyValue = firstValue[key];
|
|
2507
|
-
const rightPropertyValue = secondValue[key];
|
|
2508
|
-
firstValue[key] = deepAssign(leftPopertyValue, rightPropertyValue);
|
|
2509
|
-
}
|
|
2510
|
-
return firstValue;
|
|
2511
|
-
};
|
|
2512
|
-
|
|
2513
|
-
const urlChildMayMatch = ({ url, associations, predicate }) => {
|
|
2514
|
-
if (url && typeof url.href === "string") url = url.href;
|
|
2515
|
-
assertUrlLike(url, "url");
|
|
2516
|
-
// the function was meants to be used on url ending with '/'
|
|
2517
|
-
if (!url.endsWith("/")) {
|
|
2518
|
-
throw new Error(`url should end with /, got ${url}`);
|
|
2519
|
-
}
|
|
2520
|
-
if (typeof predicate !== "function") {
|
|
2521
|
-
throw new TypeError(`predicate must be a function, got ${predicate}`);
|
|
2522
|
-
}
|
|
2523
|
-
const flatAssociations = asFlatAssociations(associations);
|
|
2524
|
-
// for full match we must create an object to allow pattern to override previous ones
|
|
2525
|
-
let fullMatchMeta = {};
|
|
2526
|
-
let someFullMatch = false;
|
|
2527
|
-
// for partial match, any meta satisfying predicate will be valid because
|
|
2528
|
-
// we don't know for sure if pattern will still match for a file inside pathname
|
|
2529
|
-
const partialMatchMetaArray = [];
|
|
2530
|
-
for (const pattern of Object.keys(flatAssociations)) {
|
|
2531
|
-
const value = flatAssociations[pattern];
|
|
2532
|
-
const matchResult = applyPatternMatching({
|
|
2533
|
-
pattern,
|
|
2534
|
-
url,
|
|
2535
|
-
});
|
|
2536
|
-
if (matchResult.matched) {
|
|
2537
|
-
someFullMatch = true;
|
|
2538
|
-
if (isPlainObject(fullMatchMeta) && isPlainObject(value)) {
|
|
2539
|
-
fullMatchMeta = {
|
|
2540
|
-
...fullMatchMeta,
|
|
2541
|
-
...value,
|
|
2542
|
-
};
|
|
2543
|
-
} else {
|
|
2544
|
-
fullMatchMeta = value;
|
|
2545
|
-
}
|
|
2546
|
-
} else if (someFullMatch === false && matchResult.urlIndex >= url.length) {
|
|
2547
|
-
partialMatchMetaArray.push(value);
|
|
2548
|
-
}
|
|
2549
|
-
}
|
|
2550
|
-
if (someFullMatch) {
|
|
2551
|
-
return Boolean(predicate(fullMatchMeta));
|
|
2552
|
-
}
|
|
2553
|
-
return partialMatchMetaArray.some((partialMatchMeta) =>
|
|
2554
|
-
predicate(partialMatchMeta),
|
|
2555
|
-
);
|
|
2556
|
-
};
|
|
2557
|
-
|
|
2558
|
-
const applyAliases = ({ url, aliases }) => {
|
|
2559
|
-
for (const key of Object.keys(aliases)) {
|
|
2560
|
-
const matchResult = applyPatternMatching({
|
|
2561
|
-
pattern: key,
|
|
2562
|
-
url,
|
|
2563
|
-
});
|
|
2564
|
-
if (!matchResult.matched) {
|
|
2565
|
-
continue;
|
|
2566
|
-
}
|
|
2567
|
-
const { matchGroups } = matchResult;
|
|
2568
|
-
const alias = aliases[key];
|
|
2569
|
-
const parts = alias.split("*");
|
|
2570
|
-
let newUrl = "";
|
|
2571
|
-
let index = 0;
|
|
2572
|
-
for (const part of parts) {
|
|
2573
|
-
newUrl += `${part}`;
|
|
2574
|
-
if (index < parts.length - 1) {
|
|
2575
|
-
newUrl += matchGroups[index];
|
|
2576
|
-
}
|
|
2577
|
-
index++;
|
|
2578
|
-
}
|
|
2579
|
-
return newUrl;
|
|
2580
|
-
}
|
|
2581
|
-
return url;
|
|
2582
|
-
};
|
|
2583
|
-
|
|
2584
|
-
const matches = (url, patterns) => {
|
|
2585
|
-
return Boolean(
|
|
2586
|
-
applyAssociations({
|
|
2587
|
-
url,
|
|
2588
|
-
associations: {
|
|
2589
|
-
yes: patterns,
|
|
2590
|
-
},
|
|
2591
|
-
}).yes,
|
|
2592
|
-
);
|
|
2593
|
-
};
|
|
2594
|
-
|
|
2595
|
-
// const assertSpecifierMetaMap = (value, checkComposition = true) => {
|
|
2596
|
-
// if (!isPlainObject(value)) {
|
|
2597
|
-
// throw new TypeError(
|
|
2598
|
-
// `specifierMetaMap must be a plain object, got ${value}`,
|
|
2599
|
-
// );
|
|
2600
|
-
// }
|
|
2601
|
-
// if (checkComposition) {
|
|
2602
|
-
// const plainObject = value;
|
|
2603
|
-
// Object.keys(plainObject).forEach((key) => {
|
|
2604
|
-
// assertUrlLike(key, "specifierMetaMap key");
|
|
2605
|
-
// const value = plainObject[key];
|
|
2606
|
-
// if (value !== null && !isPlainObject(value)) {
|
|
2607
|
-
// throw new TypeError(
|
|
2608
|
-
// `specifierMetaMap value must be a plain object or null, got ${value} under key ${key}`,
|
|
2609
|
-
// );
|
|
2610
|
-
// }
|
|
2611
|
-
// });
|
|
2612
|
-
// }
|
|
2613
|
-
// };
|
|
2614
|
-
const assertUrlLike = (value, name = "url") => {
|
|
2615
|
-
if (typeof value !== "string") {
|
|
2616
|
-
throw new TypeError(`${name} must be a url string, got ${value}`);
|
|
2617
|
-
}
|
|
2618
|
-
if (isWindowsPathnameSpecifier(value)) {
|
|
2619
|
-
throw new TypeError(
|
|
2620
|
-
`${name} must be a url but looks like a windows pathname, got ${value}`,
|
|
2621
|
-
);
|
|
2622
|
-
}
|
|
2623
|
-
if (!hasScheme(value)) {
|
|
2624
|
-
throw new TypeError(
|
|
2625
|
-
`${name} must be a url and no scheme found, got ${value}`,
|
|
2626
|
-
);
|
|
2627
|
-
}
|
|
2628
|
-
};
|
|
2629
|
-
const isPlainObject = (value) => {
|
|
2630
|
-
if (value === null) {
|
|
2631
|
-
return false;
|
|
2632
|
-
}
|
|
2633
|
-
if (typeof value === "object") {
|
|
2634
|
-
if (Array.isArray(value)) {
|
|
2635
|
-
return false;
|
|
2636
|
-
}
|
|
2637
|
-
return true;
|
|
2638
|
-
}
|
|
2639
|
-
return false;
|
|
2640
|
-
};
|
|
2641
|
-
const isWindowsPathnameSpecifier = (specifier) => {
|
|
2642
|
-
const firstChar = specifier[0];
|
|
2643
|
-
if (!/[a-zA-Z]/.test(firstChar)) return false;
|
|
2644
|
-
const secondChar = specifier[1];
|
|
2645
|
-
if (secondChar !== ":") return false;
|
|
2646
|
-
const thirdChar = specifier[2];
|
|
2647
|
-
return thirdChar === "/" || thirdChar === "\\";
|
|
2648
|
-
};
|
|
2649
|
-
const hasScheme = (specifier) => /^[a-zA-Z]+:/.test(specifier);
|
|
2650
|
-
|
|
2651
|
-
const createFilter = (patterns, url, map = (v) => v) => {
|
|
2652
|
-
const associations = resolveAssociations(
|
|
2653
|
-
{
|
|
2654
|
-
yes: patterns,
|
|
2655
|
-
},
|
|
2656
|
-
url,
|
|
2657
|
-
);
|
|
2658
|
-
return (url) => {
|
|
2659
|
-
const meta = applyAssociations({ url, associations });
|
|
2660
|
-
return Boolean(map(meta.yes));
|
|
2661
|
-
};
|
|
2662
|
-
};
|
|
2663
|
-
|
|
2664
|
-
const URL_META = {
|
|
2665
|
-
resolveAssociations,
|
|
2666
|
-
applyAssociations,
|
|
2667
|
-
applyAliases,
|
|
2668
|
-
applyPatternMatching,
|
|
2669
|
-
urlChildMayMatch,
|
|
2670
|
-
matches,
|
|
2671
|
-
createFilter,
|
|
2672
|
-
};
|
|
2673
|
-
|
|
2674
2162
|
const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
2675
2163
|
const directoryUrl = assertAndNormalizeDirectoryUrl(url);
|
|
2676
2164
|
const directoryUrlObject = new URL(directoryUrl);
|
|
@@ -3648,92 +3136,1459 @@ const removeDirectory = async (
|
|
|
3648
3136
|
} else {
|
|
3649
3137
|
await visitDirectory(rootDirectoryUrl);
|
|
3650
3138
|
}
|
|
3651
|
-
} finally {
|
|
3652
|
-
await removeDirectoryOperation.end();
|
|
3139
|
+
} finally {
|
|
3140
|
+
await removeDirectoryOperation.end();
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
|
|
3144
|
+
const removeDirectoryNaive = (
|
|
3145
|
+
directoryPath,
|
|
3146
|
+
{ handleNotEmptyError = null, handlePermissionError = null } = {},
|
|
3147
|
+
) => {
|
|
3148
|
+
return new Promise((resolve, reject) => {
|
|
3149
|
+
rmdir(directoryPath, (error, lstatObject) => {
|
|
3150
|
+
if (error) {
|
|
3151
|
+
if (handlePermissionError && error.code === "EPERM") {
|
|
3152
|
+
resolve(handlePermissionError(error));
|
|
3153
|
+
} else if (error.code === "ENOENT") {
|
|
3154
|
+
resolve();
|
|
3155
|
+
} else if (
|
|
3156
|
+
handleNotEmptyError &&
|
|
3157
|
+
// linux os
|
|
3158
|
+
(error.code === "ENOTEMPTY" ||
|
|
3159
|
+
// SunOS
|
|
3160
|
+
error.code === "EEXIST")
|
|
3161
|
+
) {
|
|
3162
|
+
resolve(handleNotEmptyError(error));
|
|
3163
|
+
} else {
|
|
3164
|
+
reject(error);
|
|
3165
|
+
}
|
|
3166
|
+
} else {
|
|
3167
|
+
resolve(lstatObject);
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
});
|
|
3171
|
+
};
|
|
3172
|
+
|
|
3173
|
+
const ensureEmptyDirectorySync = (source) => {
|
|
3174
|
+
const stat = readEntryStatSync(source, {
|
|
3175
|
+
nullIfNotFound: true,
|
|
3176
|
+
followLink: false,
|
|
3177
|
+
});
|
|
3178
|
+
|
|
3179
|
+
if (stat === null) {
|
|
3180
|
+
// if there is nothing, create a directory
|
|
3181
|
+
writeDirectorySync(source, { allowUseless: true });
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
if (stat.isDirectory()) {
|
|
3185
|
+
removeEntrySync(source, {
|
|
3186
|
+
recursive: true,
|
|
3187
|
+
onlyContent: true,
|
|
3188
|
+
allowUseless: true,
|
|
3189
|
+
});
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
const sourceType = statsToType(stat);
|
|
3194
|
+
const sourcePath = urlToFileSystemPath(assertAndNormalizeFileUrl(source));
|
|
3195
|
+
throw new Error(
|
|
3196
|
+
`ensureEmptyDirectorySync expect directory at ${sourcePath}, found ${sourceType} instead`,
|
|
3197
|
+
);
|
|
3198
|
+
};
|
|
3199
|
+
|
|
3200
|
+
const ensureEmptyDirectory = async (source) => {
|
|
3201
|
+
const stats = await readEntryStat(source, {
|
|
3202
|
+
nullIfNotFound: true,
|
|
3203
|
+
followLink: false,
|
|
3204
|
+
});
|
|
3205
|
+
if (stats === null) {
|
|
3206
|
+
// if there is nothing, create a directory
|
|
3207
|
+
await writeDirectory(source, { allowUseless: true });
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
if (stats.isDirectory()) {
|
|
3211
|
+
// if there is a directory remove its content and done
|
|
3212
|
+
await removeEntry(source, {
|
|
3213
|
+
allowUseless: true,
|
|
3214
|
+
recursive: true,
|
|
3215
|
+
onlyContent: true,
|
|
3216
|
+
});
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3220
|
+
const sourceType = statsToType(stats);
|
|
3221
|
+
const sourcePath = urlToFileSystemPath(assertAndNormalizeFileUrl(source));
|
|
3222
|
+
throw new Error(
|
|
3223
|
+
`ensureEmptyDirectory expect directory at ${sourcePath}, found ${sourceType} instead`,
|
|
3224
|
+
);
|
|
3225
|
+
};
|
|
3226
|
+
|
|
3227
|
+
const POST = ({ url, body, githubToken, headers }) => {
|
|
3228
|
+
return sendHttpRequest({
|
|
3229
|
+
url,
|
|
3230
|
+
method: "POST",
|
|
3231
|
+
headers: {
|
|
3232
|
+
...tokenToHeaders(githubToken),
|
|
3233
|
+
"accept": "application/vnd.github+json",
|
|
3234
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
3235
|
+
...headers,
|
|
3236
|
+
},
|
|
3237
|
+
body: JSON.stringify(body),
|
|
3238
|
+
responseStatusHandlers: {
|
|
3239
|
+
201: async (response) => {
|
|
3240
|
+
const json = await response.json();
|
|
3241
|
+
return json;
|
|
3242
|
+
},
|
|
3243
|
+
},
|
|
3244
|
+
});
|
|
3245
|
+
};
|
|
3246
|
+
|
|
3247
|
+
const PATCH = ({ signal, url, body, githubToken, headers }) => {
|
|
3248
|
+
return sendHttpRequest({
|
|
3249
|
+
signal,
|
|
3250
|
+
url,
|
|
3251
|
+
method: "PATCH",
|
|
3252
|
+
headers: {
|
|
3253
|
+
...tokenToHeaders(githubToken),
|
|
3254
|
+
"accept": "application/vnd.github+json",
|
|
3255
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
3256
|
+
...headers,
|
|
3257
|
+
},
|
|
3258
|
+
body: JSON.stringify(body),
|
|
3259
|
+
responseStatusHandlers: {
|
|
3260
|
+
200: async (response) => {
|
|
3261
|
+
const json = await response.json();
|
|
3262
|
+
return json;
|
|
3263
|
+
},
|
|
3264
|
+
},
|
|
3265
|
+
});
|
|
3266
|
+
};
|
|
3267
|
+
|
|
3268
|
+
const tokenToHeaders = (token) => {
|
|
3269
|
+
if (!token) {
|
|
3270
|
+
throw new Error(`missing token, request will not be authorized.`);
|
|
3271
|
+
}
|
|
3272
|
+
return {
|
|
3273
|
+
authorization: `token ${token}`,
|
|
3274
|
+
};
|
|
3275
|
+
};
|
|
3276
|
+
|
|
3277
|
+
const sendHttpRequest = async ({
|
|
3278
|
+
signal,
|
|
3279
|
+
url,
|
|
3280
|
+
method,
|
|
3281
|
+
headers,
|
|
3282
|
+
body,
|
|
3283
|
+
responseStatusHandlers = {},
|
|
3284
|
+
}) => {
|
|
3285
|
+
let response;
|
|
3286
|
+
try {
|
|
3287
|
+
response = await fetch(url, {
|
|
3288
|
+
signal,
|
|
3289
|
+
method,
|
|
3290
|
+
headers: {
|
|
3291
|
+
...(typeof body === "undefined"
|
|
3292
|
+
? {}
|
|
3293
|
+
: { "content-length": Buffer.byteLength(body) }),
|
|
3294
|
+
...headers,
|
|
3295
|
+
},
|
|
3296
|
+
body,
|
|
3297
|
+
});
|
|
3298
|
+
} catch (error) {
|
|
3299
|
+
throw new Error(`network error during request.
|
|
3300
|
+
--- request method ---
|
|
3301
|
+
${method}
|
|
3302
|
+
--- request url ---
|
|
3303
|
+
${url}
|
|
3304
|
+
--- error stack ---
|
|
3305
|
+
${error.stack}`);
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
const { status } = response;
|
|
3309
|
+
const responseStatusHandler = responseStatusHandlers[status];
|
|
3310
|
+
if (responseStatusHandler) {
|
|
3311
|
+
return responseStatusHandler(response);
|
|
3312
|
+
}
|
|
3313
|
+
const responseBodyAsJson = await response.json();
|
|
3314
|
+
const error = new Error(`unexpected response status.
|
|
3315
|
+
--- response status ---
|
|
3316
|
+
${response.status}
|
|
3317
|
+
--- response statusText ---
|
|
3318
|
+
${response.statusText}
|
|
3319
|
+
--- expect response status ---
|
|
3320
|
+
${Object.keys(responseStatusHandlers).join(", ")}
|
|
3321
|
+
--- request method ---
|
|
3322
|
+
${method}
|
|
3323
|
+
--- request url ---
|
|
3324
|
+
${url}
|
|
3325
|
+
--- response json ---
|
|
3326
|
+
${(JSON.stringify(responseBodyAsJson), " ")}`);
|
|
3327
|
+
error.responseStatus = status;
|
|
3328
|
+
throw error;
|
|
3329
|
+
};
|
|
3330
|
+
|
|
3331
|
+
/**
|
|
3332
|
+
*
|
|
3333
|
+
* https://github.com/IgnusG/jest-report-action/blob/c006b890ba3c3b650e6c55916a643ca82b64133b/tasks/github-api.js#L12
|
|
3334
|
+
* https://docs.github.com/fr/rest/checks/runs?apiVersion=2022-11-28#create-a-check-run
|
|
3335
|
+
*/
|
|
3336
|
+
|
|
3337
|
+
|
|
3338
|
+
const startGithubCheckRun = async ({
|
|
3339
|
+
logLevel,
|
|
3340
|
+
githubToken,
|
|
3341
|
+
repositoryOwner,
|
|
3342
|
+
repositoryName,
|
|
3343
|
+
commitSha,
|
|
3344
|
+
checkName,
|
|
3345
|
+
checkTitle,
|
|
3346
|
+
checkSummary,
|
|
3347
|
+
checkStatus = "in_progress",
|
|
3348
|
+
}) => {
|
|
3349
|
+
if (typeof githubToken !== "string") {
|
|
3350
|
+
throw new TypeError(
|
|
3351
|
+
`githubToken must be a string but received ${githubToken}`,
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
if (typeof repositoryOwner !== "string") {
|
|
3355
|
+
throw new TypeError(
|
|
3356
|
+
`repositoryOwner must be a string but received ${repositoryOwner}`,
|
|
3357
|
+
);
|
|
3358
|
+
}
|
|
3359
|
+
if (typeof repositoryName !== "string") {
|
|
3360
|
+
throw new TypeError(
|
|
3361
|
+
`repositoryName must be a string but received ${repositoryName}`,
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
if (typeof commitSha !== "string") {
|
|
3365
|
+
throw new TypeError(`commitSha must be a string but received ${commitSha}`);
|
|
3366
|
+
}
|
|
3367
|
+
if (typeof checkName !== "string") {
|
|
3368
|
+
throw new TypeError(`checkName must be a string but received ${checkName}`);
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
const logger = createLogger({ logLevel });
|
|
3372
|
+
|
|
3373
|
+
const checkApiURL = `https://api.github.com/repos/${repositoryOwner}/${repositoryName}/check-runs`;
|
|
3374
|
+
logger.debug(`POST ${checkApiURL} (for commit ${commitSha})`);
|
|
3375
|
+
let check;
|
|
3376
|
+
try {
|
|
3377
|
+
check = await POST({
|
|
3378
|
+
url: checkApiURL,
|
|
3379
|
+
githubToken,
|
|
3380
|
+
headers: {
|
|
3381
|
+
"accept": "application/vnd.github+json",
|
|
3382
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
3383
|
+
},
|
|
3384
|
+
body: {
|
|
3385
|
+
head_sha: commitSha,
|
|
3386
|
+
status: checkStatus,
|
|
3387
|
+
name: checkName,
|
|
3388
|
+
output: {
|
|
3389
|
+
title: checkTitle,
|
|
3390
|
+
summary: checkSummary,
|
|
3391
|
+
},
|
|
3392
|
+
},
|
|
3393
|
+
});
|
|
3394
|
+
logger.debug(`${UNICODE.OK} check created ${check.html_url}`);
|
|
3395
|
+
} catch (e) {
|
|
3396
|
+
logger.error(
|
|
3397
|
+
createDetailedMessage(`${UNICODE.FAILURE} failed to create check`, {
|
|
3398
|
+
"error stack": e.stack,
|
|
3399
|
+
}),
|
|
3400
|
+
);
|
|
3401
|
+
return { progress: () => {}, fail: () => {}, pass: () => {} };
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
const updateState = async ({
|
|
3405
|
+
status,
|
|
3406
|
+
conclusion,
|
|
3407
|
+
title = check.title,
|
|
3408
|
+
summary = check.summary,
|
|
3409
|
+
annotations = [],
|
|
3410
|
+
}) => {
|
|
3411
|
+
let annotationsSent = 0;
|
|
3412
|
+
const annotationsBatch = annotations.slice(annotationsSent, 50);
|
|
3413
|
+
const body = {
|
|
3414
|
+
...(status ? { status } : {}),
|
|
3415
|
+
...(conclusion ? { conclusion } : {}),
|
|
3416
|
+
output: {
|
|
3417
|
+
title: title === undefined ? check.title : title,
|
|
3418
|
+
summary: summary === undefined ? check.summary : summary,
|
|
3419
|
+
...(annotationsBatch.length ? { annotations: annotationsBatch } : {}),
|
|
3420
|
+
},
|
|
3421
|
+
};
|
|
3422
|
+
|
|
3423
|
+
try {
|
|
3424
|
+
logger.debug(`PATCH check ${check.html_url}
|
|
3425
|
+
--- body ---
|
|
3426
|
+
${JSON.stringify(body, null, " ")}`);
|
|
3427
|
+
check = await PATCH({
|
|
3428
|
+
url: check.url,
|
|
3429
|
+
githubToken,
|
|
3430
|
+
body,
|
|
3431
|
+
});
|
|
3432
|
+
logger.debug(`${UNICODE.OK} check updated`);
|
|
3433
|
+
} catch (e) {
|
|
3434
|
+
logger.error(
|
|
3435
|
+
createDetailedMessage(`${UNICODE.FAILURE} failed to update check`, {
|
|
3436
|
+
"error stack": e.stack,
|
|
3437
|
+
}),
|
|
3438
|
+
);
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
annotationsSent += annotationsBatch.length;
|
|
3443
|
+
while (annotationsSent < annotations.length) {
|
|
3444
|
+
const annotationsBatch = annotations.slice(annotationsSent, 50);
|
|
3445
|
+
check = await PATCH({
|
|
3446
|
+
url: check.url,
|
|
3447
|
+
githubToken,
|
|
3448
|
+
body: {
|
|
3449
|
+
head_sha: commitSha,
|
|
3450
|
+
output: {
|
|
3451
|
+
annotations: annotationsBatch,
|
|
3452
|
+
},
|
|
3453
|
+
},
|
|
3454
|
+
});
|
|
3455
|
+
annotationsSent += annotationsBatch.length;
|
|
3456
|
+
}
|
|
3457
|
+
};
|
|
3458
|
+
|
|
3459
|
+
let lastProgressCall;
|
|
3460
|
+
let pendingAnnotations = [];
|
|
3461
|
+
let pendingAbortController;
|
|
3462
|
+
let pendingProgressPromise = Promise.resolve();
|
|
3463
|
+
let msBetweenProgressCalls = 500;
|
|
3464
|
+
|
|
3465
|
+
return {
|
|
3466
|
+
progress: async ({ title, summary, annotations = [] }) => {
|
|
3467
|
+
if (check.conclusion === "failure") {
|
|
3468
|
+
throw new Error(`cannot progress() after fail()`);
|
|
3469
|
+
}
|
|
3470
|
+
if (check.conclusion === "success") {
|
|
3471
|
+
throw new Error(`cannot progress() after pass()`);
|
|
3472
|
+
}
|
|
3473
|
+
pendingProgressPromise = (async () => {
|
|
3474
|
+
const nowMs = Date.now();
|
|
3475
|
+
const isFirstCall = !lastProgressCall;
|
|
3476
|
+
lastProgressCall = nowMs;
|
|
3477
|
+
if (isFirstCall) {
|
|
3478
|
+
await updateState({
|
|
3479
|
+
title,
|
|
3480
|
+
summary,
|
|
3481
|
+
annotations,
|
|
3482
|
+
});
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
if (pendingAbortController) {
|
|
3486
|
+
pendingAbortController.abort();
|
|
3487
|
+
}
|
|
3488
|
+
const msEllapsedSinceLastProgressCall = nowMs - lastProgressCall;
|
|
3489
|
+
const msEllapsedIsBigEnough =
|
|
3490
|
+
msEllapsedSinceLastProgressCall > msBetweenProgressCalls;
|
|
3491
|
+
if (msEllapsedIsBigEnough) {
|
|
3492
|
+
annotations = [...pendingAnnotations, ...annotations];
|
|
3493
|
+
pendingAnnotations.length = 0;
|
|
3494
|
+
await updateState({
|
|
3495
|
+
title,
|
|
3496
|
+
summary,
|
|
3497
|
+
annotations,
|
|
3498
|
+
});
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3501
|
+
pendingAnnotations.push(...annotations);
|
|
3502
|
+
pendingAbortController = new AbortController();
|
|
3503
|
+
await new Promise((resolve) => {
|
|
3504
|
+
pendingAbortController.signal.onabort = resolve;
|
|
3505
|
+
setTimeout(resolve, msBetweenProgressCalls);
|
|
3506
|
+
});
|
|
3507
|
+
if (pendingAbortController && pendingAbortController.signal.aborted) {
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
pendingAbortController = null;
|
|
3511
|
+
await updateState({
|
|
3512
|
+
title,
|
|
3513
|
+
summary,
|
|
3514
|
+
annotations,
|
|
3515
|
+
});
|
|
3516
|
+
})();
|
|
3517
|
+
await pendingProgressPromise;
|
|
3518
|
+
pendingProgressPromise = Promise.resolve();
|
|
3519
|
+
},
|
|
3520
|
+
fail: async ({ title, summary, annotations } = {}) => {
|
|
3521
|
+
await pendingProgressPromise;
|
|
3522
|
+
if (pendingAbortController) {
|
|
3523
|
+
pendingAbortController.abort();
|
|
3524
|
+
}
|
|
3525
|
+
if (check.conclusion === "failure") {
|
|
3526
|
+
throw new Error(`already failed`);
|
|
3527
|
+
}
|
|
3528
|
+
if (check.conclusion === "success") {
|
|
3529
|
+
throw new Error(`cannot fail() after pass()`);
|
|
3530
|
+
}
|
|
3531
|
+
return updateState({
|
|
3532
|
+
status: "completed",
|
|
3533
|
+
conclusion: "failure",
|
|
3534
|
+
title,
|
|
3535
|
+
summary,
|
|
3536
|
+
annotations,
|
|
3537
|
+
});
|
|
3538
|
+
},
|
|
3539
|
+
pass: async ({ title, summary, annotations } = {}) => {
|
|
3540
|
+
await pendingProgressPromise;
|
|
3541
|
+
if (pendingAbortController) {
|
|
3542
|
+
pendingAbortController.abort();
|
|
3543
|
+
}
|
|
3544
|
+
if (check.conclusion === "failure") {
|
|
3545
|
+
throw new Error(`cannot pass() after fail()`);
|
|
3546
|
+
}
|
|
3547
|
+
if (check.conclusion === "success") {
|
|
3548
|
+
throw new Error(`already passed`);
|
|
3549
|
+
}
|
|
3550
|
+
return updateState({
|
|
3551
|
+
status: "completed",
|
|
3552
|
+
conclusion: "success",
|
|
3553
|
+
title,
|
|
3554
|
+
summary,
|
|
3555
|
+
annotations,
|
|
3556
|
+
});
|
|
3557
|
+
},
|
|
3558
|
+
};
|
|
3559
|
+
};
|
|
3560
|
+
|
|
3561
|
+
// https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
|
|
3562
|
+
const readGitHubWorkflowEnv = () => {
|
|
3563
|
+
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
3564
|
+
if (!eventName) {
|
|
3565
|
+
throw new Error(
|
|
3566
|
+
`missing process.env.GITHUB_EVENT_NAME, we are not in a github workflow`,
|
|
3567
|
+
);
|
|
3568
|
+
}
|
|
3569
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
3570
|
+
if (!githubToken) {
|
|
3571
|
+
throw new Error(`missing githubToken`);
|
|
3572
|
+
}
|
|
3573
|
+
const githubRepository = process.env.GITHUB_REPOSITORY;
|
|
3574
|
+
if (!githubRepository) {
|
|
3575
|
+
throw new Error(`missing process.env.GITHUB_REPOSITORY`);
|
|
3576
|
+
}
|
|
3577
|
+
const [repositoryOwner, repositoryName] = githubRepository.split("/");
|
|
3578
|
+
return {
|
|
3579
|
+
rootDirectoryUrl: process.env.GITHUB_WORKSPACE,
|
|
3580
|
+
githubToken,
|
|
3581
|
+
repositoryOwner,
|
|
3582
|
+
repositoryName,
|
|
3583
|
+
commitSha: process.env.GITHUB_SHA,
|
|
3584
|
+
};
|
|
3585
|
+
};
|
|
3586
|
+
|
|
3587
|
+
// https://nodejs.org/api/packages.html#resolving-user-conditions
|
|
3588
|
+
const readCustomConditionsFromProcessArgs = () => {
|
|
3589
|
+
const packageConditions = [];
|
|
3590
|
+
for (const arg of process.execArgv) {
|
|
3591
|
+
if (arg.includes("-C=")) {
|
|
3592
|
+
const packageCondition = arg.slice(0, "-C=".length);
|
|
3593
|
+
packageConditions.push(packageCondition);
|
|
3594
|
+
}
|
|
3595
|
+
if (arg.includes("--conditions=")) {
|
|
3596
|
+
const packageCondition = arg.slice("--conditions=".length);
|
|
3597
|
+
packageConditions.push(packageCondition);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
return packageConditions;
|
|
3601
|
+
};
|
|
3602
|
+
|
|
3603
|
+
const asDirectoryUrl = (url) => {
|
|
3604
|
+
const { pathname } = new URL(url);
|
|
3605
|
+
if (pathname.endsWith("/")) {
|
|
3606
|
+
return url;
|
|
3607
|
+
}
|
|
3608
|
+
return new URL("./", url).href;
|
|
3609
|
+
};
|
|
3610
|
+
|
|
3611
|
+
const getParentUrl = (url) => {
|
|
3612
|
+
if (url.startsWith("file://")) {
|
|
3613
|
+
// With node.js new URL('../', 'file:///C:/').href
|
|
3614
|
+
// returns "file:///C:/" instead of "file:///"
|
|
3615
|
+
const resource = url.slice("file://".length);
|
|
3616
|
+
const slashLastIndex = resource.lastIndexOf("/");
|
|
3617
|
+
if (slashLastIndex === -1) {
|
|
3618
|
+
return url;
|
|
3619
|
+
}
|
|
3620
|
+
const lastCharIndex = resource.length - 1;
|
|
3621
|
+
if (slashLastIndex === lastCharIndex) {
|
|
3622
|
+
const slashBeforeLastIndex = resource.lastIndexOf(
|
|
3623
|
+
"/",
|
|
3624
|
+
slashLastIndex - 1,
|
|
3625
|
+
);
|
|
3626
|
+
if (slashBeforeLastIndex === -1) {
|
|
3627
|
+
return url;
|
|
3628
|
+
}
|
|
3629
|
+
return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
return `file://${resource.slice(0, slashLastIndex + 1)}`;
|
|
3633
|
+
}
|
|
3634
|
+
return new URL(url.endsWith("/") ? "../" : "./", url).href;
|
|
3635
|
+
};
|
|
3636
|
+
|
|
3637
|
+
const isValidUrl = (url) => {
|
|
3638
|
+
try {
|
|
3639
|
+
// eslint-disable-next-line no-new
|
|
3640
|
+
new URL(url);
|
|
3641
|
+
return true;
|
|
3642
|
+
} catch {
|
|
3643
|
+
return false;
|
|
3644
|
+
}
|
|
3645
|
+
};
|
|
3646
|
+
|
|
3647
|
+
const defaultLookupPackageScope = (url) => {
|
|
3648
|
+
let scopeUrl = asDirectoryUrl(url);
|
|
3649
|
+
while (scopeUrl !== "file:///") {
|
|
3650
|
+
if (scopeUrl.endsWith("node_modules/")) {
|
|
3651
|
+
return null;
|
|
3652
|
+
}
|
|
3653
|
+
const packageJsonUrlObject = new URL("package.json", scopeUrl);
|
|
3654
|
+
if (existsSync(packageJsonUrlObject)) {
|
|
3655
|
+
return scopeUrl;
|
|
3656
|
+
}
|
|
3657
|
+
scopeUrl = getParentUrl(scopeUrl);
|
|
3658
|
+
}
|
|
3659
|
+
return null;
|
|
3660
|
+
};
|
|
3661
|
+
|
|
3662
|
+
const defaultReadPackageJson = (packageUrl) => {
|
|
3663
|
+
const packageJsonUrl = new URL("package.json", packageUrl);
|
|
3664
|
+
const buffer = readFileSync(packageJsonUrl);
|
|
3665
|
+
const string = String(buffer);
|
|
3666
|
+
try {
|
|
3667
|
+
return JSON.parse(string);
|
|
3668
|
+
} catch {
|
|
3669
|
+
throw new Error(`Invalid package configuration`);
|
|
3670
|
+
}
|
|
3671
|
+
};
|
|
3672
|
+
|
|
3673
|
+
// https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/tools/node_modules/eslint/node_modules/%40babel/core/lib/vendor/import-meta-resolve.js#L2473
|
|
3674
|
+
|
|
3675
|
+
const createInvalidModuleSpecifierError = (
|
|
3676
|
+
reason,
|
|
3677
|
+
specifier,
|
|
3678
|
+
{ parentUrl },
|
|
3679
|
+
) => {
|
|
3680
|
+
const error = new Error(
|
|
3681
|
+
`Invalid module "${specifier}" ${reason} imported from ${fileURLToPath(
|
|
3682
|
+
parentUrl,
|
|
3683
|
+
)}`,
|
|
3684
|
+
);
|
|
3685
|
+
error.code = "INVALID_MODULE_SPECIFIER";
|
|
3686
|
+
return error;
|
|
3687
|
+
};
|
|
3688
|
+
|
|
3689
|
+
const createInvalidPackageTargetError = (
|
|
3690
|
+
reason,
|
|
3691
|
+
target,
|
|
3692
|
+
{ parentUrl, packageDirectoryUrl, key, isImport },
|
|
3693
|
+
) => {
|
|
3694
|
+
let message;
|
|
3695
|
+
if (key === ".") {
|
|
3696
|
+
message = `Invalid "exports" main target defined in ${fileURLToPath(
|
|
3697
|
+
packageDirectoryUrl,
|
|
3698
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
|
|
3699
|
+
} else {
|
|
3700
|
+
message = `Invalid "${
|
|
3701
|
+
isImport ? "imports" : "exports"
|
|
3702
|
+
}" target ${JSON.stringify(target)} defined for "${key}" in ${fileURLToPath(
|
|
3703
|
+
packageDirectoryUrl,
|
|
3704
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
|
|
3705
|
+
}
|
|
3706
|
+
const error = new Error(message);
|
|
3707
|
+
error.code = "INVALID_PACKAGE_TARGET";
|
|
3708
|
+
return error;
|
|
3709
|
+
};
|
|
3710
|
+
|
|
3711
|
+
const createPackagePathNotExportedError = (
|
|
3712
|
+
subpath,
|
|
3713
|
+
{ parentUrl, packageDirectoryUrl },
|
|
3714
|
+
) => {
|
|
3715
|
+
let message;
|
|
3716
|
+
if (subpath === ".") {
|
|
3717
|
+
message = `No "exports" main defined in ${fileURLToPath(
|
|
3718
|
+
packageDirectoryUrl,
|
|
3719
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`;
|
|
3720
|
+
} else {
|
|
3721
|
+
message = `Package subpath "${subpath}" is not defined by "exports" in ${fileURLToPath(
|
|
3722
|
+
packageDirectoryUrl,
|
|
3723
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`;
|
|
3724
|
+
}
|
|
3725
|
+
const error = new Error(message);
|
|
3726
|
+
error.code = "PACKAGE_PATH_NOT_EXPORTED";
|
|
3727
|
+
return error;
|
|
3728
|
+
};
|
|
3729
|
+
|
|
3730
|
+
const createModuleNotFoundError = (specifier, { parentUrl }) => {
|
|
3731
|
+
const error = new Error(
|
|
3732
|
+
`Cannot find "${specifier}" imported from ${fileURLToPath(parentUrl)}`,
|
|
3733
|
+
);
|
|
3734
|
+
error.code = "MODULE_NOT_FOUND";
|
|
3735
|
+
return error;
|
|
3736
|
+
};
|
|
3737
|
+
|
|
3738
|
+
const createPackageImportNotDefinedError = (
|
|
3739
|
+
specifier,
|
|
3740
|
+
{ parentUrl, packageDirectoryUrl },
|
|
3741
|
+
) => {
|
|
3742
|
+
const error = new Error(
|
|
3743
|
+
`Package import specifier "${specifier}" is not defined in ${fileURLToPath(
|
|
3744
|
+
packageDirectoryUrl,
|
|
3745
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`,
|
|
3746
|
+
);
|
|
3747
|
+
error.code = "PACKAGE_IMPORT_NOT_DEFINED";
|
|
3748
|
+
return error;
|
|
3749
|
+
};
|
|
3750
|
+
|
|
3751
|
+
const isSpecifierForNodeBuiltin = (specifier) => {
|
|
3752
|
+
return (
|
|
3753
|
+
specifier.startsWith("node:") ||
|
|
3754
|
+
NODE_BUILTIN_MODULE_SPECIFIERS.includes(specifier)
|
|
3755
|
+
);
|
|
3756
|
+
};
|
|
3757
|
+
|
|
3758
|
+
const NODE_BUILTIN_MODULE_SPECIFIERS = [
|
|
3759
|
+
"assert",
|
|
3760
|
+
"assert/strict",
|
|
3761
|
+
"async_hooks",
|
|
3762
|
+
"buffer_ieee754",
|
|
3763
|
+
"buffer",
|
|
3764
|
+
"child_process",
|
|
3765
|
+
"cluster",
|
|
3766
|
+
"console",
|
|
3767
|
+
"constants",
|
|
3768
|
+
"crypto",
|
|
3769
|
+
"_debugger",
|
|
3770
|
+
"diagnostics_channel",
|
|
3771
|
+
"dgram",
|
|
3772
|
+
"dns",
|
|
3773
|
+
"domain",
|
|
3774
|
+
"events",
|
|
3775
|
+
"freelist",
|
|
3776
|
+
"fs",
|
|
3777
|
+
"fsevents",
|
|
3778
|
+
"fs/promises",
|
|
3779
|
+
"_http_agent",
|
|
3780
|
+
"_http_client",
|
|
3781
|
+
"_http_common",
|
|
3782
|
+
"_http_incoming",
|
|
3783
|
+
"_http_outgoing",
|
|
3784
|
+
"_http_server",
|
|
3785
|
+
"http",
|
|
3786
|
+
"http2",
|
|
3787
|
+
"https",
|
|
3788
|
+
"inspector",
|
|
3789
|
+
"_linklist",
|
|
3790
|
+
"module",
|
|
3791
|
+
"net",
|
|
3792
|
+
"node-inspect/lib/_inspect",
|
|
3793
|
+
"node-inspect/lib/internal/inspect_client",
|
|
3794
|
+
"node-inspect/lib/internal/inspect_repl",
|
|
3795
|
+
"os",
|
|
3796
|
+
"path",
|
|
3797
|
+
"perf_hooks",
|
|
3798
|
+
"process",
|
|
3799
|
+
"punycode",
|
|
3800
|
+
"querystring",
|
|
3801
|
+
"readline",
|
|
3802
|
+
"repl",
|
|
3803
|
+
"smalloc",
|
|
3804
|
+
"sqlite",
|
|
3805
|
+
"_stream_duplex",
|
|
3806
|
+
"_stream_transform",
|
|
3807
|
+
"_stream_wrap",
|
|
3808
|
+
"_stream_passthrough",
|
|
3809
|
+
"_stream_readable",
|
|
3810
|
+
"_stream_writable",
|
|
3811
|
+
"stream",
|
|
3812
|
+
"stream/promises",
|
|
3813
|
+
"string_decoder",
|
|
3814
|
+
"sys",
|
|
3815
|
+
"timers",
|
|
3816
|
+
"_tls_common",
|
|
3817
|
+
"_tls_legacy",
|
|
3818
|
+
"_tls_wrap",
|
|
3819
|
+
"tls",
|
|
3820
|
+
"trace_events",
|
|
3821
|
+
"tty",
|
|
3822
|
+
"url",
|
|
3823
|
+
"util",
|
|
3824
|
+
"v8/tools/arguments",
|
|
3825
|
+
"v8/tools/codemap",
|
|
3826
|
+
"v8/tools/consarray",
|
|
3827
|
+
"v8/tools/csvparser",
|
|
3828
|
+
"v8/tools/logreader",
|
|
3829
|
+
"v8/tools/profile_view",
|
|
3830
|
+
"v8/tools/splaytree",
|
|
3831
|
+
"v8",
|
|
3832
|
+
"vm",
|
|
3833
|
+
"worker_threads",
|
|
3834
|
+
"zlib",
|
|
3835
|
+
// global is special
|
|
3836
|
+
"global",
|
|
3837
|
+
];
|
|
3838
|
+
|
|
3839
|
+
/*
|
|
3840
|
+
* https://nodejs.org/api/esm.html#resolver-algorithm-specification
|
|
3841
|
+
* https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L1
|
|
3842
|
+
* deviations from the spec:
|
|
3843
|
+
* - take into account "browser", "module" and "jsnext"
|
|
3844
|
+
* - the check for isDirectory -> throw is delayed is descoped to the caller
|
|
3845
|
+
* - the call to real path ->
|
|
3846
|
+
* delayed to the caller so that we can decide to
|
|
3847
|
+
* maintain symlink as facade url when it's outside project directory
|
|
3848
|
+
* or use the real path when inside
|
|
3849
|
+
*/
|
|
3850
|
+
|
|
3851
|
+
const applyNodeEsmResolution = ({
|
|
3852
|
+
specifier,
|
|
3853
|
+
parentUrl,
|
|
3854
|
+
conditions = [...readCustomConditionsFromProcessArgs(), "node", "import"],
|
|
3855
|
+
lookupPackageScope = defaultLookupPackageScope,
|
|
3856
|
+
readPackageJson = defaultReadPackageJson,
|
|
3857
|
+
preservesSymlink = false,
|
|
3858
|
+
}) => {
|
|
3859
|
+
const resolution = applyPackageSpecifierResolution(specifier, {
|
|
3860
|
+
parentUrl: String(parentUrl),
|
|
3861
|
+
conditions,
|
|
3862
|
+
lookupPackageScope,
|
|
3863
|
+
readPackageJson,
|
|
3864
|
+
preservesSymlink,
|
|
3865
|
+
});
|
|
3866
|
+
const { url } = resolution;
|
|
3867
|
+
if (url.startsWith("file:")) {
|
|
3868
|
+
if (url.includes("%2F") || url.includes("%5C")) {
|
|
3869
|
+
throw createInvalidModuleSpecifierError(
|
|
3870
|
+
`must not include encoded "/" or "\\" characters`,
|
|
3871
|
+
specifier,
|
|
3872
|
+
{
|
|
3873
|
+
parentUrl,
|
|
3874
|
+
},
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
return resolution;
|
|
3878
|
+
}
|
|
3879
|
+
return resolution;
|
|
3880
|
+
};
|
|
3881
|
+
|
|
3882
|
+
const applyPackageSpecifierResolution = (specifier, resolutionContext) => {
|
|
3883
|
+
const { parentUrl } = resolutionContext;
|
|
3884
|
+
// relative specifier
|
|
3885
|
+
if (
|
|
3886
|
+
specifier[0] === "/" ||
|
|
3887
|
+
specifier.startsWith("./") ||
|
|
3888
|
+
specifier.startsWith("../")
|
|
3889
|
+
) {
|
|
3890
|
+
if (specifier[0] !== "/") {
|
|
3891
|
+
const browserFieldResolution = applyBrowserFieldResolution(
|
|
3892
|
+
specifier,
|
|
3893
|
+
resolutionContext,
|
|
3894
|
+
);
|
|
3895
|
+
if (browserFieldResolution) {
|
|
3896
|
+
return browserFieldResolution;
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
return {
|
|
3900
|
+
type: "relative_specifier",
|
|
3901
|
+
url: new URL(specifier, parentUrl).href,
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
if (specifier[0] === "#") {
|
|
3905
|
+
return applyPackageImportsResolution(specifier, resolutionContext);
|
|
3906
|
+
}
|
|
3907
|
+
try {
|
|
3908
|
+
const urlObject = new URL(specifier);
|
|
3909
|
+
if (specifier.startsWith("node:")) {
|
|
3910
|
+
return {
|
|
3911
|
+
type: "node_builtin_specifier",
|
|
3912
|
+
url: specifier,
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
return {
|
|
3916
|
+
type: "absolute_specifier",
|
|
3917
|
+
url: urlObject.href,
|
|
3918
|
+
};
|
|
3919
|
+
} catch {
|
|
3920
|
+
// bare specifier
|
|
3921
|
+
const browserFieldResolution = applyBrowserFieldResolution(
|
|
3922
|
+
specifier,
|
|
3923
|
+
resolutionContext,
|
|
3924
|
+
);
|
|
3925
|
+
if (browserFieldResolution) {
|
|
3926
|
+
return browserFieldResolution;
|
|
3927
|
+
}
|
|
3928
|
+
const packageResolution = applyPackageResolve(specifier, resolutionContext);
|
|
3929
|
+
const search = new URL(specifier, "file:///").search;
|
|
3930
|
+
if (search && !new URL(packageResolution.url).search) {
|
|
3931
|
+
packageResolution.url = `${packageResolution.url}${search}`;
|
|
3932
|
+
}
|
|
3933
|
+
return packageResolution;
|
|
3934
|
+
}
|
|
3935
|
+
};
|
|
3936
|
+
|
|
3937
|
+
const applyBrowserFieldResolution = (specifier, resolutionContext) => {
|
|
3938
|
+
const { parentUrl, conditions, lookupPackageScope, readPackageJson } =
|
|
3939
|
+
resolutionContext;
|
|
3940
|
+
const browserCondition = conditions.includes("browser");
|
|
3941
|
+
if (!browserCondition) {
|
|
3942
|
+
return null;
|
|
3943
|
+
}
|
|
3944
|
+
const packageDirectoryUrl = lookupPackageScope(parentUrl);
|
|
3945
|
+
if (!packageDirectoryUrl) {
|
|
3946
|
+
return null;
|
|
3947
|
+
}
|
|
3948
|
+
const packageJson = readPackageJson(packageDirectoryUrl);
|
|
3949
|
+
if (!packageJson) {
|
|
3950
|
+
return null;
|
|
3951
|
+
}
|
|
3952
|
+
const { browser } = packageJson;
|
|
3953
|
+
if (!browser) {
|
|
3954
|
+
return null;
|
|
3955
|
+
}
|
|
3956
|
+
if (typeof browser !== "object") {
|
|
3957
|
+
return null;
|
|
3958
|
+
}
|
|
3959
|
+
let url;
|
|
3960
|
+
if (specifier.startsWith(".")) {
|
|
3961
|
+
const specifierUrl = new URL(specifier, parentUrl).href;
|
|
3962
|
+
const specifierRelativeUrl = specifierUrl.slice(packageDirectoryUrl.length);
|
|
3963
|
+
const secifierRelativeNotation = `./${specifierRelativeUrl}`;
|
|
3964
|
+
const browserMapping = browser[secifierRelativeNotation];
|
|
3965
|
+
if (typeof browserMapping === "string") {
|
|
3966
|
+
url = new URL(browserMapping, packageDirectoryUrl).href;
|
|
3967
|
+
} else if (browserMapping === false) {
|
|
3968
|
+
url = `file:///@ignore/${specifierUrl.slice("file:///")}`;
|
|
3969
|
+
}
|
|
3970
|
+
} else {
|
|
3971
|
+
const browserMapping = browser[specifier];
|
|
3972
|
+
if (typeof browserMapping === "string") {
|
|
3973
|
+
url = new URL(browserMapping, packageDirectoryUrl).href;
|
|
3974
|
+
} else if (browserMapping === false) {
|
|
3975
|
+
url = `file:///@ignore/${specifier}`;
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
if (url) {
|
|
3979
|
+
return {
|
|
3980
|
+
type: "field:browser",
|
|
3981
|
+
isMain: true,
|
|
3982
|
+
packageDirectoryUrl,
|
|
3983
|
+
packageJson,
|
|
3984
|
+
url,
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
return null;
|
|
3988
|
+
};
|
|
3989
|
+
|
|
3990
|
+
const applyPackageImportsResolution = (
|
|
3991
|
+
internalSpecifier,
|
|
3992
|
+
resolutionContext,
|
|
3993
|
+
) => {
|
|
3994
|
+
const { parentUrl, lookupPackageScope, readPackageJson } = resolutionContext;
|
|
3995
|
+
if (internalSpecifier === "#" || internalSpecifier.startsWith("#/")) {
|
|
3996
|
+
throw createInvalidModuleSpecifierError(
|
|
3997
|
+
"not a valid internal imports specifier name",
|
|
3998
|
+
internalSpecifier,
|
|
3999
|
+
resolutionContext,
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
const packageDirectoryUrl = lookupPackageScope(parentUrl);
|
|
4003
|
+
if (packageDirectoryUrl !== null) {
|
|
4004
|
+
const packageJson = readPackageJson(packageDirectoryUrl);
|
|
4005
|
+
const { imports } = packageJson;
|
|
4006
|
+
if (imports !== null && typeof imports === "object") {
|
|
4007
|
+
const resolved = applyPackageImportsExportsResolution(internalSpecifier, {
|
|
4008
|
+
...resolutionContext,
|
|
4009
|
+
packageDirectoryUrl,
|
|
4010
|
+
packageJson,
|
|
4011
|
+
isImport: true,
|
|
4012
|
+
});
|
|
4013
|
+
if (resolved) {
|
|
4014
|
+
return resolved;
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
throw createPackageImportNotDefinedError(internalSpecifier, {
|
|
4019
|
+
...resolutionContext,
|
|
4020
|
+
packageDirectoryUrl,
|
|
4021
|
+
});
|
|
4022
|
+
};
|
|
4023
|
+
|
|
4024
|
+
const applyPackageResolve = (packageSpecifier, resolutionContext) => {
|
|
4025
|
+
const { parentUrl, conditions, readPackageJson, preservesSymlink, isImport } =
|
|
4026
|
+
resolutionContext;
|
|
4027
|
+
if (packageSpecifier === "") {
|
|
4028
|
+
throw new Error("invalid module specifier");
|
|
4029
|
+
}
|
|
4030
|
+
if (
|
|
4031
|
+
conditions.includes("node") &&
|
|
4032
|
+
isSpecifierForNodeBuiltin(packageSpecifier)
|
|
4033
|
+
) {
|
|
4034
|
+
return {
|
|
4035
|
+
type: "node_builtin_specifier",
|
|
4036
|
+
url: `node:${packageSpecifier}`,
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
let { packageName, packageSubpath } = parsePackageSpecifier(packageSpecifier);
|
|
4040
|
+
if (
|
|
4041
|
+
packageName[0] === "." ||
|
|
4042
|
+
packageName.includes("\\") ||
|
|
4043
|
+
packageName.includes("%")
|
|
4044
|
+
) {
|
|
4045
|
+
throw createInvalidModuleSpecifierError(
|
|
4046
|
+
`is not a valid package name`,
|
|
4047
|
+
packageName,
|
|
4048
|
+
resolutionContext,
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
4051
|
+
if (isImport && packageSubpath.endsWith("/")) {
|
|
4052
|
+
throw new Error("invalid module specifier");
|
|
4053
|
+
}
|
|
4054
|
+
const questionCharIndex = packageName.indexOf("?");
|
|
4055
|
+
if (questionCharIndex > -1) {
|
|
4056
|
+
packageName = packageName.slice(0, questionCharIndex);
|
|
4057
|
+
}
|
|
4058
|
+
const selfResolution = applyPackageSelfResolution(packageSubpath, {
|
|
4059
|
+
...resolutionContext,
|
|
4060
|
+
packageName,
|
|
4061
|
+
});
|
|
4062
|
+
if (selfResolution) {
|
|
4063
|
+
return selfResolution;
|
|
4064
|
+
}
|
|
4065
|
+
let currentUrl = parentUrl;
|
|
4066
|
+
while (currentUrl !== "file:///") {
|
|
4067
|
+
const packageDirectoryFacadeUrl = new URL(
|
|
4068
|
+
`node_modules/${packageName}/`,
|
|
4069
|
+
currentUrl,
|
|
4070
|
+
).href;
|
|
4071
|
+
if (!existsSync(new URL(packageDirectoryFacadeUrl))) {
|
|
4072
|
+
currentUrl = getParentUrl(currentUrl);
|
|
4073
|
+
continue;
|
|
4074
|
+
}
|
|
4075
|
+
const packageDirectoryUrl = preservesSymlink
|
|
4076
|
+
? packageDirectoryFacadeUrl
|
|
4077
|
+
: resolvePackageSymlink(packageDirectoryFacadeUrl);
|
|
4078
|
+
const packageJson = readPackageJson(packageDirectoryUrl);
|
|
4079
|
+
if (packageJson !== null) {
|
|
4080
|
+
const { exports } = packageJson;
|
|
4081
|
+
if (exports !== null && exports !== undefined) {
|
|
4082
|
+
return applyPackageExportsResolution(packageSubpath, {
|
|
4083
|
+
...resolutionContext,
|
|
4084
|
+
packageDirectoryUrl,
|
|
4085
|
+
packageJson,
|
|
4086
|
+
exports,
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
return applyLegacySubpathResolution(packageSubpath, {
|
|
4091
|
+
...resolutionContext,
|
|
4092
|
+
packageDirectoryUrl,
|
|
4093
|
+
packageJson,
|
|
4094
|
+
});
|
|
4095
|
+
}
|
|
4096
|
+
throw createModuleNotFoundError(packageName, resolutionContext);
|
|
4097
|
+
};
|
|
4098
|
+
|
|
4099
|
+
const applyPackageSelfResolution = (packageSubpath, resolutionContext) => {
|
|
4100
|
+
const { parentUrl, packageName, lookupPackageScope, readPackageJson } =
|
|
4101
|
+
resolutionContext;
|
|
4102
|
+
const packageDirectoryUrl = lookupPackageScope(parentUrl);
|
|
4103
|
+
if (!packageDirectoryUrl) {
|
|
4104
|
+
return undefined;
|
|
4105
|
+
}
|
|
4106
|
+
const packageJson = readPackageJson(packageDirectoryUrl);
|
|
4107
|
+
if (!packageJson) {
|
|
4108
|
+
return undefined;
|
|
4109
|
+
}
|
|
4110
|
+
if (packageJson.name !== packageName) {
|
|
4111
|
+
return undefined;
|
|
4112
|
+
}
|
|
4113
|
+
const { exports } = packageJson;
|
|
4114
|
+
if (!exports) {
|
|
4115
|
+
const subpathResolution = applyLegacySubpathResolution(packageSubpath, {
|
|
4116
|
+
...resolutionContext,
|
|
4117
|
+
packageDirectoryUrl,
|
|
4118
|
+
packageJson,
|
|
4119
|
+
});
|
|
4120
|
+
if (subpathResolution && subpathResolution.type !== "subpath") {
|
|
4121
|
+
return subpathResolution;
|
|
4122
|
+
}
|
|
4123
|
+
return undefined;
|
|
4124
|
+
}
|
|
4125
|
+
return applyPackageExportsResolution(packageSubpath, {
|
|
4126
|
+
...resolutionContext,
|
|
4127
|
+
packageDirectoryUrl,
|
|
4128
|
+
packageJson,
|
|
4129
|
+
});
|
|
4130
|
+
};
|
|
4131
|
+
|
|
4132
|
+
// https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L642
|
|
4133
|
+
const applyPackageExportsResolution = (packageSubpath, resolutionContext) => {
|
|
4134
|
+
if (packageSubpath === ".") {
|
|
4135
|
+
const mainExport = applyMainExportResolution(resolutionContext);
|
|
4136
|
+
if (!mainExport) {
|
|
4137
|
+
throw createPackagePathNotExportedError(
|
|
4138
|
+
packageSubpath,
|
|
4139
|
+
resolutionContext,
|
|
4140
|
+
);
|
|
4141
|
+
}
|
|
4142
|
+
const resolved = applyPackageTargetResolution(mainExport, {
|
|
4143
|
+
...resolutionContext,
|
|
4144
|
+
key: ".",
|
|
4145
|
+
});
|
|
4146
|
+
if (resolved) {
|
|
4147
|
+
return resolved;
|
|
4148
|
+
}
|
|
4149
|
+
throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
|
|
4150
|
+
}
|
|
4151
|
+
const packageExportsInfo = readExports(resolutionContext);
|
|
4152
|
+
if (
|
|
4153
|
+
packageExportsInfo.type === "object" &&
|
|
4154
|
+
packageExportsInfo.allKeysAreRelative
|
|
4155
|
+
) {
|
|
4156
|
+
const resolved = applyPackageImportsExportsResolution(packageSubpath, {
|
|
4157
|
+
...resolutionContext,
|
|
4158
|
+
isImport: false,
|
|
4159
|
+
});
|
|
4160
|
+
if (resolved) {
|
|
4161
|
+
return resolved;
|
|
4162
|
+
}
|
|
3653
4163
|
}
|
|
4164
|
+
throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
|
|
3654
4165
|
};
|
|
3655
4166
|
|
|
3656
|
-
const
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
4167
|
+
const applyPackageImportsExportsResolution = (matchKey, resolutionContext) => {
|
|
4168
|
+
const { packageJson, isImport } = resolutionContext;
|
|
4169
|
+
const matchObject = isImport ? packageJson.imports : packageJson.exports;
|
|
4170
|
+
|
|
4171
|
+
if (!matchKey.includes("*") && matchObject.hasOwnProperty(matchKey)) {
|
|
4172
|
+
const target = matchObject[matchKey];
|
|
4173
|
+
return applyPackageTargetResolution(target, {
|
|
4174
|
+
...resolutionContext,
|
|
4175
|
+
key: matchKey,
|
|
4176
|
+
isImport,
|
|
4177
|
+
});
|
|
4178
|
+
}
|
|
4179
|
+
const expansionKeys = Object.keys(matchObject)
|
|
4180
|
+
.filter((key) => key.split("*").length === 2)
|
|
4181
|
+
.sort(comparePatternKeys);
|
|
4182
|
+
for (const expansionKey of expansionKeys) {
|
|
4183
|
+
const [patternBase, patternTrailer] = expansionKey.split("*");
|
|
4184
|
+
if (matchKey === patternBase) continue;
|
|
4185
|
+
if (!matchKey.startsWith(patternBase)) continue;
|
|
4186
|
+
if (patternTrailer.length > 0) {
|
|
4187
|
+
if (!matchKey.endsWith(patternTrailer)) continue;
|
|
4188
|
+
if (matchKey.length < expansionKey.length) continue;
|
|
4189
|
+
}
|
|
4190
|
+
const target = matchObject[expansionKey];
|
|
4191
|
+
const subpath = matchKey.slice(
|
|
4192
|
+
patternBase.length,
|
|
4193
|
+
matchKey.length - patternTrailer.length,
|
|
4194
|
+
);
|
|
4195
|
+
return applyPackageTargetResolution(target, {
|
|
4196
|
+
...resolutionContext,
|
|
4197
|
+
key: matchKey,
|
|
4198
|
+
subpath,
|
|
4199
|
+
pattern: true,
|
|
4200
|
+
isImport,
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
return null;
|
|
4204
|
+
};
|
|
4205
|
+
|
|
4206
|
+
const applyPackageTargetResolution = (target, resolutionContext) => {
|
|
4207
|
+
const {
|
|
4208
|
+
conditions,
|
|
4209
|
+
packageDirectoryUrl,
|
|
4210
|
+
packageJson,
|
|
4211
|
+
key,
|
|
4212
|
+
subpath = "",
|
|
4213
|
+
pattern = false,
|
|
4214
|
+
isImport = false,
|
|
4215
|
+
} = resolutionContext;
|
|
4216
|
+
|
|
4217
|
+
if (typeof target === "string") {
|
|
4218
|
+
if (pattern === false && subpath !== "" && !target.endsWith("/")) {
|
|
4219
|
+
throw new Error("invalid module specifier");
|
|
4220
|
+
}
|
|
4221
|
+
if (target.startsWith("./")) {
|
|
4222
|
+
const targetUrl = new URL(target, packageDirectoryUrl).href;
|
|
4223
|
+
if (!targetUrl.startsWith(packageDirectoryUrl)) {
|
|
4224
|
+
throw createInvalidPackageTargetError(
|
|
4225
|
+
`target must be inside package`,
|
|
4226
|
+
target,
|
|
4227
|
+
resolutionContext,
|
|
4228
|
+
);
|
|
4229
|
+
}
|
|
4230
|
+
return {
|
|
4231
|
+
type: isImport ? "field:imports" : "field:exports",
|
|
4232
|
+
isMain: subpath === "" || subpath === ".",
|
|
4233
|
+
packageDirectoryUrl,
|
|
4234
|
+
packageJson,
|
|
4235
|
+
url: pattern
|
|
4236
|
+
? targetUrl.replaceAll("*", subpath)
|
|
4237
|
+
: new URL(subpath, targetUrl).href,
|
|
4238
|
+
};
|
|
4239
|
+
}
|
|
4240
|
+
if (!isImport || target.startsWith("../") || isValidUrl(target)) {
|
|
4241
|
+
throw createInvalidPackageTargetError(
|
|
4242
|
+
`target must starst with "./"`,
|
|
4243
|
+
target,
|
|
4244
|
+
resolutionContext,
|
|
4245
|
+
);
|
|
4246
|
+
}
|
|
4247
|
+
return applyPackageResolve(
|
|
4248
|
+
pattern ? target.replaceAll("*", subpath) : `${target}${subpath}`,
|
|
4249
|
+
{
|
|
4250
|
+
...resolutionContext,
|
|
4251
|
+
parentUrl: packageDirectoryUrl,
|
|
4252
|
+
},
|
|
4253
|
+
);
|
|
4254
|
+
}
|
|
4255
|
+
if (Array.isArray(target)) {
|
|
4256
|
+
if (target.length === 0) {
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
let lastResult;
|
|
4260
|
+
let i = 0;
|
|
4261
|
+
while (i < target.length) {
|
|
4262
|
+
const targetValue = target[i];
|
|
4263
|
+
i++;
|
|
4264
|
+
try {
|
|
4265
|
+
const resolved = applyPackageTargetResolution(targetValue, {
|
|
4266
|
+
...resolutionContext,
|
|
4267
|
+
key: `${key}[${i}]`,
|
|
4268
|
+
subpath,
|
|
4269
|
+
pattern,
|
|
4270
|
+
isImport,
|
|
4271
|
+
});
|
|
4272
|
+
if (resolved) {
|
|
4273
|
+
return resolved;
|
|
4274
|
+
}
|
|
4275
|
+
lastResult = resolved;
|
|
4276
|
+
} catch (e) {
|
|
4277
|
+
if (e.code === "INVALID_PACKAGE_TARGET") {
|
|
4278
|
+
continue;
|
|
4279
|
+
}
|
|
4280
|
+
lastResult = e;
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
if (lastResult) {
|
|
4284
|
+
throw lastResult;
|
|
4285
|
+
}
|
|
4286
|
+
return null;
|
|
4287
|
+
}
|
|
4288
|
+
if (target === null) {
|
|
4289
|
+
return null;
|
|
4290
|
+
}
|
|
4291
|
+
if (typeof target === "object") {
|
|
4292
|
+
const keys = Object.keys(target);
|
|
4293
|
+
for (const key of keys) {
|
|
4294
|
+
if (Number.isInteger(key)) {
|
|
4295
|
+
throw new Error("Invalid package configuration");
|
|
4296
|
+
}
|
|
4297
|
+
if (key === "default" || conditions.includes(key)) {
|
|
4298
|
+
const targetValue = target[key];
|
|
4299
|
+
const resolved = applyPackageTargetResolution(targetValue, {
|
|
4300
|
+
...resolutionContext,
|
|
4301
|
+
key,
|
|
4302
|
+
subpath,
|
|
4303
|
+
pattern,
|
|
4304
|
+
isImport,
|
|
4305
|
+
});
|
|
4306
|
+
if (resolved) {
|
|
4307
|
+
return resolved;
|
|
3677
4308
|
}
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
return null;
|
|
4312
|
+
}
|
|
4313
|
+
throw createInvalidPackageTargetError(
|
|
4314
|
+
`target must be a string, array, object or null`,
|
|
4315
|
+
target,
|
|
4316
|
+
resolutionContext,
|
|
4317
|
+
);
|
|
4318
|
+
};
|
|
4319
|
+
|
|
4320
|
+
const readExports = ({ packageDirectoryUrl, packageJson }) => {
|
|
4321
|
+
const packageExports = packageJson.exports;
|
|
4322
|
+
if (Array.isArray(packageExports)) {
|
|
4323
|
+
return {
|
|
4324
|
+
type: "array",
|
|
4325
|
+
};
|
|
4326
|
+
}
|
|
4327
|
+
if (packageExports === null) {
|
|
4328
|
+
return {};
|
|
4329
|
+
}
|
|
4330
|
+
if (typeof packageExports === "object") {
|
|
4331
|
+
const keys = Object.keys(packageExports);
|
|
4332
|
+
const relativeKeys = [];
|
|
4333
|
+
const conditionalKeys = [];
|
|
4334
|
+
keys.forEach((availableKey) => {
|
|
4335
|
+
if (availableKey.startsWith(".")) {
|
|
4336
|
+
relativeKeys.push(availableKey);
|
|
3678
4337
|
} else {
|
|
3679
|
-
|
|
4338
|
+
conditionalKeys.push(availableKey);
|
|
3680
4339
|
}
|
|
3681
4340
|
});
|
|
3682
|
-
|
|
4341
|
+
const hasRelativeKey = relativeKeys.length > 0;
|
|
4342
|
+
if (hasRelativeKey && conditionalKeys.length > 0) {
|
|
4343
|
+
throw new Error(
|
|
4344
|
+
`Invalid package configuration: cannot mix relative and conditional keys in package.exports
|
|
4345
|
+
--- unexpected keys ---
|
|
4346
|
+
${conditionalKeys.map((key) => `"${key}"`).join("\n")}
|
|
4347
|
+
--- package directory url ---
|
|
4348
|
+
${packageDirectoryUrl}`,
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
return {
|
|
4352
|
+
type: "object",
|
|
4353
|
+
hasRelativeKey,
|
|
4354
|
+
allKeysAreRelative: relativeKeys.length === keys.length,
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
if (typeof packageExports === "string") {
|
|
4358
|
+
return { type: "string" };
|
|
4359
|
+
}
|
|
4360
|
+
return {};
|
|
3683
4361
|
};
|
|
3684
4362
|
|
|
3685
|
-
const
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
4363
|
+
const parsePackageSpecifier = (packageSpecifier) => {
|
|
4364
|
+
if (packageSpecifier[0] === "@") {
|
|
4365
|
+
const firstSlashIndex = packageSpecifier.indexOf("/");
|
|
4366
|
+
if (firstSlashIndex === -1) {
|
|
4367
|
+
throw new Error("invalid module specifier");
|
|
4368
|
+
}
|
|
4369
|
+
const secondSlashIndex = packageSpecifier.indexOf("/", firstSlashIndex + 1);
|
|
4370
|
+
if (secondSlashIndex === -1) {
|
|
4371
|
+
return {
|
|
4372
|
+
packageName: packageSpecifier,
|
|
4373
|
+
packageSubpath: ".",
|
|
4374
|
+
isScoped: true,
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
const packageName = packageSpecifier.slice(0, secondSlashIndex);
|
|
4378
|
+
const afterSecondSlash = packageSpecifier.slice(secondSlashIndex + 1);
|
|
4379
|
+
const packageSubpath = `./${afterSecondSlash}`;
|
|
4380
|
+
return {
|
|
4381
|
+
packageName,
|
|
4382
|
+
packageSubpath,
|
|
4383
|
+
isScoped: true,
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
const firstSlashIndex = packageSpecifier.indexOf("/");
|
|
4387
|
+
if (firstSlashIndex === -1) {
|
|
4388
|
+
return {
|
|
4389
|
+
packageName: packageSpecifier,
|
|
4390
|
+
packageSubpath: ".",
|
|
4391
|
+
};
|
|
4392
|
+
}
|
|
4393
|
+
const packageName = packageSpecifier.slice(0, firstSlashIndex);
|
|
4394
|
+
const afterFirstSlash = packageSpecifier.slice(firstSlashIndex + 1);
|
|
4395
|
+
if (afterFirstSlash === "") {
|
|
4396
|
+
return {
|
|
4397
|
+
packageName,
|
|
4398
|
+
packageSubpath: "/",
|
|
4399
|
+
};
|
|
4400
|
+
}
|
|
4401
|
+
const packageSubpath = `./${afterFirstSlash}`;
|
|
4402
|
+
return {
|
|
4403
|
+
packageName,
|
|
4404
|
+
packageSubpath,
|
|
4405
|
+
};
|
|
4406
|
+
};
|
|
3690
4407
|
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4408
|
+
const applyMainExportResolution = (resolutionContext) => {
|
|
4409
|
+
const { packageJson } = resolutionContext;
|
|
4410
|
+
const packageExportsInfo = readExports(resolutionContext);
|
|
4411
|
+
if (
|
|
4412
|
+
packageExportsInfo.type === "array" ||
|
|
4413
|
+
packageExportsInfo.type === "string"
|
|
4414
|
+
) {
|
|
4415
|
+
return packageJson.exports;
|
|
3695
4416
|
}
|
|
3696
|
-
if (
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
});
|
|
3702
|
-
return;
|
|
4417
|
+
if (packageExportsInfo.type === "object") {
|
|
4418
|
+
if (packageExportsInfo.hasRelativeKey) {
|
|
4419
|
+
return packageJson.exports["."];
|
|
4420
|
+
}
|
|
4421
|
+
return packageJson.exports;
|
|
3703
4422
|
}
|
|
4423
|
+
return undefined;
|
|
4424
|
+
};
|
|
3704
4425
|
|
|
3705
|
-
|
|
3706
|
-
const
|
|
3707
|
-
|
|
3708
|
-
|
|
4426
|
+
const applyLegacySubpathResolution = (packageSubpath, resolutionContext) => {
|
|
4427
|
+
const { packageDirectoryUrl, packageJson } = resolutionContext;
|
|
4428
|
+
|
|
4429
|
+
if (packageSubpath === ".") {
|
|
4430
|
+
return applyLegacyMainResolution(packageSubpath, resolutionContext);
|
|
4431
|
+
}
|
|
4432
|
+
const browserFieldResolution = applyBrowserFieldResolution(
|
|
4433
|
+
packageSubpath,
|
|
4434
|
+
resolutionContext,
|
|
3709
4435
|
);
|
|
4436
|
+
if (browserFieldResolution) {
|
|
4437
|
+
return browserFieldResolution;
|
|
4438
|
+
}
|
|
4439
|
+
return {
|
|
4440
|
+
type: "subpath",
|
|
4441
|
+
isMain: packageSubpath === ".",
|
|
4442
|
+
packageDirectoryUrl,
|
|
4443
|
+
packageJson,
|
|
4444
|
+
url: new URL(packageSubpath, packageDirectoryUrl).href,
|
|
4445
|
+
};
|
|
3710
4446
|
};
|
|
3711
4447
|
|
|
3712
|
-
const
|
|
3713
|
-
const
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
4448
|
+
const applyLegacyMainResolution = (packageSubpath, resolutionContext) => {
|
|
4449
|
+
const { conditions, packageDirectoryUrl, packageJson } = resolutionContext;
|
|
4450
|
+
for (const condition of conditions) {
|
|
4451
|
+
const conditionResolver = mainLegacyResolvers[condition];
|
|
4452
|
+
if (!conditionResolver) {
|
|
4453
|
+
continue;
|
|
4454
|
+
}
|
|
4455
|
+
const resolved = conditionResolver(resolutionContext);
|
|
4456
|
+
if (resolved) {
|
|
4457
|
+
return {
|
|
4458
|
+
type: resolved.type,
|
|
4459
|
+
isMain: resolved.isMain,
|
|
4460
|
+
packageDirectoryUrl,
|
|
4461
|
+
packageJson,
|
|
4462
|
+
url: new URL(resolved.path, packageDirectoryUrl).href,
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
3721
4465
|
}
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
4466
|
+
return {
|
|
4467
|
+
type: "field:main", // the absence of "main" field
|
|
4468
|
+
isMain: true,
|
|
4469
|
+
packageDirectoryUrl,
|
|
4470
|
+
packageJson,
|
|
4471
|
+
url: new URL("index.js", packageDirectoryUrl).href,
|
|
4472
|
+
};
|
|
4473
|
+
};
|
|
4474
|
+
const mainLegacyResolvers = {
|
|
4475
|
+
import: ({ packageJson }) => {
|
|
4476
|
+
if (typeof packageJson.module === "string") {
|
|
4477
|
+
return { type: "field:module", isMain: true, path: packageJson.module };
|
|
4478
|
+
}
|
|
4479
|
+
if (typeof packageJson.jsnext === "string") {
|
|
4480
|
+
return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
|
|
4481
|
+
}
|
|
4482
|
+
if (typeof packageJson.main === "string") {
|
|
4483
|
+
return { type: "field:main", isMain: true, path: packageJson.main };
|
|
4484
|
+
}
|
|
4485
|
+
return null;
|
|
4486
|
+
},
|
|
4487
|
+
browser: ({ packageDirectoryUrl, packageJson }) => {
|
|
4488
|
+
const browserMain = (() => {
|
|
4489
|
+
if (typeof packageJson.browser === "string") {
|
|
4490
|
+
return packageJson.browser;
|
|
4491
|
+
}
|
|
4492
|
+
if (
|
|
4493
|
+
typeof packageJson.browser === "object" &&
|
|
4494
|
+
packageJson.browser !== null
|
|
4495
|
+
) {
|
|
4496
|
+
return packageJson.browser["."];
|
|
4497
|
+
}
|
|
4498
|
+
return "";
|
|
4499
|
+
})();
|
|
4500
|
+
|
|
4501
|
+
if (!browserMain) {
|
|
4502
|
+
if (typeof packageJson.module === "string") {
|
|
4503
|
+
return {
|
|
4504
|
+
type: "field:module",
|
|
4505
|
+
isMain: true,
|
|
4506
|
+
path: packageJson.module,
|
|
4507
|
+
};
|
|
4508
|
+
}
|
|
4509
|
+
return null;
|
|
4510
|
+
}
|
|
4511
|
+
if (
|
|
4512
|
+
typeof packageJson.module !== "string" ||
|
|
4513
|
+
packageJson.module === browserMain
|
|
4514
|
+
) {
|
|
4515
|
+
return {
|
|
4516
|
+
type: "field:browser",
|
|
4517
|
+
isMain: true,
|
|
4518
|
+
path: browserMain,
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
const browserMainUrlObject = new URL(browserMain, packageDirectoryUrl);
|
|
4522
|
+
const content = readFileSync(browserMainUrlObject, "utf-8");
|
|
4523
|
+
if (
|
|
4524
|
+
(/typeof exports\s*==/.test(content) &&
|
|
4525
|
+
/typeof module\s*==/.test(content)) ||
|
|
4526
|
+
/module\.exports\s*=/.test(content)
|
|
4527
|
+
) {
|
|
4528
|
+
return {
|
|
4529
|
+
type: "field:module",
|
|
4530
|
+
isMain: true,
|
|
4531
|
+
path: packageJson.module,
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
return {
|
|
4535
|
+
type: "field:browser",
|
|
4536
|
+
isMain: true,
|
|
4537
|
+
path: browserMain,
|
|
4538
|
+
};
|
|
4539
|
+
},
|
|
4540
|
+
node: ({ packageJson, conditions }) => {
|
|
4541
|
+
if (conditions.includes("import")) {
|
|
4542
|
+
if (typeof packageJson.module === "string") {
|
|
4543
|
+
return { type: "field:module", isMain: true, path: packageJson.module };
|
|
4544
|
+
}
|
|
4545
|
+
if (typeof packageJson.jsnext === "string") {
|
|
4546
|
+
return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
if (typeof packageJson.main === "string") {
|
|
4550
|
+
return { type: "field:main", isMain: true, path: packageJson.main };
|
|
4551
|
+
}
|
|
4552
|
+
return null;
|
|
4553
|
+
},
|
|
4554
|
+
};
|
|
4555
|
+
|
|
4556
|
+
const comparePatternKeys = (keyA, keyB) => {
|
|
4557
|
+
if (!keyA.endsWith("/") && !keyA.includes("*")) {
|
|
4558
|
+
throw new Error("Invalid package configuration");
|
|
4559
|
+
}
|
|
4560
|
+
if (!keyB.endsWith("/") && !keyB.includes("*")) {
|
|
4561
|
+
throw new Error("Invalid package configuration");
|
|
4562
|
+
}
|
|
4563
|
+
const aStarIndex = keyA.indexOf("*");
|
|
4564
|
+
const baseLengthA = aStarIndex > -1 ? aStarIndex + 1 : keyA.length;
|
|
4565
|
+
const bStarIndex = keyB.indexOf("*");
|
|
4566
|
+
const baseLengthB = bStarIndex > -1 ? bStarIndex + 1 : keyB.length;
|
|
4567
|
+
if (baseLengthA > baseLengthB) {
|
|
4568
|
+
return -1;
|
|
4569
|
+
}
|
|
4570
|
+
if (baseLengthB > baseLengthA) {
|
|
4571
|
+
return 1;
|
|
4572
|
+
}
|
|
4573
|
+
if (aStarIndex === -1) {
|
|
4574
|
+
return 1;
|
|
4575
|
+
}
|
|
4576
|
+
if (bStarIndex === -1) {
|
|
4577
|
+
return -1;
|
|
4578
|
+
}
|
|
4579
|
+
if (keyA.length > keyB.length) {
|
|
4580
|
+
return -1;
|
|
4581
|
+
}
|
|
4582
|
+
if (keyB.length > keyA.length) {
|
|
4583
|
+
return 1;
|
|
3730
4584
|
}
|
|
4585
|
+
return 0;
|
|
4586
|
+
};
|
|
3731
4587
|
|
|
3732
|
-
|
|
3733
|
-
const
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
);
|
|
4588
|
+
const resolvePackageSymlink = (packageDirectoryUrl) => {
|
|
4589
|
+
const packageDirectoryPath = realpathSync(new URL(packageDirectoryUrl));
|
|
4590
|
+
const packageDirectoryResolvedUrl = pathToFileURL(packageDirectoryPath).href;
|
|
4591
|
+
return `${packageDirectoryResolvedUrl}/`;
|
|
3737
4592
|
};
|
|
3738
4593
|
|
|
3739
4594
|
const countAvailableCpus = () => {
|
|
@@ -8551,6 +9406,86 @@ const createWekbitRuntime = (params) => {
|
|
|
8551
9406
|
});
|
|
8552
9407
|
};
|
|
8553
9408
|
|
|
9409
|
+
const findFreePort = async (
|
|
9410
|
+
initialPort = 1,
|
|
9411
|
+
{
|
|
9412
|
+
signal = new AbortController().signal,
|
|
9413
|
+
hostname = "127.0.0.1",
|
|
9414
|
+
min = 1,
|
|
9415
|
+
max = 65534,
|
|
9416
|
+
next = (port) => port + 1,
|
|
9417
|
+
} = {},
|
|
9418
|
+
) => {
|
|
9419
|
+
const findFreePortOperation = Abort.startOperation();
|
|
9420
|
+
try {
|
|
9421
|
+
findFreePortOperation.addAbortSignal(signal);
|
|
9422
|
+
findFreePortOperation.throwIfAborted();
|
|
9423
|
+
|
|
9424
|
+
const testUntil = async (port, host) => {
|
|
9425
|
+
findFreePortOperation.throwIfAborted();
|
|
9426
|
+
const free = await portIsFree(port, host);
|
|
9427
|
+
if (free) {
|
|
9428
|
+
return port;
|
|
9429
|
+
}
|
|
9430
|
+
|
|
9431
|
+
const nextPort = next(port);
|
|
9432
|
+
if (nextPort > max) {
|
|
9433
|
+
throw new Error(
|
|
9434
|
+
`${hostname} has no available port between ${min} and ${max}`,
|
|
9435
|
+
);
|
|
9436
|
+
}
|
|
9437
|
+
return testUntil(nextPort, hostname);
|
|
9438
|
+
};
|
|
9439
|
+
const freePort = await testUntil(initialPort, hostname);
|
|
9440
|
+
return freePort;
|
|
9441
|
+
} finally {
|
|
9442
|
+
await findFreePortOperation.end();
|
|
9443
|
+
}
|
|
9444
|
+
};
|
|
9445
|
+
|
|
9446
|
+
const portIsFree = async (port, hostname) => {
|
|
9447
|
+
const server = createServer();
|
|
9448
|
+
|
|
9449
|
+
try {
|
|
9450
|
+
await startListening({
|
|
9451
|
+
server,
|
|
9452
|
+
port,
|
|
9453
|
+
hostname,
|
|
9454
|
+
});
|
|
9455
|
+
} catch (error) {
|
|
9456
|
+
if (error && error.code === "EADDRINUSE") {
|
|
9457
|
+
return false;
|
|
9458
|
+
}
|
|
9459
|
+
if (error && error.code === "EACCES") {
|
|
9460
|
+
return false;
|
|
9461
|
+
}
|
|
9462
|
+
throw error;
|
|
9463
|
+
}
|
|
9464
|
+
|
|
9465
|
+
await stopListening(server);
|
|
9466
|
+
return true;
|
|
9467
|
+
};
|
|
9468
|
+
|
|
9469
|
+
const startListening = ({ server, port, hostname }) => {
|
|
9470
|
+
return new Promise((resolve, reject) => {
|
|
9471
|
+
server.on("error", reject);
|
|
9472
|
+
server.on("listening", () => {
|
|
9473
|
+
// in case port is 0 (randomly assign an available port)
|
|
9474
|
+
// https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
|
|
9475
|
+
resolve(server.address().port);
|
|
9476
|
+
});
|
|
9477
|
+
server.listen(port, hostname);
|
|
9478
|
+
});
|
|
9479
|
+
};
|
|
9480
|
+
|
|
9481
|
+
const stopListening = (server) => {
|
|
9482
|
+
return new Promise((resolve, reject) => {
|
|
9483
|
+
server.on("error", reject);
|
|
9484
|
+
server.on("close", resolve);
|
|
9485
|
+
server.close();
|
|
9486
|
+
});
|
|
9487
|
+
};
|
|
9488
|
+
|
|
8554
9489
|
const ExecOptions = {
|
|
8555
9490
|
fromExecArgv: (execArgv) => {
|
|
8556
9491
|
const execOptions = {};
|