@jsenv/test 3.5.31 → 3.5.32
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/jsenv_test.js +331 -242
- package/package.json +2 -2
package/dist/jsenv_test.js
CHANGED
|
@@ -4,7 +4,7 @@ import tty from "node:tty";
|
|
|
4
4
|
import stringWidth from "string-width";
|
|
5
5
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
6
6
|
import { URL_META, filterV8Coverage } from "./js/v8_coverage.js";
|
|
7
|
-
import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1,
|
|
7
|
+
import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFile as readFile$1, unlinkSync, openSync, closeSync, readdirSync, rmdirSync, mkdirSync, readFileSync, writeFileSync as writeFileSync$1, unlink, rmdir, existsSync } from "node:fs";
|
|
8
8
|
import { dirname } from "node:path";
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import { readGitHubWorkflowEnv, startGithubCheckRun } from "@jsenv/github-check-run";
|
|
@@ -2120,6 +2120,42 @@ const extractDriveLetter = (resource) => {
|
|
|
2120
2120
|
return null;
|
|
2121
2121
|
};
|
|
2122
2122
|
|
|
2123
|
+
const getParentDirectoryUrl = (url) => {
|
|
2124
|
+
if (url.startsWith("file://")) {
|
|
2125
|
+
// With node.js new URL('../', 'file:///C:/').href
|
|
2126
|
+
// returns "file:///C:/" instead of "file:///"
|
|
2127
|
+
const resource = url.slice("file://".length);
|
|
2128
|
+
const slashLastIndex = resource.lastIndexOf("/");
|
|
2129
|
+
if (slashLastIndex === -1) {
|
|
2130
|
+
return url;
|
|
2131
|
+
}
|
|
2132
|
+
const lastCharIndex = resource.length - 1;
|
|
2133
|
+
if (slashLastIndex === lastCharIndex) {
|
|
2134
|
+
const slashBeforeLastIndex = resource.lastIndexOf(
|
|
2135
|
+
"/",
|
|
2136
|
+
slashLastIndex - 1,
|
|
2137
|
+
);
|
|
2138
|
+
if (slashBeforeLastIndex === -1) {
|
|
2139
|
+
return url;
|
|
2140
|
+
}
|
|
2141
|
+
return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
|
|
2142
|
+
}
|
|
2143
|
+
return `file://${resource.slice(0, slashLastIndex + 1)}`;
|
|
2144
|
+
}
|
|
2145
|
+
return new URL(url.endsWith("/") ? "../" : "./", url).href;
|
|
2146
|
+
};
|
|
2147
|
+
|
|
2148
|
+
const findAncestorDirectoryUrl = (url, callback) => {
|
|
2149
|
+
url = String(url);
|
|
2150
|
+
while (url !== "file:///") {
|
|
2151
|
+
if (callback(url)) {
|
|
2152
|
+
return url;
|
|
2153
|
+
}
|
|
2154
|
+
url = getParentDirectoryUrl(url);
|
|
2155
|
+
}
|
|
2156
|
+
return null;
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2123
2159
|
const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
2124
2160
|
const directoryUrl = assertAndNormalizeDirectoryUrl(url);
|
|
2125
2161
|
const directoryUrlObject = new URL(directoryUrl);
|
|
@@ -2542,7 +2578,284 @@ const readFile = async (value, { as = "buffer" } = {}) => {
|
|
|
2542
2578
|
);
|
|
2543
2579
|
};
|
|
2544
2580
|
|
|
2545
|
-
const
|
|
2581
|
+
const removeEntrySync = (
|
|
2582
|
+
source,
|
|
2583
|
+
{
|
|
2584
|
+
allowUseless = false,
|
|
2585
|
+
recursive = false,
|
|
2586
|
+
maxRetries = 3,
|
|
2587
|
+
retryDelay = 100,
|
|
2588
|
+
onlyContent = false,
|
|
2589
|
+
} = {},
|
|
2590
|
+
) => {
|
|
2591
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
2592
|
+
const sourceStats = readEntryStatSync(sourceUrl, {
|
|
2593
|
+
nullIfNotFound: true,
|
|
2594
|
+
followLink: false,
|
|
2595
|
+
});
|
|
2596
|
+
if (!sourceStats) {
|
|
2597
|
+
if (allowUseless) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
|
|
2604
|
+
// FIFO and socket are ignored, not sure what they are exactly and what to do with them
|
|
2605
|
+
// other libraries ignore them, let's do the same.
|
|
2606
|
+
if (
|
|
2607
|
+
sourceStats.isFile() ||
|
|
2608
|
+
sourceStats.isSymbolicLink() ||
|
|
2609
|
+
sourceStats.isCharacterDevice() ||
|
|
2610
|
+
sourceStats.isBlockDevice()
|
|
2611
|
+
) {
|
|
2612
|
+
removeNonDirectory$1(
|
|
2613
|
+
sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl);
|
|
2614
|
+
} else if (sourceStats.isDirectory()) {
|
|
2615
|
+
const directoryUrl = ensurePathnameTrailingSlash(sourceUrl);
|
|
2616
|
+
removeDirectorySync$1(directoryUrl, {
|
|
2617
|
+
recursive,
|
|
2618
|
+
maxRetries,
|
|
2619
|
+
retryDelay,
|
|
2620
|
+
onlyContent,
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
|
|
2625
|
+
const removeNonDirectory$1 = (sourceUrl) => {
|
|
2626
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
2627
|
+
const attempt = () => {
|
|
2628
|
+
unlinkSyncNaive(sourcePath);
|
|
2629
|
+
};
|
|
2630
|
+
attempt();
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
const unlinkSyncNaive = (sourcePath, { handleTemporaryError = null } = {}) => {
|
|
2634
|
+
try {
|
|
2635
|
+
unlinkSync(sourcePath);
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
if (error.code === "ENOENT") {
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
if (
|
|
2641
|
+
handleTemporaryError &&
|
|
2642
|
+
(error.code === "EBUSY" ||
|
|
2643
|
+
error.code === "EMFILE" ||
|
|
2644
|
+
error.code === "ENFILE" ||
|
|
2645
|
+
error.code === "ENOENT")
|
|
2646
|
+
) {
|
|
2647
|
+
handleTemporaryError(error);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
throw error;
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
|
|
2654
|
+
const removeDirectorySync$1 = (
|
|
2655
|
+
rootDirectoryUrl,
|
|
2656
|
+
{ maxRetries, retryDelay, recursive, onlyContent },
|
|
2657
|
+
) => {
|
|
2658
|
+
const visit = (sourceUrl) => {
|
|
2659
|
+
const sourceStats = readEntryStatSync(sourceUrl, {
|
|
2660
|
+
nullIfNotFound: true,
|
|
2661
|
+
followLink: false,
|
|
2662
|
+
});
|
|
2663
|
+
|
|
2664
|
+
// file/directory not found
|
|
2665
|
+
if (sourceStats === null) {
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
if (
|
|
2670
|
+
sourceStats.isFile() ||
|
|
2671
|
+
sourceStats.isCharacterDevice() ||
|
|
2672
|
+
sourceStats.isBlockDevice()
|
|
2673
|
+
) {
|
|
2674
|
+
visitFile(sourceUrl);
|
|
2675
|
+
} else if (sourceStats.isSymbolicLink()) {
|
|
2676
|
+
visitSymbolicLink(sourceUrl);
|
|
2677
|
+
} else if (sourceStats.isDirectory()) {
|
|
2678
|
+
visitDirectory(`${sourceUrl}/`);
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
|
|
2682
|
+
const visitDirectory = (directoryUrl) => {
|
|
2683
|
+
const directoryPath = urlToFileSystemPath(directoryUrl);
|
|
2684
|
+
const optionsFromRecursive = recursive
|
|
2685
|
+
? {
|
|
2686
|
+
handleNotEmptyError: () => {
|
|
2687
|
+
removeDirectoryContent(directoryUrl);
|
|
2688
|
+
visitDirectory(directoryUrl);
|
|
2689
|
+
},
|
|
2690
|
+
}
|
|
2691
|
+
: {};
|
|
2692
|
+
removeDirectorySyncNaive(directoryPath, {
|
|
2693
|
+
...optionsFromRecursive,
|
|
2694
|
+
// Workaround for https://github.com/joyent/node/issues/4337
|
|
2695
|
+
...(process.platform === "win32"
|
|
2696
|
+
? {
|
|
2697
|
+
handlePermissionError: (error) => {
|
|
2698
|
+
console.error(
|
|
2699
|
+
`trying to fix windows EPERM after readir on ${directoryPath}`,
|
|
2700
|
+
);
|
|
2701
|
+
|
|
2702
|
+
let openOrCloseError;
|
|
2703
|
+
try {
|
|
2704
|
+
const fd = openSync(directoryPath);
|
|
2705
|
+
closeSync(fd);
|
|
2706
|
+
} catch (e) {
|
|
2707
|
+
openOrCloseError = e;
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
if (openOrCloseError) {
|
|
2711
|
+
if (openOrCloseError.code === "ENOENT") {
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
console.error(
|
|
2715
|
+
`error while trying to fix windows EPERM after readir on ${directoryPath}: ${openOrCloseError.stack}`,
|
|
2716
|
+
);
|
|
2717
|
+
throw error;
|
|
2718
|
+
}
|
|
2719
|
+
removeDirectorySyncNaive(directoryPath, {
|
|
2720
|
+
...optionsFromRecursive,
|
|
2721
|
+
});
|
|
2722
|
+
},
|
|
2723
|
+
}
|
|
2724
|
+
: {}),
|
|
2725
|
+
});
|
|
2726
|
+
};
|
|
2727
|
+
|
|
2728
|
+
const removeDirectoryContent = (directoryUrl) => {
|
|
2729
|
+
const entryNames = readdirSync(new URL(directoryUrl));
|
|
2730
|
+
for (const entryName of entryNames) {
|
|
2731
|
+
const url = resolveUrl(entryName, directoryUrl);
|
|
2732
|
+
visit(url);
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
|
|
2736
|
+
const visitFile = (fileUrl) => {
|
|
2737
|
+
removeNonDirectory$1(fileUrl);
|
|
2738
|
+
};
|
|
2739
|
+
|
|
2740
|
+
const visitSymbolicLink = (symbolicLinkUrl) => {
|
|
2741
|
+
removeNonDirectory$1(symbolicLinkUrl);
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
if (onlyContent) {
|
|
2745
|
+
removeDirectoryContent(rootDirectoryUrl);
|
|
2746
|
+
} else {
|
|
2747
|
+
visitDirectory(rootDirectoryUrl);
|
|
2748
|
+
}
|
|
2749
|
+
};
|
|
2750
|
+
|
|
2751
|
+
const removeDirectorySyncNaive = (
|
|
2752
|
+
directoryPath,
|
|
2753
|
+
{ handleNotEmptyError = null, handlePermissionError = null } = {},
|
|
2754
|
+
) => {
|
|
2755
|
+
try {
|
|
2756
|
+
rmdirSync(directoryPath);
|
|
2757
|
+
} catch (error) {
|
|
2758
|
+
if (handlePermissionError && error.code === "EPERM") {
|
|
2759
|
+
handlePermissionError(error);
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
if (error.code === "ENOENT") {
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
if (
|
|
2766
|
+
handleNotEmptyError &&
|
|
2767
|
+
// linux os
|
|
2768
|
+
(error.code === "ENOTEMPTY" ||
|
|
2769
|
+
// SunOS
|
|
2770
|
+
error.code === "EEXIST")
|
|
2771
|
+
) {
|
|
2772
|
+
handleNotEmptyError(error);
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
throw error;
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
|
|
2779
|
+
const removeDirectorySync = (url, options = {}) => {
|
|
2780
|
+
return removeEntrySync(url, {
|
|
2781
|
+
...options,
|
|
2782
|
+
recursive: true,
|
|
2783
|
+
});
|
|
2784
|
+
};
|
|
2785
|
+
|
|
2786
|
+
const writeDirectorySync = (
|
|
2787
|
+
destination,
|
|
2788
|
+
{ recursive = true, allowUseless = false, force } = {},
|
|
2789
|
+
) => {
|
|
2790
|
+
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
2791
|
+
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
2792
|
+
|
|
2793
|
+
let destinationStats;
|
|
2794
|
+
try {
|
|
2795
|
+
destinationStats = readEntryStatSync(destinationUrl, {
|
|
2796
|
+
nullIfNotFound: true,
|
|
2797
|
+
followLink: false,
|
|
2798
|
+
});
|
|
2799
|
+
} catch (e) {
|
|
2800
|
+
if (e.code === "ENOTDIR") {
|
|
2801
|
+
let previousNonDirUrl = destinationUrl;
|
|
2802
|
+
// we must try all parent directories as long as it fails with ENOTDIR
|
|
2803
|
+
findAncestorDirectoryUrl(destinationUrl, (ancestorUrl) => {
|
|
2804
|
+
try {
|
|
2805
|
+
statSync(new URL(ancestorUrl));
|
|
2806
|
+
return true;
|
|
2807
|
+
} catch (e) {
|
|
2808
|
+
if (e.code === "ENOTDIR") {
|
|
2809
|
+
previousNonDirUrl = ancestorUrl;
|
|
2810
|
+
return false;
|
|
2811
|
+
}
|
|
2812
|
+
throw e;
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2815
|
+
if (force) {
|
|
2816
|
+
unlinkSync(
|
|
2817
|
+
new URL(
|
|
2818
|
+
previousNonDirUrl
|
|
2819
|
+
// remove trailing slash
|
|
2820
|
+
.slice(0, -1),
|
|
2821
|
+
),
|
|
2822
|
+
);
|
|
2823
|
+
} else {
|
|
2824
|
+
throw new Error(
|
|
2825
|
+
`cannot write directory at ${destinationPath} because there is a file at ${urlToFileSystemPath(
|
|
2826
|
+
previousNonDirUrl,
|
|
2827
|
+
)}`,
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2830
|
+
} else {
|
|
2831
|
+
throw e;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
if (destinationStats) {
|
|
2836
|
+
if (destinationStats.isDirectory()) {
|
|
2837
|
+
if (allowUseless) {
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
throw new Error(`directory already exists at ${destinationPath}`);
|
|
2841
|
+
}
|
|
2842
|
+
const destinationType = statsToType(destinationStats);
|
|
2843
|
+
throw new Error(
|
|
2844
|
+
`cannot write directory at ${destinationPath} because there is a ${destinationType}`,
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
try {
|
|
2849
|
+
mkdirSync(destinationPath, { recursive });
|
|
2850
|
+
} catch (error) {
|
|
2851
|
+
if (allowUseless && error.code === "EEXIST") {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
throw error;
|
|
2855
|
+
}
|
|
2856
|
+
};
|
|
2857
|
+
|
|
2858
|
+
const writeFileSync = (destination, content = "", { force } = {}) => {
|
|
2546
2859
|
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
2547
2860
|
const destinationUrlObject = new URL(destinationUrl);
|
|
2548
2861
|
if (content && content instanceof URL) {
|
|
@@ -2551,8 +2864,18 @@ const writeFileSync = (destination, content = "") => {
|
|
|
2551
2864
|
try {
|
|
2552
2865
|
writeFileSync$1(destinationUrlObject, content);
|
|
2553
2866
|
} catch (error) {
|
|
2554
|
-
if (error.code === "
|
|
2555
|
-
|
|
2867
|
+
if (error.code === "EISDIR") {
|
|
2868
|
+
// happens when directory existed but got deleted and now it's a file
|
|
2869
|
+
if (force) {
|
|
2870
|
+
removeDirectorySync(destinationUrlObject);
|
|
2871
|
+
writeFileSync$1(destinationUrlObject, content);
|
|
2872
|
+
} else {
|
|
2873
|
+
throw error;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
|
2877
|
+
writeDirectorySync(new URL("./", destinationUrlObject), {
|
|
2878
|
+
force,
|
|
2556
2879
|
recursive: true,
|
|
2557
2880
|
});
|
|
2558
2881
|
writeFileSync$1(destinationUrlObject, content);
|
|
@@ -2600,7 +2923,7 @@ const removeEntry = async (
|
|
|
2600
2923
|
sourceStats.isCharacterDevice() ||
|
|
2601
2924
|
sourceStats.isBlockDevice()
|
|
2602
2925
|
) {
|
|
2603
|
-
await removeNonDirectory
|
|
2926
|
+
await removeNonDirectory(
|
|
2604
2927
|
sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl,
|
|
2605
2928
|
{
|
|
2606
2929
|
maxRetries,
|
|
@@ -2621,7 +2944,7 @@ const removeEntry = async (
|
|
|
2621
2944
|
}
|
|
2622
2945
|
};
|
|
2623
2946
|
|
|
2624
|
-
const removeNonDirectory
|
|
2947
|
+
const removeNonDirectory = (sourceUrl, { maxRetries, retryDelay }) => {
|
|
2625
2948
|
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
2626
2949
|
|
|
2627
2950
|
let retryCount = 0;
|
|
@@ -2760,11 +3083,11 @@ const removeDirectory = async (
|
|
|
2760
3083
|
};
|
|
2761
3084
|
|
|
2762
3085
|
const visitFile = async (fileUrl) => {
|
|
2763
|
-
await removeNonDirectory
|
|
3086
|
+
await removeNonDirectory(fileUrl, { maxRetries, retryDelay });
|
|
2764
3087
|
};
|
|
2765
3088
|
|
|
2766
3089
|
const visitSymbolicLink = async (symbolicLinkUrl) => {
|
|
2767
|
-
await removeNonDirectory
|
|
3090
|
+
await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay });
|
|
2768
3091
|
};
|
|
2769
3092
|
|
|
2770
3093
|
try {
|
|
@@ -2809,240 +3132,6 @@ const removeDirectoryNaive = (
|
|
|
2809
3132
|
|
|
2810
3133
|
process.platform === "win32";
|
|
2811
3134
|
|
|
2812
|
-
const writeDirectorySync = (
|
|
2813
|
-
destination,
|
|
2814
|
-
{ recursive = true, allowUseless = false } = {},
|
|
2815
|
-
) => {
|
|
2816
|
-
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
2817
|
-
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
2818
|
-
|
|
2819
|
-
const destinationStats = readEntryStatSync(destinationUrl, {
|
|
2820
|
-
nullIfNotFound: true,
|
|
2821
|
-
followLink: false,
|
|
2822
|
-
});
|
|
2823
|
-
|
|
2824
|
-
if (destinationStats) {
|
|
2825
|
-
if (destinationStats.isDirectory()) {
|
|
2826
|
-
if (allowUseless) {
|
|
2827
|
-
return;
|
|
2828
|
-
}
|
|
2829
|
-
throw new Error(`directory already exists at ${destinationPath}`);
|
|
2830
|
-
}
|
|
2831
|
-
|
|
2832
|
-
const destinationType = statsToType(destinationStats);
|
|
2833
|
-
throw new Error(
|
|
2834
|
-
`cannot write directory at ${destinationPath} because there is a ${destinationType}`,
|
|
2835
|
-
);
|
|
2836
|
-
}
|
|
2837
|
-
|
|
2838
|
-
try {
|
|
2839
|
-
mkdirSync(destinationPath, { recursive });
|
|
2840
|
-
} catch (error) {
|
|
2841
|
-
if (allowUseless && error.code === "EEXIST") {
|
|
2842
|
-
return;
|
|
2843
|
-
}
|
|
2844
|
-
throw error;
|
|
2845
|
-
}
|
|
2846
|
-
};
|
|
2847
|
-
|
|
2848
|
-
const removeEntrySync = (
|
|
2849
|
-
source,
|
|
2850
|
-
{
|
|
2851
|
-
allowUseless = false,
|
|
2852
|
-
recursive = false,
|
|
2853
|
-
maxRetries = 3,
|
|
2854
|
-
retryDelay = 100,
|
|
2855
|
-
onlyContent = false,
|
|
2856
|
-
} = {},
|
|
2857
|
-
) => {
|
|
2858
|
-
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
2859
|
-
const sourceStats = readEntryStatSync(sourceUrl, {
|
|
2860
|
-
nullIfNotFound: true,
|
|
2861
|
-
followLink: false,
|
|
2862
|
-
});
|
|
2863
|
-
if (!sourceStats) {
|
|
2864
|
-
if (allowUseless) {
|
|
2865
|
-
return;
|
|
2866
|
-
}
|
|
2867
|
-
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`);
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
|
|
2871
|
-
// FIFO and socket are ignored, not sure what they are exactly and what to do with them
|
|
2872
|
-
// other libraries ignore them, let's do the same.
|
|
2873
|
-
if (
|
|
2874
|
-
sourceStats.isFile() ||
|
|
2875
|
-
sourceStats.isSymbolicLink() ||
|
|
2876
|
-
sourceStats.isCharacterDevice() ||
|
|
2877
|
-
sourceStats.isBlockDevice()
|
|
2878
|
-
) {
|
|
2879
|
-
removeNonDirectory(
|
|
2880
|
-
sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl);
|
|
2881
|
-
} else if (sourceStats.isDirectory()) {
|
|
2882
|
-
const directoryUrl = ensurePathnameTrailingSlash(sourceUrl);
|
|
2883
|
-
removeDirectorySync(directoryUrl, {
|
|
2884
|
-
recursive,
|
|
2885
|
-
maxRetries,
|
|
2886
|
-
retryDelay,
|
|
2887
|
-
onlyContent,
|
|
2888
|
-
});
|
|
2889
|
-
}
|
|
2890
|
-
};
|
|
2891
|
-
|
|
2892
|
-
const removeNonDirectory = (sourceUrl) => {
|
|
2893
|
-
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
2894
|
-
const attempt = () => {
|
|
2895
|
-
unlinkSyncNaive(sourcePath);
|
|
2896
|
-
};
|
|
2897
|
-
attempt();
|
|
2898
|
-
};
|
|
2899
|
-
|
|
2900
|
-
const unlinkSyncNaive = (sourcePath, { handleTemporaryError = null } = {}) => {
|
|
2901
|
-
try {
|
|
2902
|
-
unlinkSync(sourcePath);
|
|
2903
|
-
} catch (error) {
|
|
2904
|
-
if (error.code === "ENOENT") {
|
|
2905
|
-
return;
|
|
2906
|
-
}
|
|
2907
|
-
if (
|
|
2908
|
-
handleTemporaryError &&
|
|
2909
|
-
(error.code === "EBUSY" ||
|
|
2910
|
-
error.code === "EMFILE" ||
|
|
2911
|
-
error.code === "ENFILE" ||
|
|
2912
|
-
error.code === "ENOENT")
|
|
2913
|
-
) {
|
|
2914
|
-
handleTemporaryError(error);
|
|
2915
|
-
return;
|
|
2916
|
-
}
|
|
2917
|
-
throw error;
|
|
2918
|
-
}
|
|
2919
|
-
};
|
|
2920
|
-
|
|
2921
|
-
const removeDirectorySync = (
|
|
2922
|
-
rootDirectoryUrl,
|
|
2923
|
-
{ maxRetries, retryDelay, recursive, onlyContent },
|
|
2924
|
-
) => {
|
|
2925
|
-
const visit = (sourceUrl) => {
|
|
2926
|
-
const sourceStats = readEntryStatSync(sourceUrl, {
|
|
2927
|
-
nullIfNotFound: true,
|
|
2928
|
-
followLink: false,
|
|
2929
|
-
});
|
|
2930
|
-
|
|
2931
|
-
// file/directory not found
|
|
2932
|
-
if (sourceStats === null) {
|
|
2933
|
-
return;
|
|
2934
|
-
}
|
|
2935
|
-
|
|
2936
|
-
if (
|
|
2937
|
-
sourceStats.isFile() ||
|
|
2938
|
-
sourceStats.isCharacterDevice() ||
|
|
2939
|
-
sourceStats.isBlockDevice()
|
|
2940
|
-
) {
|
|
2941
|
-
visitFile(sourceUrl);
|
|
2942
|
-
} else if (sourceStats.isSymbolicLink()) {
|
|
2943
|
-
visitSymbolicLink(sourceUrl);
|
|
2944
|
-
} else if (sourceStats.isDirectory()) {
|
|
2945
|
-
visitDirectory(`${sourceUrl}/`);
|
|
2946
|
-
}
|
|
2947
|
-
};
|
|
2948
|
-
|
|
2949
|
-
const visitDirectory = (directoryUrl) => {
|
|
2950
|
-
const directoryPath = urlToFileSystemPath(directoryUrl);
|
|
2951
|
-
const optionsFromRecursive = recursive
|
|
2952
|
-
? {
|
|
2953
|
-
handleNotEmptyError: () => {
|
|
2954
|
-
removeDirectoryContent(directoryUrl);
|
|
2955
|
-
visitDirectory(directoryUrl);
|
|
2956
|
-
},
|
|
2957
|
-
}
|
|
2958
|
-
: {};
|
|
2959
|
-
removeDirectorySyncNaive(directoryPath, {
|
|
2960
|
-
...optionsFromRecursive,
|
|
2961
|
-
// Workaround for https://github.com/joyent/node/issues/4337
|
|
2962
|
-
...(process.platform === "win32"
|
|
2963
|
-
? {
|
|
2964
|
-
handlePermissionError: (error) => {
|
|
2965
|
-
console.error(
|
|
2966
|
-
`trying to fix windows EPERM after readir on ${directoryPath}`,
|
|
2967
|
-
);
|
|
2968
|
-
|
|
2969
|
-
let openOrCloseError;
|
|
2970
|
-
try {
|
|
2971
|
-
const fd = openSync(directoryPath);
|
|
2972
|
-
closeSync(fd);
|
|
2973
|
-
} catch (e) {
|
|
2974
|
-
openOrCloseError = e;
|
|
2975
|
-
}
|
|
2976
|
-
|
|
2977
|
-
if (openOrCloseError) {
|
|
2978
|
-
if (openOrCloseError.code === "ENOENT") {
|
|
2979
|
-
return;
|
|
2980
|
-
}
|
|
2981
|
-
console.error(
|
|
2982
|
-
`error while trying to fix windows EPERM after readir on ${directoryPath}: ${openOrCloseError.stack}`,
|
|
2983
|
-
);
|
|
2984
|
-
throw error;
|
|
2985
|
-
}
|
|
2986
|
-
removeDirectorySyncNaive(directoryPath, {
|
|
2987
|
-
...optionsFromRecursive,
|
|
2988
|
-
});
|
|
2989
|
-
},
|
|
2990
|
-
}
|
|
2991
|
-
: {}),
|
|
2992
|
-
});
|
|
2993
|
-
};
|
|
2994
|
-
|
|
2995
|
-
const removeDirectoryContent = (directoryUrl) => {
|
|
2996
|
-
const entryNames = readdirSync(new URL(directoryUrl));
|
|
2997
|
-
for (const entryName of entryNames) {
|
|
2998
|
-
const url = resolveUrl(entryName, directoryUrl);
|
|
2999
|
-
visit(url);
|
|
3000
|
-
}
|
|
3001
|
-
};
|
|
3002
|
-
|
|
3003
|
-
const visitFile = (fileUrl) => {
|
|
3004
|
-
removeNonDirectory(fileUrl);
|
|
3005
|
-
};
|
|
3006
|
-
|
|
3007
|
-
const visitSymbolicLink = (symbolicLinkUrl) => {
|
|
3008
|
-
removeNonDirectory(symbolicLinkUrl);
|
|
3009
|
-
};
|
|
3010
|
-
|
|
3011
|
-
if (onlyContent) {
|
|
3012
|
-
removeDirectoryContent(rootDirectoryUrl);
|
|
3013
|
-
} else {
|
|
3014
|
-
visitDirectory(rootDirectoryUrl);
|
|
3015
|
-
}
|
|
3016
|
-
};
|
|
3017
|
-
|
|
3018
|
-
const removeDirectorySyncNaive = (
|
|
3019
|
-
directoryPath,
|
|
3020
|
-
{ handleNotEmptyError = null, handlePermissionError = null } = {},
|
|
3021
|
-
) => {
|
|
3022
|
-
try {
|
|
3023
|
-
rmdirSync(directoryPath);
|
|
3024
|
-
} catch (error) {
|
|
3025
|
-
if (handlePermissionError && error.code === "EPERM") {
|
|
3026
|
-
handlePermissionError(error);
|
|
3027
|
-
return;
|
|
3028
|
-
}
|
|
3029
|
-
if (error.code === "ENOENT") {
|
|
3030
|
-
return;
|
|
3031
|
-
}
|
|
3032
|
-
if (
|
|
3033
|
-
handleNotEmptyError &&
|
|
3034
|
-
// linux os
|
|
3035
|
-
(error.code === "ENOTEMPTY" ||
|
|
3036
|
-
// SunOS
|
|
3037
|
-
error.code === "EEXIST")
|
|
3038
|
-
) {
|
|
3039
|
-
handleNotEmptyError(error);
|
|
3040
|
-
return;
|
|
3041
|
-
}
|
|
3042
|
-
throw error;
|
|
3043
|
-
}
|
|
3044
|
-
};
|
|
3045
|
-
|
|
3046
3135
|
process.platform === "win32";
|
|
3047
3136
|
|
|
3048
3137
|
const ensureEmptyDirectorySync = (source) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/test",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.32",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@jsenv/abort": "4.3.0",
|
|
40
40
|
"@jsenv/ast": "6.4.1",
|
|
41
41
|
"@jsenv/exception": "1.1.3",
|
|
42
|
-
"@jsenv/filesystem": "4.
|
|
42
|
+
"@jsenv/filesystem": "4.12.0",
|
|
43
43
|
"@jsenv/github-check-run": "0.2.5",
|
|
44
44
|
"@jsenv/node-esm-resolution": "1.0.6",
|
|
45
45
|
"@jsenv/humanize": "1.2.8",
|