@jsenv/core 39.7.6 → 39.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import os, { networkInterfaces } from "node:os";
3
3
  import tty from "node:tty";
4
4
  import stringWidth from "string-width";
5
5
  import { pathToFileURL, fileURLToPath } from "node:url";
6
- import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFileSync, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, watch, readdirSync, createReadStream, readFile, existsSync, realpathSync } from "node:fs";
6
+ import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFileSync, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, unlinkSync, readdirSync, rmdirSync, watch, createReadStream, readFile, existsSync, realpathSync } from "node:fs";
7
7
  import { extname } from "node:path";
8
8
  import crypto, { createHash } from "node:crypto";
9
9
  import cluster from "node:cluster";
@@ -3668,7 +3668,7 @@ const removeEntry = async (
3668
3668
  sourceStats.isCharacterDevice() ||
3669
3669
  sourceStats.isBlockDevice()
3670
3670
  ) {
3671
- await removeNonDirectory(
3671
+ await removeNonDirectory$1(
3672
3672
  sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl,
3673
3673
  {
3674
3674
  maxRetries,
@@ -3689,7 +3689,7 @@ const removeEntry = async (
3689
3689
  }
3690
3690
  };
3691
3691
 
3692
- const removeNonDirectory = (sourceUrl, { maxRetries, retryDelay }) => {
3692
+ const removeNonDirectory$1 = (sourceUrl, { maxRetries, retryDelay }) => {
3693
3693
  const sourcePath = urlToFileSystemPath(sourceUrl);
3694
3694
 
3695
3695
  let retryCount = 0;
@@ -3828,11 +3828,11 @@ const removeDirectory = async (
3828
3828
  };
3829
3829
 
3830
3830
  const visitFile = async (fileUrl) => {
3831
- await removeNonDirectory(fileUrl, { maxRetries, retryDelay });
3831
+ await removeNonDirectory$1(fileUrl, { maxRetries, retryDelay });
3832
3832
  };
3833
3833
 
3834
3834
  const visitSymbolicLink = async (symbolicLinkUrl) => {
3835
- await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay });
3835
+ await removeNonDirectory$1(symbolicLinkUrl, { maxRetries, retryDelay });
3836
3836
  };
3837
3837
 
3838
3838
  try {
@@ -3877,6 +3877,204 @@ const removeDirectoryNaive = (
3877
3877
 
3878
3878
  process.platform === "win32";
3879
3879
 
3880
+ const removeEntrySync = (
3881
+ source,
3882
+ {
3883
+ allowUseless = false,
3884
+ recursive = false,
3885
+ maxRetries = 3,
3886
+ retryDelay = 100,
3887
+ onlyContent = false,
3888
+ } = {},
3889
+ ) => {
3890
+ const sourceUrl = assertAndNormalizeFileUrl(source);
3891
+ const sourceStats = readEntryStatSync(sourceUrl, {
3892
+ nullIfNotFound: true,
3893
+ followLink: false,
3894
+ });
3895
+ if (!sourceStats) {
3896
+ if (allowUseless) {
3897
+ return;
3898
+ }
3899
+ throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`);
3900
+ }
3901
+
3902
+ // https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
3903
+ // FIFO and socket are ignored, not sure what they are exactly and what to do with them
3904
+ // other libraries ignore them, let's do the same.
3905
+ if (
3906
+ sourceStats.isFile() ||
3907
+ sourceStats.isSymbolicLink() ||
3908
+ sourceStats.isCharacterDevice() ||
3909
+ sourceStats.isBlockDevice()
3910
+ ) {
3911
+ removeNonDirectory(
3912
+ sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl);
3913
+ } else if (sourceStats.isDirectory()) {
3914
+ const directoryUrl = ensurePathnameTrailingSlash(sourceUrl);
3915
+ removeDirectorySync$1(directoryUrl, {
3916
+ recursive,
3917
+ maxRetries,
3918
+ retryDelay,
3919
+ onlyContent,
3920
+ });
3921
+ }
3922
+ };
3923
+
3924
+ const removeNonDirectory = (sourceUrl) => {
3925
+ const sourcePath = urlToFileSystemPath(sourceUrl);
3926
+ const attempt = () => {
3927
+ unlinkSyncNaive(sourcePath);
3928
+ };
3929
+ attempt();
3930
+ };
3931
+
3932
+ const unlinkSyncNaive = (sourcePath, { handleTemporaryError = null } = {}) => {
3933
+ try {
3934
+ unlinkSync(sourcePath);
3935
+ } catch (error) {
3936
+ if (error.code === "ENOENT") {
3937
+ return;
3938
+ }
3939
+ if (
3940
+ handleTemporaryError &&
3941
+ (error.code === "EBUSY" ||
3942
+ error.code === "EMFILE" ||
3943
+ error.code === "ENFILE" ||
3944
+ error.code === "ENOENT")
3945
+ ) {
3946
+ handleTemporaryError(error);
3947
+ return;
3948
+ }
3949
+ throw error;
3950
+ }
3951
+ };
3952
+
3953
+ const removeDirectorySync$1 = (
3954
+ rootDirectoryUrl,
3955
+ { maxRetries, retryDelay, recursive, onlyContent },
3956
+ ) => {
3957
+ const visit = (sourceUrl) => {
3958
+ const sourceStats = readEntryStatSync(sourceUrl, {
3959
+ nullIfNotFound: true,
3960
+ followLink: false,
3961
+ });
3962
+
3963
+ // file/directory not found
3964
+ if (sourceStats === null) {
3965
+ return;
3966
+ }
3967
+
3968
+ if (
3969
+ sourceStats.isFile() ||
3970
+ sourceStats.isCharacterDevice() ||
3971
+ sourceStats.isBlockDevice()
3972
+ ) {
3973
+ visitFile(sourceUrl);
3974
+ } else if (sourceStats.isSymbolicLink()) {
3975
+ visitSymbolicLink(sourceUrl);
3976
+ } else if (sourceStats.isDirectory()) {
3977
+ visitDirectory(`${sourceUrl}/`);
3978
+ }
3979
+ };
3980
+
3981
+ const visitDirectory = (directoryUrl) => {
3982
+ const directoryPath = urlToFileSystemPath(directoryUrl);
3983
+ const optionsFromRecursive = recursive
3984
+ ? {
3985
+ handleNotEmptyError: () => {
3986
+ removeDirectoryContent(directoryUrl);
3987
+ visitDirectory(directoryUrl);
3988
+ },
3989
+ }
3990
+ : {};
3991
+ removeDirectorySyncNaive(directoryPath, {
3992
+ ...optionsFromRecursive,
3993
+ // Workaround for https://github.com/joyent/node/issues/4337
3994
+ ...(process.platform === "win32"
3995
+ ? {
3996
+ handlePermissionError: (error) => {
3997
+ console.error(
3998
+ `trying to fix windows EPERM after readir on ${directoryPath}`,
3999
+ );
4000
+
4001
+ let openOrCloseError;
4002
+ try {
4003
+ const fd = openSync(directoryPath);
4004
+ closeSync(fd);
4005
+ } catch (e) {
4006
+ openOrCloseError = e;
4007
+ }
4008
+
4009
+ if (openOrCloseError) {
4010
+ if (openOrCloseError.code === "ENOENT") {
4011
+ return;
4012
+ }
4013
+ console.error(
4014
+ `error while trying to fix windows EPERM after readir on ${directoryPath}: ${openOrCloseError.stack}`,
4015
+ );
4016
+ throw error;
4017
+ }
4018
+ removeDirectorySyncNaive(directoryPath, {
4019
+ ...optionsFromRecursive,
4020
+ });
4021
+ },
4022
+ }
4023
+ : {}),
4024
+ });
4025
+ };
4026
+
4027
+ const removeDirectoryContent = (directoryUrl) => {
4028
+ const entryNames = readdirSync(new URL(directoryUrl));
4029
+ for (const entryName of entryNames) {
4030
+ const url = resolveUrl$1(entryName, directoryUrl);
4031
+ visit(url);
4032
+ }
4033
+ };
4034
+
4035
+ const visitFile = (fileUrl) => {
4036
+ removeNonDirectory(fileUrl);
4037
+ };
4038
+
4039
+ const visitSymbolicLink = (symbolicLinkUrl) => {
4040
+ removeNonDirectory(symbolicLinkUrl);
4041
+ };
4042
+
4043
+ if (onlyContent) {
4044
+ removeDirectoryContent(rootDirectoryUrl);
4045
+ } else {
4046
+ visitDirectory(rootDirectoryUrl);
4047
+ }
4048
+ };
4049
+
4050
+ const removeDirectorySyncNaive = (
4051
+ directoryPath,
4052
+ { handleNotEmptyError = null, handlePermissionError = null } = {},
4053
+ ) => {
4054
+ try {
4055
+ rmdirSync(directoryPath);
4056
+ } catch (error) {
4057
+ if (handlePermissionError && error.code === "EPERM") {
4058
+ handlePermissionError(error);
4059
+ return;
4060
+ }
4061
+ if (error.code === "ENOENT") {
4062
+ return;
4063
+ }
4064
+ if (
4065
+ handleNotEmptyError &&
4066
+ // linux os
4067
+ (error.code === "ENOTEMPTY" ||
4068
+ // SunOS
4069
+ error.code === "EEXIST")
4070
+ ) {
4071
+ handleNotEmptyError(error);
4072
+ return;
4073
+ }
4074
+ throw error;
4075
+ }
4076
+ };
4077
+
3880
4078
  process.platform === "win32";
3881
4079
 
3882
4080
  const ensureEmptyDirectory = async (source) => {
@@ -3906,6 +4104,13 @@ const ensureEmptyDirectory = async (source) => {
3906
4104
  );
3907
4105
  };
3908
4106
 
4107
+ const removeDirectorySync = (url, options = {}) => {
4108
+ return removeEntrySync(url, {
4109
+ ...options,
4110
+ recursive: true,
4111
+ });
4112
+ };
4113
+
3909
4114
  const callOnceIdlePerFile = (callback, idleMs) => {
3910
4115
  const timeoutIdMap = new Map();
3911
4116
  return (fileEvent) => {
@@ -14163,7 +14368,18 @@ const createUrlInfoTransformer = ({
14163
14368
  contentIsInlined = false;
14164
14369
  }
14165
14370
  if (!contentIsInlined) {
14166
- writeFileSync(new URL(generatedUrl), urlInfo.content);
14371
+ try {
14372
+ writeFileSync(new URL(generatedUrl), urlInfo.content);
14373
+ } catch (e) {
14374
+ if (e.code === "EISDIR") {
14375
+ // happens when directory existed but got delete
14376
+ // we can safely remove that directory and write the new file
14377
+ removeDirectorySync(new URL(generatedUrl));
14378
+ writeFileSync(new URL(generatedUrl), urlInfo.content);
14379
+ } else {
14380
+ throw e;
14381
+ }
14382
+ }
14167
14383
  }
14168
14384
  const { sourcemapGeneratedUrl, sourcemapReference } = urlInfo;
14169
14385
  if (sourcemapGeneratedUrl && sourcemapReference) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "39.7.6",
3
+ "version": "39.7.7",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,4 +1,4 @@
1
- import { writeFileSync } from "@jsenv/filesystem";
1
+ import { removeDirectorySync, writeFileSync } from "@jsenv/filesystem";
2
2
  import {
3
3
  composeTwoSourcemaps,
4
4
  generateSourcemapDataUrl,
@@ -275,7 +275,18 @@ export const createUrlInfoTransformer = ({
275
275
  contentIsInlined = false;
276
276
  }
277
277
  if (!contentIsInlined) {
278
- writeFileSync(new URL(generatedUrl), urlInfo.content);
278
+ try {
279
+ writeFileSync(new URL(generatedUrl), urlInfo.content);
280
+ } catch (e) {
281
+ if (e.code === "EISDIR") {
282
+ // happens when directory existed but got delete
283
+ // we can safely remove that directory and write the new file
284
+ removeDirectorySync(new URL(generatedUrl));
285
+ writeFileSync(new URL(generatedUrl), urlInfo.content);
286
+ } else {
287
+ throw e;
288
+ }
289
+ }
279
290
  }
280
291
  const { sourcemapGeneratedUrl, sourcemapReference } = urlInfo;
281
292
  if (sourcemapGeneratedUrl && sourcemapReference) {