@jsenv/core 41.4.1 → 41.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { createSupportsColor, isUnicodeSupported, eastAsianWidth, clearTerminal, eraseLines } from "./jsenv_core_node_modules.js";
2
2
  import { stripVTControlCharacters } from "node:util";
3
- import { readFileSync, existsSync, chmodSync, statSync, lstatSync, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, writeFileSync as writeFileSync$1, watch, realpathSync } from "node:fs";
3
+ import { readFileSync, existsSync, readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, writeFileSync as writeFileSync$1, watch, realpathSync } from "node:fs";
4
4
  import { extname, basename, dirname } from "node:path";
5
5
  import crypto, { createHash } from "node:crypto";
6
6
  import { pathToFileURL, fileURLToPath } from "node:url";
@@ -1978,7 +1978,7 @@ const compareFileUrls = (a, b) => {
1978
1978
  return comparePathnames(new URL(a).pathname, new URL(b).pathname);
1979
1979
  };
1980
1980
 
1981
- const isWindows$2 = process.platform === "win32";
1981
+ const isWindows$3 = process.platform === "win32";
1982
1982
  const baseUrlFallback = fileSystemPathToUrl(process.cwd());
1983
1983
 
1984
1984
  /**
@@ -2001,7 +2001,7 @@ const ensureWindowsDriveLetter = (url, baseUrl) => {
2001
2001
  throw new Error(`absolute url expect but got ${url}`);
2002
2002
  }
2003
2003
 
2004
- if (!isWindows$2) {
2004
+ if (!isWindows$3) {
2005
2005
  return url;
2006
2006
  }
2007
2007
 
@@ -2113,6 +2113,457 @@ const readPackageAtOrNull = (packageDirectoryUrl) => {
2113
2113
  }
2114
2114
  };
2115
2115
 
2116
+ const createCallbackListNotifiedOnce = () => {
2117
+ let callbacks = [];
2118
+ let status = "waiting";
2119
+ let currentCallbackIndex = -1;
2120
+
2121
+ const callbackListOnce = {};
2122
+
2123
+ const add = (callback) => {
2124
+ if (status !== "waiting") {
2125
+ emitUnexpectedActionWarning({ action: "add", status });
2126
+ return removeNoop;
2127
+ }
2128
+
2129
+ if (typeof callback !== "function") {
2130
+ throw new Error(`callback must be a function, got ${callback}`);
2131
+ }
2132
+
2133
+ // don't register twice
2134
+ const existingCallback = callbacks.find((callbackCandidate) => {
2135
+ return callbackCandidate === callback;
2136
+ });
2137
+ if (existingCallback) {
2138
+ emitCallbackDuplicationWarning();
2139
+ return removeNoop;
2140
+ }
2141
+
2142
+ callbacks.push(callback);
2143
+ return () => {
2144
+ if (status === "notified") {
2145
+ // once called removing does nothing
2146
+ // as the callbacks array is frozen to null
2147
+ return;
2148
+ }
2149
+
2150
+ const index = callbacks.indexOf(callback);
2151
+ if (index === -1) {
2152
+ return;
2153
+ }
2154
+
2155
+ if (status === "looping") {
2156
+ if (index <= currentCallbackIndex) {
2157
+ // The callback was already called (or is the current callback)
2158
+ // We don't want to mutate the callbacks array
2159
+ // or it would alter the looping done in "call" and the next callback
2160
+ // would be skipped
2161
+ return;
2162
+ }
2163
+
2164
+ // Callback is part of the next callback to call,
2165
+ // we mutate the callbacks array to prevent this callback to be called
2166
+ }
2167
+
2168
+ callbacks.splice(index, 1);
2169
+ };
2170
+ };
2171
+
2172
+ const notify = (param) => {
2173
+ if (status !== "waiting") {
2174
+ emitUnexpectedActionWarning({ action: "call", status });
2175
+ return [];
2176
+ }
2177
+ status = "looping";
2178
+ const values = callbacks.map((callback, index) => {
2179
+ currentCallbackIndex = index;
2180
+ return callback(param);
2181
+ });
2182
+ callbackListOnce.notified = true;
2183
+ status = "notified";
2184
+ // we reset callbacks to null after looping
2185
+ // so that it's possible to remove during the loop
2186
+ callbacks = null;
2187
+ currentCallbackIndex = -1;
2188
+
2189
+ return values;
2190
+ };
2191
+
2192
+ callbackListOnce.notified = false;
2193
+ callbackListOnce.add = add;
2194
+ callbackListOnce.notify = notify;
2195
+
2196
+ return callbackListOnce;
2197
+ };
2198
+
2199
+ const emitUnexpectedActionWarning = ({ action, status }) => {
2200
+ if (typeof process.emitWarning === "function") {
2201
+ process.emitWarning(
2202
+ `"${action}" should not happen when callback list is ${status}`,
2203
+ {
2204
+ CODE: "UNEXPECTED_ACTION_ON_CALLBACK_LIST",
2205
+ detail: `Code is potentially executed when it should not`,
2206
+ },
2207
+ );
2208
+ } else {
2209
+ console.warn(
2210
+ `"${action}" should not happen when callback list is ${status}`,
2211
+ );
2212
+ }
2213
+ };
2214
+
2215
+ const emitCallbackDuplicationWarning = () => {
2216
+ if (typeof process.emitWarning === "function") {
2217
+ process.emitWarning(`Trying to add a callback already in the list`, {
2218
+ CODE: "CALLBACK_DUPLICATION",
2219
+ detail: `Code is potentially executed more than it should`,
2220
+ });
2221
+ } else {
2222
+ console.warn(`Trying to add same callback twice`);
2223
+ }
2224
+ };
2225
+
2226
+ const removeNoop = () => {};
2227
+
2228
+ /*
2229
+ * See callback_race.md
2230
+ */
2231
+
2232
+ const raceCallbacks = (raceDescription, winnerCallback) => {
2233
+ let cleanCallbacks = [];
2234
+ let status = "racing";
2235
+
2236
+ const clean = () => {
2237
+ cleanCallbacks.forEach((clean) => {
2238
+ clean();
2239
+ });
2240
+ cleanCallbacks = null;
2241
+ };
2242
+
2243
+ const cancel = () => {
2244
+ if (status !== "racing") {
2245
+ return;
2246
+ }
2247
+ status = "cancelled";
2248
+ clean();
2249
+ };
2250
+
2251
+ Object.keys(raceDescription).forEach((candidateName) => {
2252
+ const register = raceDescription[candidateName];
2253
+ const returnValue = register((data) => {
2254
+ if (status !== "racing") {
2255
+ return;
2256
+ }
2257
+ status = "done";
2258
+ clean();
2259
+ winnerCallback({
2260
+ name: candidateName,
2261
+ data,
2262
+ });
2263
+ });
2264
+ if (typeof returnValue === "function") {
2265
+ cleanCallbacks.push(returnValue);
2266
+ }
2267
+ });
2268
+
2269
+ return cancel;
2270
+ };
2271
+
2272
+ /*
2273
+ * https://github.com/whatwg/dom/issues/920
2274
+ */
2275
+
2276
+
2277
+ const Abort = {
2278
+ isAbortError: (error) => {
2279
+ return error && error.name === "AbortError";
2280
+ },
2281
+
2282
+ startOperation: () => {
2283
+ return createOperation();
2284
+ },
2285
+
2286
+ throwIfAborted: (signal) => {
2287
+ if (signal.aborted) {
2288
+ const error = new Error(`The operation was aborted`);
2289
+ error.name = "AbortError";
2290
+ error.type = "aborted";
2291
+ throw error;
2292
+ }
2293
+ },
2294
+ };
2295
+
2296
+ const createOperation = () => {
2297
+ const operationAbortController = new AbortController();
2298
+ // const abortOperation = (value) => abortController.abort(value)
2299
+ const operationSignal = operationAbortController.signal;
2300
+
2301
+ // abortCallbackList is used to ignore the max listeners warning from Node.js
2302
+ // this warning is useful but becomes problematic when it's expect
2303
+ // (a function doing 20 http call in parallel)
2304
+ // To be 100% sure we don't have memory leak, only Abortable.asyncCallback
2305
+ // uses abortCallbackList to know when something is aborted
2306
+ const abortCallbackList = createCallbackListNotifiedOnce();
2307
+ const endCallbackList = createCallbackListNotifiedOnce();
2308
+
2309
+ let isAbortAfterEnd = false;
2310
+
2311
+ operationSignal.onabort = () => {
2312
+ operationSignal.onabort = null;
2313
+
2314
+ const allAbortCallbacksPromise = Promise.all(abortCallbackList.notify());
2315
+ if (!isAbortAfterEnd) {
2316
+ addEndCallback(async () => {
2317
+ await allAbortCallbacksPromise;
2318
+ });
2319
+ }
2320
+ };
2321
+
2322
+ const throwIfAborted = () => {
2323
+ Abort.throwIfAborted(operationSignal);
2324
+ };
2325
+
2326
+ // add a callback called on abort
2327
+ // differences with signal.addEventListener('abort')
2328
+ // - operation.end awaits the return value of this callback
2329
+ // - It won't increase the count of listeners for "abort" that would
2330
+ // trigger max listeners warning when count > 10
2331
+ const addAbortCallback = (callback) => {
2332
+ // It would be painful and not super redable to check if signal is aborted
2333
+ // before deciding if it's an abort or end callback
2334
+ // with pseudo-code below where we want to stop server either
2335
+ // on abort or when ended because signal is aborted
2336
+ // operation[operation.signal.aborted ? 'addAbortCallback': 'addEndCallback'](async () => {
2337
+ // await server.stop()
2338
+ // })
2339
+ if (operationSignal.aborted) {
2340
+ return addEndCallback(callback);
2341
+ }
2342
+ return abortCallbackList.add(callback);
2343
+ };
2344
+
2345
+ const addEndCallback = (callback) => {
2346
+ return endCallbackList.add(callback);
2347
+ };
2348
+
2349
+ const end = async ({ abortAfterEnd = false } = {}) => {
2350
+ await Promise.all(endCallbackList.notify());
2351
+
2352
+ // "abortAfterEnd" can be handy to ensure "abort" callbacks
2353
+ // added with { once: true } are removed
2354
+ // It might also help garbage collection because
2355
+ // runtime implementing AbortSignal (Node.js, browsers) can consider abortSignal
2356
+ // as settled and clean up things
2357
+ if (abortAfterEnd) {
2358
+ // because of operationSignal.onabort = null
2359
+ // + abortCallbackList.clear() this won't re-call
2360
+ // callbacks
2361
+ if (!operationSignal.aborted) {
2362
+ isAbortAfterEnd = true;
2363
+ operationAbortController.abort();
2364
+ }
2365
+ }
2366
+ };
2367
+
2368
+ const addAbortSignal = (
2369
+ signal,
2370
+ { onAbort = callbackNoop, onRemove = callbackNoop } = {},
2371
+ ) => {
2372
+ const applyAbortEffects = () => {
2373
+ const onAbortCallback = onAbort;
2374
+ onAbort = callbackNoop;
2375
+ onAbortCallback();
2376
+ };
2377
+ const applyRemoveEffects = () => {
2378
+ const onRemoveCallback = onRemove;
2379
+ onRemove = callbackNoop;
2380
+ onAbort = callbackNoop;
2381
+ onRemoveCallback();
2382
+ };
2383
+
2384
+ if (operationSignal.aborted) {
2385
+ applyAbortEffects();
2386
+ applyRemoveEffects();
2387
+ return callbackNoop;
2388
+ }
2389
+
2390
+ if (signal.aborted) {
2391
+ operationAbortController.abort();
2392
+ applyAbortEffects();
2393
+ applyRemoveEffects();
2394
+ return callbackNoop;
2395
+ }
2396
+
2397
+ const cancelRace = raceCallbacks(
2398
+ {
2399
+ operation_abort: (cb) => {
2400
+ return addAbortCallback(cb);
2401
+ },
2402
+ operation_end: (cb) => {
2403
+ return addEndCallback(cb);
2404
+ },
2405
+ child_abort: (cb) => {
2406
+ return addEventListener(signal, "abort", cb);
2407
+ },
2408
+ },
2409
+ (winner) => {
2410
+ const raceEffects = {
2411
+ // Both "operation_abort" and "operation_end"
2412
+ // means we don't care anymore if the child aborts.
2413
+ // So we can:
2414
+ // - remove "abort" event listener on child (done by raceCallback)
2415
+ // - remove abort callback on operation (done by raceCallback)
2416
+ // - remove end callback on operation (done by raceCallback)
2417
+ // - call any custom cancel function
2418
+ operation_abort: () => {
2419
+ applyAbortEffects();
2420
+ applyRemoveEffects();
2421
+ },
2422
+ operation_end: () => {
2423
+ // Exists to
2424
+ // - remove abort callback on operation
2425
+ // - remove "abort" event listener on child
2426
+ // - call any custom cancel function
2427
+ applyRemoveEffects();
2428
+ },
2429
+ child_abort: () => {
2430
+ applyAbortEffects();
2431
+ operationAbortController.abort();
2432
+ },
2433
+ };
2434
+ raceEffects[winner.name](winner.value);
2435
+ },
2436
+ );
2437
+
2438
+ return () => {
2439
+ cancelRace();
2440
+ applyRemoveEffects();
2441
+ };
2442
+ };
2443
+
2444
+ const addAbortSource = (abortSourceCallback) => {
2445
+ const abortSource = {
2446
+ cleaned: false,
2447
+ signal: null,
2448
+ remove: callbackNoop,
2449
+ };
2450
+ const abortSourceController = new AbortController();
2451
+ const abortSourceSignal = abortSourceController.signal;
2452
+ abortSource.signal = abortSourceSignal;
2453
+ if (operationSignal.aborted) {
2454
+ return abortSource;
2455
+ }
2456
+ const returnValue = abortSourceCallback((value) => {
2457
+ abortSourceController.abort(value);
2458
+ });
2459
+ const removeAbortSignal = addAbortSignal(abortSourceSignal, {
2460
+ onRemove: () => {
2461
+ if (typeof returnValue === "function") {
2462
+ returnValue();
2463
+ }
2464
+ abortSource.cleaned = true;
2465
+ },
2466
+ });
2467
+ abortSource.remove = removeAbortSignal;
2468
+ return abortSource;
2469
+ };
2470
+
2471
+ const timeout = (ms) => {
2472
+ return addAbortSource((abort) => {
2473
+ const timeoutId = setTimeout(abort, ms);
2474
+ // an abort source return value is called when:
2475
+ // - operation is aborted (by an other source)
2476
+ // - operation ends
2477
+ return () => {
2478
+ clearTimeout(timeoutId);
2479
+ };
2480
+ });
2481
+ };
2482
+
2483
+ const wait = (ms) => {
2484
+ return new Promise((resolve) => {
2485
+ const timeoutId = setTimeout(() => {
2486
+ removeAbortCallback();
2487
+ resolve();
2488
+ }, ms);
2489
+ const removeAbortCallback = addAbortCallback(() => {
2490
+ clearTimeout(timeoutId);
2491
+ });
2492
+ });
2493
+ };
2494
+
2495
+ const withSignal = async (asyncCallback) => {
2496
+ const abortController = new AbortController();
2497
+ const signal = abortController.signal;
2498
+ const removeAbortSignal = addAbortSignal(signal, {
2499
+ onAbort: () => {
2500
+ abortController.abort();
2501
+ },
2502
+ });
2503
+ try {
2504
+ const value = await asyncCallback(signal);
2505
+ removeAbortSignal();
2506
+ return value;
2507
+ } catch (e) {
2508
+ removeAbortSignal();
2509
+ throw e;
2510
+ }
2511
+ };
2512
+
2513
+ const withSignalSync = (callback) => {
2514
+ const abortController = new AbortController();
2515
+ const signal = abortController.signal;
2516
+ const removeAbortSignal = addAbortSignal(signal, {
2517
+ onAbort: () => {
2518
+ abortController.abort();
2519
+ },
2520
+ });
2521
+ try {
2522
+ const value = callback(signal);
2523
+ removeAbortSignal();
2524
+ return value;
2525
+ } catch (e) {
2526
+ removeAbortSignal();
2527
+ throw e;
2528
+ }
2529
+ };
2530
+
2531
+ const fork = () => {
2532
+ const forkedOperation = createOperation();
2533
+ forkedOperation.addAbortSignal(operationSignal);
2534
+ return forkedOperation;
2535
+ };
2536
+
2537
+ return {
2538
+ // We could almost hide the operationSignal
2539
+ // But it can be handy for 2 things:
2540
+ // - know if operation is aborted (operation.signal.aborted)
2541
+ // - forward the operation.signal directly (not using "withSignal" or "withSignalSync")
2542
+ signal: operationSignal,
2543
+
2544
+ throwIfAborted,
2545
+ addAbortCallback,
2546
+ addAbortSignal,
2547
+ addAbortSource,
2548
+ fork,
2549
+ timeout,
2550
+ wait,
2551
+ withSignal,
2552
+ withSignalSync,
2553
+ addEndCallback,
2554
+ end,
2555
+ };
2556
+ };
2557
+
2558
+ const callbackNoop = () => {};
2559
+
2560
+ const addEventListener = (target, eventName, cb) => {
2561
+ target.addEventListener(eventName, cb);
2562
+ return () => {
2563
+ target.removeEventListener(eventName, cb);
2564
+ };
2565
+ };
2566
+
2116
2567
  /*
2117
2568
  * Link to things doing pattern matching:
2118
2569
  * https://git-scm.com/docs/gitignore
@@ -2716,6 +3167,43 @@ const URL_META = {
2716
3167
  createFilter,
2717
3168
  };
2718
3169
 
3170
+ const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
3171
+ const directoryUrl = assertAndNormalizeDirectoryUrl(url);
3172
+ const directoryUrlObject = new URL(directoryUrl);
3173
+ const startMs = Date.now();
3174
+ let attemptCount = 0;
3175
+
3176
+ const attempt = async () => {
3177
+ try {
3178
+ const names = await new Promise((resolve, reject) => {
3179
+ readdir(directoryUrlObject, (error, names) => {
3180
+ if (error) {
3181
+ reject(error);
3182
+ } else {
3183
+ resolve(names);
3184
+ }
3185
+ });
3186
+ });
3187
+ return names.map(encodeURIComponent);
3188
+ } catch (e) {
3189
+ // https://nodejs.org/dist/latest-v13.x/docs/api/errors.html#errors_common_system_errors
3190
+ if (e.code === "EMFILE" || e.code === "ENFILE") {
3191
+ attemptCount++;
3192
+ const nowMs = Date.now();
3193
+ const timeSpentWaiting = nowMs - startMs;
3194
+ if (timeSpentWaiting > emfileMaxWait) {
3195
+ throw e;
3196
+ }
3197
+ await new Promise((resolve) => setTimeout(resolve), attemptCount);
3198
+ return await attempt();
3199
+ }
3200
+ throw e;
3201
+ }
3202
+ };
3203
+
3204
+ return attempt();
3205
+ };
3206
+
2719
3207
  const generateWindowsEPERMErrorMessage = (
2720
3208
  error,
2721
3209
  { operation, path },
@@ -2737,6 +3225,209 @@ const generateWindowsEPERMErrorMessage = (
2737
3225
  return message;
2738
3226
  };
2739
3227
 
3228
+ const writeEntryPermissions = async (source, permissions) => {
3229
+ const sourceUrl = assertAndNormalizeFileUrl(source);
3230
+
3231
+ let binaryFlags;
3232
+ {
3233
+ binaryFlags = permissions;
3234
+ }
3235
+
3236
+ return new Promise((resolve, reject) => {
3237
+ chmod(new URL(sourceUrl), binaryFlags, (error) => {
3238
+ if (error) {
3239
+ reject(error);
3240
+ } else {
3241
+ resolve();
3242
+ }
3243
+ });
3244
+ });
3245
+ };
3246
+
3247
+ /*
3248
+ * - stats object documentation on Node.js
3249
+ * https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
3250
+ */
3251
+
3252
+
3253
+ const isWindows$2 = process.platform === "win32";
3254
+
3255
+ const readEntryStat = async (
3256
+ source,
3257
+ { nullIfNotFound = false, followLink = true } = {},
3258
+ ) => {
3259
+ let sourceUrl = assertAndNormalizeFileUrl(source);
3260
+ if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);
3261
+
3262
+ const sourcePath = urlToFileSystemPath(sourceUrl);
3263
+
3264
+ const handleNotFoundOption = nullIfNotFound
3265
+ ? {
3266
+ handleNotFoundError: () => null,
3267
+ }
3268
+ : {};
3269
+
3270
+ return readStat(sourcePath, {
3271
+ followLink,
3272
+ ...handleNotFoundOption,
3273
+ ...(isWindows$2
3274
+ ? {
3275
+ // Windows can EPERM on stat
3276
+ handlePermissionDeniedError: async (error) => {
3277
+ console.error(
3278
+ `trying to fix windows EPERM after stats on ${sourcePath}`,
3279
+ );
3280
+
3281
+ try {
3282
+ // unfortunately it means we mutate the permissions
3283
+ // without being able to restore them to the previous value
3284
+ // (because reading current permission would also throw)
3285
+ await writeEntryPermissions(sourceUrl, 0o666);
3286
+ const stats = await readStat(sourcePath, {
3287
+ followLink,
3288
+ ...handleNotFoundOption,
3289
+ // could not fix the permission error, give up and throw original error
3290
+ handlePermissionDeniedError: () => {
3291
+ console.error(`still got EPERM after stats on ${sourcePath}`);
3292
+ throw error;
3293
+ },
3294
+ });
3295
+ return stats;
3296
+ } catch (e) {
3297
+ console.error(
3298
+ generateWindowsEPERMErrorMessage(e, {
3299
+ operation: "stats",
3300
+ path: sourcePath,
3301
+ }),
3302
+ );
3303
+ throw error;
3304
+ }
3305
+ },
3306
+ }
3307
+ : {}),
3308
+ });
3309
+ };
3310
+
3311
+ const readStat = (
3312
+ sourcePath,
3313
+ {
3314
+ followLink,
3315
+ handleNotFoundError = null,
3316
+ handlePermissionDeniedError = null,
3317
+ } = {},
3318
+ ) => {
3319
+ const nodeMethod = followLink ? stat : lstat;
3320
+
3321
+ return new Promise((resolve, reject) => {
3322
+ nodeMethod(sourcePath, (error, statsObject) => {
3323
+ if (error) {
3324
+ if (handleNotFoundError && error.code === "ENOENT") {
3325
+ resolve(handleNotFoundError(error));
3326
+ } else if (
3327
+ handlePermissionDeniedError &&
3328
+ (error.code === "EPERM" || error.code === "EACCES")
3329
+ ) {
3330
+ resolve(handlePermissionDeniedError(error));
3331
+ } else {
3332
+ reject(error);
3333
+ }
3334
+ } else {
3335
+ resolve(statsObject);
3336
+ }
3337
+ });
3338
+ });
3339
+ };
3340
+
3341
+ const collectFiles = async ({
3342
+ signal = new AbortController().signal,
3343
+ directoryUrl,
3344
+ associations,
3345
+ predicate,
3346
+ }) => {
3347
+ const rootDirectoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl);
3348
+ if (typeof predicate !== "function") {
3349
+ throw new TypeError(`predicate must be a function, got ${predicate}`);
3350
+ }
3351
+ associations = URL_META.resolveAssociations(associations, rootDirectoryUrl);
3352
+
3353
+ const collectOperation = Abort.startOperation();
3354
+ collectOperation.addAbortSignal(signal);
3355
+
3356
+ const matchingFileResultArray = [];
3357
+ const visitDirectory = async (directoryUrl) => {
3358
+ collectOperation.throwIfAborted();
3359
+ const directoryItems = await readDirectory(directoryUrl);
3360
+
3361
+ await Promise.all(
3362
+ directoryItems.map(async (directoryItem) => {
3363
+ const directoryChildNodeUrl = `${directoryUrl}${directoryItem}`;
3364
+ collectOperation.throwIfAborted();
3365
+ const directoryChildNodeStats = await readEntryStat(
3366
+ directoryChildNodeUrl,
3367
+ {
3368
+ // we ignore symlink because recursively traversed
3369
+ // so symlinked file will be discovered.
3370
+ // Moreover if they lead outside of directoryPath it can become a problem
3371
+ // like infinite recursion of whatever.
3372
+ // that we could handle using an object of pathname already seen but it will be useless
3373
+ // because directoryPath is recursively traversed
3374
+ followLink: false,
3375
+ },
3376
+ );
3377
+
3378
+ if (directoryChildNodeStats.isDirectory()) {
3379
+ const subDirectoryUrl = `${directoryChildNodeUrl}/`;
3380
+ if (
3381
+ !URL_META.urlChildMayMatch({
3382
+ url: subDirectoryUrl,
3383
+ associations,
3384
+ predicate,
3385
+ })
3386
+ ) {
3387
+ return;
3388
+ }
3389
+ await visitDirectory(subDirectoryUrl);
3390
+ return;
3391
+ }
3392
+
3393
+ if (directoryChildNodeStats.isFile()) {
3394
+ const meta = URL_META.applyAssociations({
3395
+ url: directoryChildNodeUrl,
3396
+ associations,
3397
+ });
3398
+ if (!predicate(meta)) return;
3399
+ const relativeUrl = urlToRelativeUrl(
3400
+ directoryChildNodeUrl,
3401
+ rootDirectoryUrl,
3402
+ );
3403
+ matchingFileResultArray.push({
3404
+ url: new URL(relativeUrl, rootDirectoryUrl).href,
3405
+ relativeUrl: decodeURIComponent(relativeUrl),
3406
+ meta,
3407
+ fileStats: directoryChildNodeStats,
3408
+ });
3409
+ return;
3410
+ }
3411
+ }),
3412
+ );
3413
+ };
3414
+
3415
+ try {
3416
+ await visitDirectory(rootDirectoryUrl);
3417
+
3418
+ // When we operate on thoose files later it feels more natural
3419
+ // to perform operation in the same order they appear in the filesystem.
3420
+ // It also allow to get a predictable return value.
3421
+ // For that reason we sort matchingFileResultArray
3422
+ matchingFileResultArray.sort((leftFile, rightFile) => {
3423
+ return comparePathnames(leftFile.relativeUrl, rightFile.relativeUrl);
3424
+ });
3425
+ return matchingFileResultArray;
3426
+ } finally {
3427
+ await collectOperation.end();
3428
+ }
3429
+ };
3430
+
2740
3431
  const writeEntryPermissionsSync = (source, permissions) => {
2741
3432
  const sourceUrl = assertAndNormalizeFileUrl(source);
2742
3433
 
@@ -6671,4 +7362,4 @@ const isResponseEligibleForIntegrityValidation = (response) => {
6671
7362
  return ["basic", "cors", "default"].includes(response.type);
6672
7363
  };
6673
7364
 
6674
- export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, bufferToEtag, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, registerFileLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
7365
+ export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, bufferToEtag, collectFiles, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, registerFileLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };