fs.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const path = require("path");
  7. /** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
  8. /** @typedef {import("../FileSystemInfo").FileSystemInfoEntry} FileSystemInfoEntry */
  9. /**
  10. * @template T
  11. * @typedef {object} IStatsBase
  12. * @property {() => boolean} isFile
  13. * @property {() => boolean} isDirectory
  14. * @property {() => boolean} isBlockDevice
  15. * @property {() => boolean} isCharacterDevice
  16. * @property {() => boolean} isSymbolicLink
  17. * @property {() => boolean} isFIFO
  18. * @property {() => boolean} isSocket
  19. * @property {T} dev
  20. * @property {T} ino
  21. * @property {T} mode
  22. * @property {T} nlink
  23. * @property {T} uid
  24. * @property {T} gid
  25. * @property {T} rdev
  26. * @property {T} size
  27. * @property {T} blksize
  28. * @property {T} blocks
  29. * @property {T} atimeMs
  30. * @property {T} mtimeMs
  31. * @property {T} ctimeMs
  32. * @property {T} birthtimeMs
  33. * @property {Date} atime
  34. * @property {Date} mtime
  35. * @property {Date} ctime
  36. * @property {Date} birthtime
  37. */
  38. /**
  39. * @typedef {IStatsBase<number>} IStats
  40. */
  41. /**
  42. * @typedef {IStatsBase<bigint> & { atimeNs: bigint, mtimeNs: bigint, ctimeNs: bigint, birthtimeNs: bigint }} IBigIntStats
  43. */
  44. /**
  45. * @typedef {object} Dirent
  46. * @property {() => boolean} isFile
  47. * @property {() => boolean} isDirectory
  48. * @property {() => boolean} isBlockDevice
  49. * @property {() => boolean} isCharacterDevice
  50. * @property {() => boolean} isSymbolicLink
  51. * @property {() => boolean} isFIFO
  52. * @property {() => boolean} isSocket
  53. * @property {string} name
  54. * @property {string} path
  55. */
  56. /** @typedef {string | number | boolean | null} JsonPrimitive */
  57. /** @typedef {JsonValue[]} JsonArray */
  58. /** @typedef {JsonPrimitive | JsonObject | JsonArray} JsonValue */
  59. /** @typedef {{[Key in string]: JsonValue} & {[Key in string]?: JsonValue | undefined}} JsonObject */
  60. /** @typedef {function(NodeJS.ErrnoException | null): void} NoParamCallback */
  61. /** @typedef {function(NodeJS.ErrnoException | null, string=): void} StringCallback */
  62. /** @typedef {function(NodeJS.ErrnoException | null, Buffer=): void} BufferCallback */
  63. /** @typedef {function(NodeJS.ErrnoException | null, (string | Buffer)=): void} StringOrBufferCallback */
  64. /** @typedef {function(NodeJS.ErrnoException | null, (string[])=): void} ReaddirStringCallback */
  65. /** @typedef {function(NodeJS.ErrnoException | null, (Buffer[])=): void} ReaddirBufferCallback */
  66. /** @typedef {function(NodeJS.ErrnoException | null, (string[] | Buffer[])=): void} ReaddirStringOrBufferCallback */
  67. /** @typedef {function(NodeJS.ErrnoException | null, (Dirent[])=): void} ReaddirDirentCallback */
  68. /** @typedef {function(NodeJS.ErrnoException | null, IStats=): void} StatsCallback */
  69. /** @typedef {function(NodeJS.ErrnoException | null, IBigIntStats=): void} BigIntStatsCallback */
  70. /** @typedef {function(NodeJS.ErrnoException | null, (IStats | IBigIntStats)=): void} StatsOrBigIntStatsCallback */
  71. /** @typedef {function(NodeJS.ErrnoException | null, number=): void} NumberCallback */
  72. /** @typedef {function(NodeJS.ErrnoException | Error | null, JsonObject=): void} ReadJsonCallback */
  73. /** @typedef {Map<string, FileSystemInfoEntry | "ignore">} TimeInfoEntries */
  74. /**
  75. * @typedef {object} WatcherInfo
  76. * @property {Set<string> | null} changes get current aggregated changes that have not yet send to callback
  77. * @property {Set<string> | null} removals get current aggregated removals that have not yet send to callback
  78. * @property {TimeInfoEntries} fileTimeInfoEntries get info about files
  79. * @property {TimeInfoEntries} contextTimeInfoEntries get info about directories
  80. */
  81. /** @typedef {Set<string>} Changes */
  82. /** @typedef {Set<string>} Removals */
  83. // TODO webpack 6 deprecate missing getInfo
  84. /**
  85. * @typedef {object} Watcher
  86. * @property {function(): void} close closes the watcher and all underlying file watchers
  87. * @property {function(): void} pause closes the watcher, but keeps underlying file watchers alive until the next watch call
  88. * @property {(function(): Changes | null)=} getAggregatedChanges get current aggregated changes that have not yet send to callback
  89. * @property {(function(): Removals | null)=} getAggregatedRemovals get current aggregated removals that have not yet send to callback
  90. * @property {function(): TimeInfoEntries} getFileTimeInfoEntries get info about files
  91. * @property {function(): TimeInfoEntries} getContextTimeInfoEntries get info about directories
  92. * @property {function(): WatcherInfo=} getInfo get info about timestamps and changes
  93. */
  94. /**
  95. * @callback WatchMethod
  96. * @param {Iterable<string>} files watched files
  97. * @param {Iterable<string>} directories watched directories
  98. * @param {Iterable<string>} missing watched existence entries
  99. * @param {number} startTime timestamp of start time
  100. * @param {WatchOptions} options options object
  101. * @param {function(Error | null, TimeInfoEntries=, TimeInfoEntries=, Changes=, Removals=): void} callback aggregated callback
  102. * @param {function(string, number): void} callbackUndelayed callback when the first change was detected
  103. * @returns {Watcher} a watcher
  104. */
  105. // TODO webpack 6 make optional methods required and avoid using non standard methods like `join`, `relative`, `dirname`, move IntermediateFileSystemExtras methods to InputFilesystem or OutputFilesystem
  106. /**
  107. * @typedef {string | Buffer | URL} PathLike
  108. */
  109. /**
  110. * @typedef {PathLike | number} PathOrFileDescriptor
  111. */
  112. /**
  113. * @typedef {object} ObjectEncodingOptions
  114. * @property {BufferEncoding | null | undefined} [encoding]
  115. */
  116. /**
  117. * @typedef {{
  118. * (path: PathOrFileDescriptor, options: ({ encoding?: null | undefined, flag?: string | undefined } & import("events").Abortable) | undefined | null, callback: BufferCallback): void;
  119. * (path: PathOrFileDescriptor, options: ({ encoding: BufferEncoding, flag?: string | undefined } & import("events").Abortable) | BufferEncoding, callback: StringCallback): void;
  120. * (path: PathOrFileDescriptor, options: (ObjectEncodingOptions & { flag?: string | undefined } & import("events").Abortable) | BufferEncoding | undefined | null, callback: StringOrBufferCallback): void;
  121. * (path: PathOrFileDescriptor, callback: BufferCallback): void;
  122. * }} ReadFile
  123. */
  124. /**
  125. * @typedef {{
  126. * (path: PathOrFileDescriptor, options?: { encoding?: null | undefined, flag?: string | undefined } | null): Buffer;
  127. * (path: PathOrFileDescriptor, options: { encoding: BufferEncoding, flag?: string | undefined } | BufferEncoding): string;
  128. * (path: PathOrFileDescriptor, options?: (ObjectEncodingOptions & { flag?: string | undefined }) | BufferEncoding | null): string | Buffer;
  129. * }} ReadFileSync
  130. */
  131. /**
  132. * @typedef {ObjectEncodingOptions | BufferEncoding | undefined | null} EncodingOption
  133. */
  134. /**
  135. * @typedef {'buffer'| { encoding: 'buffer' }} BufferEncodingOption
  136. */
  137. /**
  138. * @typedef {object} StatOptions
  139. * @property {(boolean | undefined)=} bigint
  140. */
  141. /**
  142. * @typedef {object} StatSyncOptions
  143. * @property {(boolean | undefined)=} bigint
  144. * @property {(boolean | undefined)=} throwIfNoEntry
  145. */
  146. /**
  147. * @typedef {{
  148. * (path: PathLike, options: EncodingOption, callback: StringCallback): void;
  149. * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void;
  150. * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void;
  151. * (path: PathLike, callback: StringCallback): void;
  152. * }} Readlink
  153. */
  154. /**
  155. * @typedef {{
  156. * (path: PathLike, options?: EncodingOption): string;
  157. * (path: PathLike, options: BufferEncodingOption): Buffer;
  158. * (path: PathLike, options?: EncodingOption): string | Buffer;
  159. * }} ReadlinkSync
  160. */
  161. /**
  162. * @typedef {{
  163. * (path: PathLike, options: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | undefined | null, callback: ReaddirStringCallback): void;
  164. * (path: PathLike, options: { encoding: 'buffer', withFileTypes?: false | undefined, recursive?: boolean | undefined } | 'buffer', callback: ReaddirBufferCallback): void;
  165. * (path: PathLike, callback: ReaddirStringCallback): void;
  166. * (path: PathLike, options: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | undefined | null, callback: ReaddirStringOrBufferCallback): void;
  167. * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }, callback: ReaddirDirentCallback): void;
  168. * }} Readdir
  169. */
  170. /**
  171. * @typedef {{
  172. * (path: PathLike, options?: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | null): string[];
  173. * (path: PathLike, options: { encoding: 'buffer', withFileTypes?: false | undefined, recursive?: boolean | undefined } | 'buffer'): Buffer[];
  174. * (path: PathLike, options?: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | null): string[] | Buffer[];
  175. * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }): Dirent[];
  176. * }} ReaddirSync
  177. */
  178. /**
  179. * @typedef {{
  180. * (path: PathLike, callback: StatsCallback): void;
  181. * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void;
  182. * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void;
  183. * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void;
  184. * }} Stat
  185. */
  186. /**
  187. * @typedef {{
  188. * (path: PathLike, options?: undefined): IStats;
  189. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined;
  190. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined;
  191. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined }): IStats;
  192. * (path: PathLike, options: StatSyncOptions & { bigint: true }): IBigIntStats;
  193. * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: false | undefined }): IStats | IBigIntStats;
  194. * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined;
  195. * }} StatSync
  196. */
  197. /**
  198. * @typedef {{
  199. * (path: PathLike, callback: StatsCallback): void;
  200. * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void;
  201. * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void;
  202. * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void;
  203. * }} LStat
  204. */
  205. /**
  206. * @typedef {{
  207. * (path: PathLike, options?: undefined): IStats;
  208. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined;
  209. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined;
  210. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined }): IStats;
  211. * (path: PathLike, options: StatSyncOptions & { bigint: true }): IBigIntStats;
  212. * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: false | undefined }): IStats | IBigIntStats;
  213. * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined;
  214. * }} LStatSync
  215. */
  216. /**
  217. * @typedef {{
  218. * (path: PathLike, options: EncodingOption, callback: StringCallback): void;
  219. * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void;
  220. * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void;
  221. * (path: PathLike, callback: StringCallback): void;
  222. * }} RealPath
  223. */
  224. /**
  225. * @typedef {{
  226. * (path: PathLike, options?: EncodingOption): string;
  227. * (path: PathLike, options: BufferEncodingOption): Buffer;
  228. * (path: PathLike, options?: EncodingOption): string | Buffer;
  229. * }} RealPathSync
  230. */
  231. /**
  232. * @typedef {function(PathOrFileDescriptor, ReadJsonCallback): void} ReadJson
  233. */
  234. /**
  235. * @typedef {function(PathOrFileDescriptor): JsonObject} ReadJsonSync
  236. */
  237. /**
  238. * @typedef {function((string | string[] | Set<string>)=): void} Purge
  239. */
  240. /**
  241. * @typedef {object} InputFileSystem
  242. * @property {ReadFile} readFile
  243. * @property {ReadFileSync=} readFileSync
  244. * @property {Readlink} readlink
  245. * @property {ReadlinkSync=} readlinkSync
  246. * @property {Readdir} readdir
  247. * @property {ReaddirSync=} readdirSync
  248. * @property {Stat} stat
  249. * @property {StatSync=} statSync
  250. * @property {LStat=} lstat
  251. * @property {LStatSync=} lstatSync
  252. * @property {RealPath=} realpath
  253. * @property {RealPathSync=} realpathSync
  254. * @property {ReadJson=} readJson
  255. * @property {ReadJsonSync=} readJsonSync
  256. * @property {Purge=} purge
  257. * @property {(function(string, string): string)=} join
  258. * @property {(function(string, string): string)=} relative
  259. * @property {(function(string): string)=} dirname
  260. */
  261. /**
  262. * @typedef {number | string} Mode
  263. */
  264. /**
  265. * @typedef {(ObjectEncodingOptions & import("events").Abortable & { mode?: Mode | undefined, flag?: string | undefined, flush?: boolean | undefined }) | BufferEncoding | null} WriteFileOptions
  266. */
  267. /**
  268. * @typedef {{
  269. * (file: PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, options: WriteFileOptions, callback: NoParamCallback): void;
  270. * (file: PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, callback: NoParamCallback): void;
  271. * }} WriteFile
  272. */
  273. /**
  274. * @typedef {{ recursive?: boolean | undefined, mode?: Mode | undefined }} MakeDirectoryOptions
  275. */
  276. /**
  277. * @typedef {{
  278. * (file: PathLike, options: MakeDirectoryOptions & { recursive: true }, callback: StringCallback): void;
  279. * (file: PathLike, options: Mode | (MakeDirectoryOptions & { recursive?: false | undefined; }) | null | undefined, callback: NoParamCallback): void;
  280. * (file: PathLike, options: Mode | MakeDirectoryOptions | null | undefined, callback: StringCallback): void;
  281. * (file: PathLike, callback: NoParamCallback): void;
  282. * }} Mkdir
  283. */
  284. /**
  285. * @typedef {{ maxRetries?: number | undefined, recursive?: boolean | undefined, retryDelay?: number | undefined }} RmDirOptions
  286. */
  287. /**
  288. * @typedef {{
  289. * (file: PathLike, callback: NoParamCallback): void;
  290. * (file: PathLike, options: RmDirOptions, callback: NoParamCallback): void;
  291. * }} Rmdir
  292. */
  293. /**
  294. * @typedef {function(PathLike, NoParamCallback): void} Unlink
  295. */
  296. /**
  297. * @typedef {object} OutputFileSystem
  298. * @property {WriteFile} writeFile
  299. * @property {Mkdir} mkdir
  300. * @property {Readdir=} readdir
  301. * @property {Rmdir=} rmdir
  302. * @property {Unlink=} unlink
  303. * @property {Stat} stat
  304. * @property {LStat=} lstat
  305. * @property {ReadFile} readFile
  306. * @property {(function(string, string): string)=} join
  307. * @property {(function(string, string): string)=} relative
  308. * @property {(function(string): string)=} dirname
  309. */
  310. /**
  311. * @typedef {object} WatchFileSystem
  312. * @property {WatchMethod} watch
  313. */
  314. /**
  315. * @typedef {{
  316. * (path: PathLike, options: MakeDirectoryOptions & { recursive: true }): string | undefined;
  317. * (path: PathLike, options?: Mode | (MakeDirectoryOptions & { recursive?: false | undefined }) | null): void;
  318. * (path: PathLike, options?: Mode | MakeDirectoryOptions | null): string | undefined;
  319. * }} MkdirSync
  320. */
  321. /**
  322. * @typedef {object} StreamOptions
  323. * @property {(string | undefined)=} flags
  324. * @property {(BufferEncoding | undefined)} encoding
  325. * @property {(number | EXPECTED_ANY | undefined)=} fd
  326. * @property {(number | undefined)=} mode
  327. * @property {(boolean | undefined)=} autoClose
  328. * @property {(boolean | undefined)=} emitClose
  329. * @property {(number | undefined)=} start
  330. * @property {(AbortSignal | null | undefined)=} signal
  331. */
  332. /**
  333. * @typedef {object} FSImplementation
  334. * @property {((...args: EXPECTED_ANY[]) => EXPECTED_ANY)=} open
  335. * @property {((...args: EXPECTED_ANY[]) => EXPECTED_ANY)=} close
  336. */
  337. /**
  338. * @typedef {FSImplementation & { write: (...args: EXPECTED_ANY[]) => EXPECTED_ANY; close?: (...args: EXPECTED_ANY[]) => EXPECTED_ANY }} CreateWriteStreamFSImplementation
  339. */
  340. /**
  341. * @typedef {StreamOptions & { fs?: CreateWriteStreamFSImplementation | null | undefined }} WriteStreamOptions
  342. */
  343. /**
  344. * @typedef {function(PathLike, (BufferEncoding | WriteStreamOptions)=): NodeJS.WritableStream} CreateWriteStream
  345. */
  346. /**
  347. * @typedef {number | string} OpenMode
  348. */
  349. /**
  350. * @typedef {{
  351. * (file: PathLike, flags: OpenMode | undefined, mode: Mode | undefined | null, callback: NumberCallback): void;
  352. * (file: PathLike, flags: OpenMode | undefined, callback: NumberCallback): void;
  353. * (file: PathLike, callback: NumberCallback): void;
  354. * }} Open
  355. */
  356. /**
  357. * @typedef {number | bigint} ReadPosition
  358. */
  359. /**
  360. * @typedef {object} ReadSyncOptions
  361. * @property {(number | undefined)=} offset
  362. * @property {(number | undefined)=} length
  363. * @property {(ReadPosition | null | undefined)=} position
  364. */
  365. /**
  366. * @template {NodeJS.ArrayBufferView} TBuffer
  367. * @typedef {object} ReadAsyncOptions
  368. * @property {(number | undefined)=} offset
  369. * @property {(number | undefined)=} length
  370. * @property {(ReadPosition | null | undefined)=} position
  371. * @property {TBuffer=} buffer
  372. */
  373. /**
  374. * @template {NodeJS.ArrayBufferView} [TBuffer=Buffer]
  375. * @typedef {{
  376. * (fd: number, buffer: TBuffer, offset: number, length: number, position: ReadPosition | null, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: TBuffer) => void): void;
  377. * (fd: number, options: ReadAsyncOptions<TBuffer>, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: TBuffer) => void): void;
  378. * (fd: number, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: NodeJS.ArrayBufferView) => void): void;
  379. * }} Read
  380. */
  381. /** @typedef {function(number, NoParamCallback): void} Close */
  382. /** @typedef {function(PathLike, PathLike, NoParamCallback): void} Rename */
  383. /**
  384. * @typedef {object} IntermediateFileSystemExtras
  385. * @property {MkdirSync} mkdirSync
  386. * @property {CreateWriteStream} createWriteStream
  387. * @property {Open} open
  388. * @property {Read} read
  389. * @property {Close} close
  390. * @property {Rename} rename
  391. */
  392. /** @typedef {InputFileSystem & OutputFileSystem & IntermediateFileSystemExtras} IntermediateFileSystem */
  393. /**
  394. * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
  395. * @param {string} rootPath the root path
  396. * @param {string} targetPath the target path
  397. * @returns {string} location of targetPath relative to rootPath
  398. */
  399. const relative = (fs, rootPath, targetPath) => {
  400. if (fs && fs.relative) {
  401. return fs.relative(rootPath, targetPath);
  402. } else if (path.posix.isAbsolute(rootPath)) {
  403. return path.posix.relative(rootPath, targetPath);
  404. } else if (path.win32.isAbsolute(rootPath)) {
  405. return path.win32.relative(rootPath, targetPath);
  406. }
  407. throw new Error(
  408. `${rootPath} is neither a posix nor a windows path, and there is no 'relative' method defined in the file system`
  409. );
  410. };
  411. module.exports.relative = relative;
  412. /**
  413. * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
  414. * @param {string} rootPath a path
  415. * @param {string} filename a filename
  416. * @returns {string} the joined path
  417. */
  418. const join = (fs, rootPath, filename) => {
  419. if (fs && fs.join) {
  420. return fs.join(rootPath, filename);
  421. } else if (path.posix.isAbsolute(rootPath)) {
  422. return path.posix.join(rootPath, filename);
  423. } else if (path.win32.isAbsolute(rootPath)) {
  424. return path.win32.join(rootPath, filename);
  425. }
  426. throw new Error(
  427. `${rootPath} is neither a posix nor a windows path, and there is no 'join' method defined in the file system`
  428. );
  429. };
  430. module.exports.join = join;
  431. /**
  432. * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
  433. * @param {string} absPath an absolute path
  434. * @returns {string} the parent directory of the absolute path
  435. */
  436. const dirname = (fs, absPath) => {
  437. if (fs && fs.dirname) {
  438. return fs.dirname(absPath);
  439. } else if (path.posix.isAbsolute(absPath)) {
  440. return path.posix.dirname(absPath);
  441. } else if (path.win32.isAbsolute(absPath)) {
  442. return path.win32.dirname(absPath);
  443. }
  444. throw new Error(
  445. `${absPath} is neither a posix nor a windows path, and there is no 'dirname' method defined in the file system`
  446. );
  447. };
  448. module.exports.dirname = dirname;
  449. /**
  450. * @param {OutputFileSystem} fs a file system
  451. * @param {string} p an absolute path
  452. * @param {function(Error=): void} callback callback function for the error
  453. * @returns {void}
  454. */
  455. const mkdirp = (fs, p, callback) => {
  456. fs.mkdir(p, err => {
  457. if (err) {
  458. if (err.code === "ENOENT") {
  459. const dir = dirname(fs, p);
  460. if (dir === p) {
  461. callback(err);
  462. return;
  463. }
  464. mkdirp(fs, dir, err => {
  465. if (err) {
  466. callback(err);
  467. return;
  468. }
  469. fs.mkdir(p, err => {
  470. if (err) {
  471. if (err.code === "EEXIST") {
  472. callback();
  473. return;
  474. }
  475. callback(err);
  476. return;
  477. }
  478. callback();
  479. });
  480. });
  481. return;
  482. } else if (err.code === "EEXIST") {
  483. callback();
  484. return;
  485. }
  486. callback(err);
  487. return;
  488. }
  489. callback();
  490. });
  491. };
  492. module.exports.mkdirp = mkdirp;
  493. /**
  494. * @param {IntermediateFileSystem} fs a file system
  495. * @param {string} p an absolute path
  496. * @returns {void}
  497. */
  498. const mkdirpSync = (fs, p) => {
  499. try {
  500. fs.mkdirSync(p);
  501. } catch (err) {
  502. if (err) {
  503. if (/** @type {NodeJS.ErrnoException} */ (err).code === "ENOENT") {
  504. const dir = dirname(fs, p);
  505. if (dir === p) {
  506. throw err;
  507. }
  508. mkdirpSync(fs, dir);
  509. fs.mkdirSync(p);
  510. return;
  511. } else if (/** @type {NodeJS.ErrnoException} */ (err).code === "EEXIST") {
  512. return;
  513. }
  514. throw err;
  515. }
  516. }
  517. };
  518. module.exports.mkdirpSync = mkdirpSync;
  519. /**
  520. * @param {InputFileSystem} fs a file system
  521. * @param {string} p an absolute path
  522. * @param {ReadJsonCallback} callback callback
  523. * @returns {void}
  524. */
  525. const readJson = (fs, p, callback) => {
  526. if ("readJson" in fs)
  527. return /** @type {NonNullable<InputFileSystem["readJson"]>} */ (
  528. fs.readJson
  529. )(p, callback);
  530. fs.readFile(p, (err, buf) => {
  531. if (err) return callback(err);
  532. let data;
  533. try {
  534. data = JSON.parse(/** @type {Buffer} */ (buf).toString("utf-8"));
  535. } catch (err1) {
  536. return callback(/** @type {Error} */ (err1));
  537. }
  538. return callback(null, data);
  539. });
  540. };
  541. module.exports.readJson = readJson;
  542. /**
  543. * @param {InputFileSystem} fs a file system
  544. * @param {string} p an absolute path
  545. * @param {function(NodeJS.ErrnoException | Error | null, (IStats | string)=): void} callback callback
  546. * @returns {void}
  547. */
  548. const lstatReadlinkAbsolute = (fs, p, callback) => {
  549. let i = 3;
  550. const doReadLink = () => {
  551. fs.readlink(p, (err, target) => {
  552. if (err && --i > 0) {
  553. // It might was just changed from symlink to file
  554. // we retry 2 times to catch this case before throwing the error
  555. return doStat();
  556. }
  557. if (err) return callback(err);
  558. const value = /** @type {string} */ (target).toString();
  559. callback(null, join(fs, dirname(fs, p), value));
  560. });
  561. };
  562. const doStat = () => {
  563. if ("lstat" in fs) {
  564. return /** @type {NonNullable<InputFileSystem["lstat"]>} */ (fs.lstat)(
  565. p,
  566. (err, stats) => {
  567. if (err) return callback(err);
  568. if (/** @type {IStats} */ (stats).isSymbolicLink()) {
  569. return doReadLink();
  570. }
  571. callback(null, stats);
  572. }
  573. );
  574. }
  575. return fs.stat(p, callback);
  576. };
  577. if ("lstat" in fs) return doStat();
  578. doReadLink();
  579. };
  580. module.exports.lstatReadlinkAbsolute = lstatReadlinkAbsolute;