fs.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  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. const { ABSOLUTE_PATH_REGEXP } = require("./identifier");
  8. /** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
  9. /**
  10. * @import {
  11. * Entry,
  12. * OnlySafeTimeEntry,
  13. * ExistenceOnlyTimeEntry
  14. * } from "watchpack"
  15. */
  16. /**
  17. * Defines the i stats base type used by this module.
  18. * @template T
  19. * @typedef {object} IStatsBase
  20. * @property {() => boolean} isFile
  21. * @property {() => boolean} isDirectory
  22. * @property {() => boolean} isBlockDevice
  23. * @property {() => boolean} isCharacterDevice
  24. * @property {() => boolean} isSymbolicLink
  25. * @property {() => boolean} isFIFO
  26. * @property {() => boolean} isSocket
  27. * @property {T} dev
  28. * @property {T} ino
  29. * @property {T} mode
  30. * @property {T} nlink
  31. * @property {T} uid
  32. * @property {T} gid
  33. * @property {T} rdev
  34. * @property {T} size
  35. * @property {T} blksize
  36. * @property {T} blocks
  37. * @property {T} atimeMs
  38. * @property {T} mtimeMs
  39. * @property {T} ctimeMs
  40. * @property {T} birthtimeMs
  41. * @property {Date} atime
  42. * @property {Date} mtime
  43. * @property {Date} ctime
  44. * @property {Date} birthtime
  45. */
  46. /**
  47. * Defines the i stats type used by this module.
  48. * @typedef {IStatsBase<number>} IStats
  49. */
  50. /**
  51. * Defines the i big int stats type used by this module.
  52. * @typedef {IStatsBase<bigint> & { atimeNs: bigint, mtimeNs: bigint, ctimeNs: bigint, birthtimeNs: bigint }} IBigIntStats
  53. */
  54. /**
  55. * Defines the dirent type used by this module.
  56. * @template {string | Buffer} [T=string]
  57. * @typedef {object} Dirent
  58. * @property {() => boolean} isFile true when is file, otherwise false
  59. * @property {() => boolean} isDirectory true when is directory, otherwise false
  60. * @property {() => boolean} isBlockDevice true when is block device, otherwise false
  61. * @property {() => boolean} isCharacterDevice true when is character device, otherwise false
  62. * @property {() => boolean} isSymbolicLink true when is symbolic link, otherwise false
  63. * @property {() => boolean} isFIFO true when is FIFO, otherwise false
  64. * @property {() => boolean} isSocket true when is socket, otherwise false
  65. * @property {T} name name
  66. * @property {string} parentPath path
  67. * @property {string=} path path
  68. */
  69. /** @typedef {string | number | boolean | null} JsonPrimitive */
  70. /** @typedef {JsonValue[]} JsonArray */
  71. /** @typedef {{ [Key in string]?: JsonValue }} JsonObject */
  72. /** @typedef {JsonPrimitive | JsonObject | JsonArray} JsonValue */
  73. /** @typedef {(err: NodeJS.ErrnoException | null) => void} NoParamCallback */
  74. /** @typedef {(err: NodeJS.ErrnoException | null, result?: string) => void} StringCallback */
  75. /** @typedef {(err: NodeJS.ErrnoException | null, result?: Buffer) => void} BufferCallback */
  76. /** @typedef {(err: NodeJS.ErrnoException | null, result?: string | Buffer) => void} StringOrBufferCallback */
  77. /** @typedef {(err: NodeJS.ErrnoException | null, result?: string[]) => void} ReaddirStringCallback */
  78. /** @typedef {(err: NodeJS.ErrnoException | null, result?: Buffer[]) => void} ReaddirBufferCallback */
  79. /** @typedef {(err: NodeJS.ErrnoException | null, result?: string[] | Buffer[]) => void} ReaddirStringOrBufferCallback */
  80. /** @typedef {(err: NodeJS.ErrnoException | null, result?: Dirent[]) => void} ReaddirDirentCallback */
  81. /** @typedef {(err: NodeJS.ErrnoException | null, result?: Dirent<Buffer>[]) => void} ReaddirDirentBufferCallback */
  82. /** @typedef {(err: NodeJS.ErrnoException | null, result?: IStats) => void} StatsCallback */
  83. /** @typedef {(err: NodeJS.ErrnoException | null, result?: IBigIntStats) => void} BigIntStatsCallback */
  84. /** @typedef {(err: NodeJS.ErrnoException | null, result?: IStats | IBigIntStats) => void} StatsOrBigIntStatsCallback */
  85. /** @typedef {(err: NodeJS.ErrnoException | null, result?: number) => void} NumberCallback */
  86. /** @typedef {(err: NodeJS.ErrnoException | Error | null, result?: JsonObject) => void} ReadJsonCallback */
  87. /** @typedef {Map<string, Entry | OnlySafeTimeEntry | ExistenceOnlyTimeEntry | null | "ignore">} TimeInfoEntries */
  88. /** @typedef {Set<string>} Changes */
  89. /** @typedef {Set<string>} Removals */
  90. /**
  91. * Defines the watcher info type used by this module.
  92. * @typedef {object} WatcherInfo
  93. * @property {Changes | null} changes get current aggregated changes that have not yet send to callback
  94. * @property {Removals | null} removals get current aggregated removals that have not yet send to callback
  95. * @property {TimeInfoEntries} fileTimeInfoEntries get info about files
  96. * @property {TimeInfoEntries} contextTimeInfoEntries get info about directories
  97. */
  98. // TODO webpack 6 deprecate missing getInfo
  99. /**
  100. * Defines the watcher type used by this module.
  101. * @typedef {object} Watcher
  102. * @property {() => void} close closes the watcher and all underlying file watchers
  103. * @property {() => void} pause closes the watcher, but keeps underlying file watchers alive until the next watch call
  104. * @property {(() => Changes | null)=} getAggregatedChanges get current aggregated changes that have not yet send to callback
  105. * @property {(() => Removals | null)=} getAggregatedRemovals get current aggregated removals that have not yet send to callback
  106. * @property {() => TimeInfoEntries} getFileTimeInfoEntries get info about files
  107. * @property {() => TimeInfoEntries} getContextTimeInfoEntries get info about directories
  108. * @property {() => WatcherInfo=} getInfo get info about timestamps and changes
  109. */
  110. /**
  111. * Defines the watch method callback.
  112. * @callback WatchMethod
  113. * @param {Iterable<string>} files watched files
  114. * @param {Iterable<string>} directories watched directories
  115. * @param {Iterable<string>} missing watched existence entries
  116. * @param {number} startTime timestamp of start time
  117. * @param {WatchOptions} options options object
  118. * @param {(err: Error | null, timeInfoEntries1?: TimeInfoEntries, timeInfoEntries2?: TimeInfoEntries, changes?: Changes, removals?: Removals) => void} callback aggregated callback
  119. * @param {(value: string, num: number) => void} callbackUndelayed callback when the first change was detected
  120. * @returns {Watcher} a watcher
  121. */
  122. // TODO webpack 6 make optional methods required and avoid using non standard methods like `join`, `relative`, `dirname`, move IntermediateFileSystemExtras methods to InputFilesystem or OutputFilesystem
  123. /**
  124. * Defines the path like type used by this module.
  125. * @typedef {string | Buffer | URL} PathLike
  126. */
  127. /**
  128. * Defines the path or file descriptor type used by this module.
  129. * @typedef {PathLike | number} PathOrFileDescriptor
  130. */
  131. /**
  132. * Defines the object encoding options type used by this module.
  133. * @typedef {object} ObjectEncodingOptions
  134. * @property {BufferEncoding | null | undefined=} encoding
  135. */
  136. /**
  137. * Describes the read file shape.
  138. * @typedef {{
  139. * (path: PathOrFileDescriptor, options: ({ encoding?: null | undefined, flag?: string | undefined } & import("events").Abortable) | undefined | null, callback: BufferCallback): void,
  140. * (path: PathOrFileDescriptor, options: ({ encoding: BufferEncoding, flag?: string | undefined } & import("events").Abortable) | BufferEncoding, callback: StringCallback): void,
  141. * (path: PathOrFileDescriptor, options: (ObjectEncodingOptions & { flag?: string | undefined } & import("events").Abortable) | BufferEncoding | undefined | null, callback: StringOrBufferCallback): void,
  142. * (path: PathOrFileDescriptor, callback: BufferCallback): void,
  143. * }} ReadFile
  144. */
  145. /**
  146. * Describes the read file sync shape.
  147. * @typedef {{
  148. * (path: PathOrFileDescriptor, options?: { encoding?: null | undefined, flag?: string | undefined } | null): Buffer,
  149. * (path: PathOrFileDescriptor, options: { encoding: BufferEncoding, flag?: string | undefined } | BufferEncoding): string,
  150. * (path: PathOrFileDescriptor, options?: (ObjectEncodingOptions & { flag?: string | undefined }) | BufferEncoding | null): string | Buffer,
  151. * }} ReadFileSync
  152. */
  153. /**
  154. * Defines the encoding option type used by this module.
  155. * @typedef {ObjectEncodingOptions | BufferEncoding | undefined | null} EncodingOption
  156. */
  157. /**
  158. * Defines the buffer encoding option type used by this module.
  159. * @typedef {"buffer" | { encoding: "buffer" }} BufferEncodingOption
  160. */
  161. /**
  162. * Defines the stat options type used by this module.
  163. * @typedef {object} StatOptions
  164. * @property {(boolean | undefined)=} bigint
  165. */
  166. /**
  167. * Defines the stat sync options type used by this module.
  168. * @typedef {object} StatSyncOptions
  169. * @property {(boolean | undefined)=} bigint
  170. * @property {(boolean | undefined)=} throwIfNoEntry
  171. */
  172. /**
  173. * Describes the readlink shape.
  174. * @typedef {{
  175. * (path: PathLike, options: EncodingOption, callback: StringCallback): void,
  176. * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void,
  177. * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void,
  178. * (path: PathLike, callback: StringCallback): void,
  179. * }} Readlink
  180. */
  181. /**
  182. * Describes the readlink sync shape.
  183. * @typedef {{
  184. * (path: PathLike, options?: EncodingOption): string,
  185. * (path: PathLike, options: BufferEncodingOption): Buffer,
  186. * (path: PathLike, options?: EncodingOption): string | Buffer,
  187. * }} ReadlinkSync
  188. */
  189. /**
  190. * Describes the readdir shape.
  191. * @typedef {{
  192. * (path: PathLike, options: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException | null, files?: string[]) => void): void,
  193. * (path: PathLike, options: { encoding: "buffer", withFileTypes?: false | undefined, recursive?: boolean | undefined } | "buffer", callback: (err: NodeJS.ErrnoException | null, files?: Buffer[]) => void): void,
  194. * (path: PathLike, options: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException | null, files?: string[] | Buffer[]) => void): void,
  195. * (path: PathLike, callback: (err: NodeJS.ErrnoException | null, files?: string[]) => void): void,
  196. * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }, callback: (err: NodeJS.ErrnoException | null, files?: Dirent<string>[]) => void): void,
  197. * (path: PathLike, options: { encoding: "buffer", withFileTypes: true, recursive?: boolean | undefined }, callback: (err: NodeJS.ErrnoException | null, files: Dirent<Buffer>[]) => void): void,
  198. * }} Readdir
  199. */
  200. /**
  201. * Describes the readdir sync shape.
  202. * @typedef {{
  203. * (path: PathLike, options?: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | null): string[],
  204. * (path: PathLike, options: { encoding: "buffer", withFileTypes?: false | undefined, recursive?: boolean | undefined } | "buffer"): Buffer[],
  205. * (path: PathLike, options?: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | null): string[] | Buffer[],
  206. * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }): Dirent[],
  207. * (path: PathLike, options: { encoding: "buffer", withFileTypes: true, recursive?: boolean | undefined }): Dirent<Buffer>[],
  208. * }} ReaddirSync
  209. */
  210. /**
  211. * Describes the stat shape.
  212. * @typedef {{
  213. * (path: PathLike, callback: StatsCallback): void,
  214. * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void,
  215. * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void,
  216. * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void,
  217. * }} Stat
  218. */
  219. /**
  220. * Describes the stat sync shape.
  221. * @typedef {{
  222. * (path: PathLike): IStats,
  223. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry?: true | undefined }): IStats,
  224. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry?: true | undefined }): IBigIntStats,
  225. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined,
  226. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined,
  227. * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: true | undefined }): IStats | IBigIntStats,
  228. * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined,
  229. * }} StatSync
  230. */
  231. /**
  232. * Describes the l stat shape.
  233. * @typedef {{
  234. * (path: PathLike, callback: StatsCallback): void,
  235. * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void,
  236. * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void,
  237. * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void,
  238. * }} LStat
  239. */
  240. /**
  241. * Describes the l stat sync shape.
  242. * @typedef {{
  243. * (path: PathLike): IStats,
  244. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry?: true | undefined }): IStats,
  245. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry?: true | undefined }): IBigIntStats,
  246. * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined,
  247. * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined,
  248. * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: true | undefined }): IStats | IBigIntStats,
  249. * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined,
  250. * }} LStatSync
  251. */
  252. /**
  253. * Describes the real path shape.
  254. * @typedef {{
  255. * (path: PathLike, options: EncodingOption, callback: StringCallback): void,
  256. * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void,
  257. * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void,
  258. * (path: PathLike, callback: StringCallback): void,
  259. * }} RealPath
  260. */
  261. /**
  262. * Describes the real path sync shape.
  263. * @typedef {{
  264. * (path: PathLike, options?: EncodingOption): string,
  265. * (path: PathLike, options: BufferEncodingOption): Buffer,
  266. * (path: PathLike, options?: EncodingOption): string | Buffer,
  267. * }} RealPathSync
  268. */
  269. /**
  270. * Defines the read json type used by this module.
  271. * @typedef {(pathOrFileDescriptor: PathOrFileDescriptor, callback: ReadJsonCallback) => void} ReadJson
  272. */
  273. /**
  274. * Defines the read json sync type used by this module.
  275. * @typedef {(pathOrFileDescriptor: PathOrFileDescriptor) => JsonObject} ReadJsonSync
  276. */
  277. /**
  278. * Defines the purge type used by this module.
  279. *
  280. * `options.exact` (supported by enhanced-resolve's `CachedInputFileSystem`
  281. * from v5.22.0): when true, only entries whose key exactly matches `value`
  282. * are invalidated; cached entries for descendants are preserved. Default
  283. * is false (legacy prefix-match behavior).
  284. * @typedef {(value?: string | string[] | Set<string>, options?: { exact?: boolean }) => void} Purge
  285. */
  286. /**
  287. * Defines the input file system type used by this module.
  288. * @typedef {object} InputFileSystem
  289. * @property {ReadFile} readFile
  290. * @property {ReadFileSync=} readFileSync
  291. * @property {Readlink} readlink
  292. * @property {ReadlinkSync=} readlinkSync
  293. * @property {Readdir} readdir
  294. * @property {ReaddirSync=} readdirSync
  295. * @property {Stat} stat
  296. * @property {StatSync=} statSync
  297. * @property {LStat=} lstat
  298. * @property {LStatSync=} lstatSync
  299. * @property {RealPath=} realpath
  300. * @property {RealPathSync=} realpathSync
  301. * @property {ReadJson=} readJson
  302. * @property {ReadJsonSync=} readJsonSync
  303. * @property {Purge=} purge
  304. * @property {((path1: string, path2: string) => string)=} join
  305. * @property {((from: string, to: string) => string)=} relative
  306. * @property {((dirname: string) => string)=} dirname
  307. */
  308. /**
  309. * Defines the mode type used by this module.
  310. * @typedef {number | string} Mode
  311. */
  312. /**
  313. * Defines the write file options type used by this module.
  314. * @typedef {(ObjectEncodingOptions & import("events").Abortable & { mode?: Mode | undefined, flag?: string | undefined, flush?: boolean | undefined }) | BufferEncoding | null} WriteFileOptions
  315. */
  316. /**
  317. * Describes the write file shape.
  318. * @typedef {{
  319. * (file: PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, options: WriteFileOptions, callback: NoParamCallback): void,
  320. * (file: PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, callback: NoParamCallback): void,
  321. * }} WriteFile
  322. */
  323. /**
  324. * Defines the make directory options type used by this module.
  325. * @typedef {{ recursive?: boolean | undefined, mode?: Mode | undefined }} MakeDirectoryOptions
  326. */
  327. /**
  328. * Describes the mkdir shape.
  329. * @typedef {{
  330. * (file: PathLike, options: MakeDirectoryOptions & { recursive: true }, callback: StringCallback): void,
  331. * (file: PathLike, options: Mode | (MakeDirectoryOptions & { recursive?: false | undefined }) | null | undefined, callback: NoParamCallback): void,
  332. * (file: PathLike, options: Mode | MakeDirectoryOptions | null | undefined, callback: StringCallback): void,
  333. * (file: PathLike, callback: NoParamCallback): void,
  334. * }} Mkdir
  335. */
  336. /**
  337. * Defines the rmdir type used by this module.
  338. * @typedef {{ (file: PathLike, callback: NoParamCallback): void }} Rmdir
  339. */
  340. /**
  341. * Defines the unlink type used by this module.
  342. * @typedef {(pathLike: PathLike, callback: NoParamCallback) => void} Unlink
  343. */
  344. /**
  345. * Defines the create read stream fs implementation type used by this module.
  346. * @typedef {FSImplementation & { read: (...args: EXPECTED_ANY[]) => EXPECTED_ANY }} CreateReadStreamFSImplementation
  347. */
  348. /**
  349. * Defines the read stream options type used by this module.
  350. * @typedef {StreamOptions & { fs?: CreateReadStreamFSImplementation | null | undefined, end?: number | undefined }} ReadStreamOptions
  351. */
  352. /**
  353. * Defines the create read stream type used by this module.
  354. * @typedef {(path: PathLike, options?: BufferEncoding | ReadStreamOptions) => NodeJS.ReadableStream} CreateReadStream
  355. */
  356. /**
  357. * Defines the output file system type used by this module.
  358. * @typedef {object} OutputFileSystem
  359. * @property {Mkdir} mkdir
  360. * @property {Readdir=} readdir
  361. * @property {Rmdir=} rmdir
  362. * @property {WriteFile} writeFile
  363. * @property {Unlink=} unlink
  364. * @property {Stat} stat
  365. * @property {LStat=} lstat
  366. * @property {ReadFile} readFile
  367. * @property {CreateReadStream=} createReadStream
  368. * @property {((path1: string, path2: string) => string)=} join
  369. * @property {((from: string, to: string) => string)=} relative
  370. * @property {((dirname: string) => string)=} dirname
  371. */
  372. /**
  373. * Defines the watch file system type used by this module.
  374. * @typedef {object} WatchFileSystem
  375. * @property {WatchMethod} watch
  376. */
  377. /**
  378. * Describes the mkdir sync shape.
  379. * @typedef {{
  380. * (path: PathLike, options: MakeDirectoryOptions & { recursive: true }): string | undefined,
  381. * (path: PathLike, options?: Mode | (MakeDirectoryOptions & { recursive?: false | undefined }) | null): void,
  382. * (path: PathLike, options?: Mode | MakeDirectoryOptions | null): string | undefined,
  383. * }} MkdirSync
  384. */
  385. /**
  386. * Defines the stream options type used by this module.
  387. * @typedef {object} StreamOptions
  388. * @property {(string | undefined)=} flags
  389. * @property {(BufferEncoding | undefined)} encoding
  390. * @property {(number | EXPECTED_ANY | undefined)=} fd
  391. * @property {(number | undefined)=} mode
  392. * @property {(boolean | undefined)=} autoClose
  393. * @property {(boolean | undefined)=} emitClose
  394. * @property {(number | undefined)=} start
  395. * @property {(AbortSignal | null | undefined)=} signal
  396. */
  397. /**
  398. * Defines the fs implementation type used by this module.
  399. * @typedef {object} FSImplementation
  400. * @property {((...args: EXPECTED_ANY[]) => EXPECTED_ANY)=} open
  401. * @property {((...args: EXPECTED_ANY[]) => EXPECTED_ANY)=} close
  402. */
  403. /**
  404. * Defines the create write stream fs implementation type used by this module.
  405. * @typedef {FSImplementation & { write: (...args: EXPECTED_ANY[]) => EXPECTED_ANY, close?: (...args: EXPECTED_ANY[]) => EXPECTED_ANY }} CreateWriteStreamFSImplementation
  406. */
  407. /**
  408. * Defines the write stream options type used by this module.
  409. * @typedef {StreamOptions & { fs?: CreateWriteStreamFSImplementation | null | undefined, flush?: boolean | undefined }} WriteStreamOptions
  410. */
  411. /**
  412. * Defines the create write stream type used by this module.
  413. * @typedef {(pathLike: PathLike, result?: BufferEncoding | WriteStreamOptions) => NodeJS.WritableStream} CreateWriteStream
  414. */
  415. /**
  416. * Defines the open mode type used by this module.
  417. * @typedef {number | string} OpenMode
  418. */
  419. /**
  420. * Describes the open shape.
  421. * @typedef {{
  422. * (file: PathLike, flags: OpenMode | undefined, mode: Mode | undefined | null, callback: NumberCallback): void,
  423. * (file: PathLike, flags: OpenMode | undefined, callback: NumberCallback): void,
  424. * (file: PathLike, callback: NumberCallback): void,
  425. * }} Open
  426. */
  427. /**
  428. * Defines the read position type used by this module.
  429. * @typedef {number | bigint} ReadPosition
  430. */
  431. /**
  432. * Defines the read sync options type used by this module.
  433. * @typedef {object} ReadSyncOptions
  434. * @property {(number | undefined)=} offset
  435. * @property {(number | undefined)=} length
  436. * @property {(ReadPosition | null | undefined)=} position
  437. */
  438. /**
  439. * Defines the read async options type used by this module.
  440. * @template {NodeJS.ArrayBufferView} TBuffer
  441. * @typedef {object} ReadAsyncOptions
  442. * @property {(number | undefined)=} offset
  443. * @property {(number | undefined)=} length
  444. * @property {(ReadPosition | null | undefined)=} position
  445. * @property {TBuffer=} buffer
  446. */
  447. /**
  448. * Defines the shared type used by this module.
  449. * @template {NodeJS.ArrayBufferView} [TBuffer=NodeJS.ArrayBufferView]
  450. * @typedef {{
  451. * (fd: number, buffer: TBuffer, offset: number, length: number, position: ReadPosition | null, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: TBuffer) => void): void,
  452. * (fd: number, options: ReadAsyncOptions<TBuffer>, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: TBuffer) => void): void,
  453. * (fd: number, callback: (err: NodeJS.ErrnoException | null, bytesRead: number, buffer: NodeJS.ArrayBufferView) => void): void,
  454. * }} Read
  455. */
  456. /** @typedef {(df: number, callback: NoParamCallback) => void} Close */
  457. /** @typedef {(a: PathLike, b: PathLike, callback: NoParamCallback) => void} Rename */
  458. /**
  459. * Defines the intermediate file system extras type used by this module.
  460. * @typedef {object} IntermediateFileSystemExtras
  461. * @property {MkdirSync} mkdirSync
  462. * @property {CreateWriteStream} createWriteStream
  463. * @property {Open} open
  464. * @property {Read} read
  465. * @property {Close} close
  466. * @property {Rename} rename
  467. */
  468. /** @typedef {InputFileSystem & OutputFileSystem & IntermediateFileSystemExtras} IntermediateFileSystem */
  469. /**
  470. * Returns location of targetPath relative to rootPath.
  471. * @param {InputFileSystem | OutputFileSystem | undefined} fs a file system
  472. * @param {string} rootPath the root path
  473. * @param {string} targetPath the target path
  474. * @returns {string} location of targetPath relative to rootPath
  475. */
  476. const relative = (fs, rootPath, targetPath) => {
  477. if (fs && fs.relative) {
  478. return fs.relative(rootPath, targetPath);
  479. } else if (path.posix.isAbsolute(rootPath)) {
  480. return path.posix.relative(rootPath, targetPath);
  481. } else if (path.win32.isAbsolute(rootPath)) {
  482. return path.win32.relative(rootPath, targetPath);
  483. }
  484. throw new Error(
  485. `${rootPath} is neither a posix nor a windows path, and there is no 'relative' method defined in the file system`
  486. );
  487. };
  488. /**
  489. * Returns the joined path.
  490. * @param {InputFileSystem | OutputFileSystem | undefined} fs a file system
  491. * @param {string} rootPath a path
  492. * @param {string} filename a filename
  493. * @returns {string} the joined path
  494. */
  495. const join = (fs, rootPath, filename) => {
  496. if (fs && fs.join) {
  497. return fs.join(rootPath, filename);
  498. } else if (path.posix.isAbsolute(rootPath)) {
  499. return path.posix.join(rootPath, filename);
  500. } else if (path.win32.isAbsolute(rootPath)) {
  501. return path.win32.join(rootPath, filename);
  502. }
  503. throw new Error(
  504. `${rootPath} is neither a posix nor a windows path, and there is no 'join' method defined in the file system`
  505. );
  506. };
  507. /**
  508. * Returns the parent directory of the absolute path.
  509. * @param {InputFileSystem | OutputFileSystem | undefined} fs a file system
  510. * @param {string} absPath an absolute path
  511. * @returns {string} the parent directory of the absolute path
  512. */
  513. const dirname = (fs, absPath) => {
  514. if (fs && fs.dirname) {
  515. return fs.dirname(absPath);
  516. } else if (path.posix.isAbsolute(absPath)) {
  517. return path.posix.dirname(absPath);
  518. } else if (path.win32.isAbsolute(absPath)) {
  519. return path.win32.dirname(absPath);
  520. }
  521. throw new Error(
  522. `${absPath} is neither a posix nor a windows path, and there is no 'dirname' method defined in the file system`
  523. );
  524. };
  525. /**
  526. * Processes the provided f.
  527. * @param {OutputFileSystem} fs a file system
  528. * @param {string} p an absolute path
  529. * @param {(err?: Error) => void} callback callback function for the error
  530. * @returns {void}
  531. */
  532. const mkdirp = (fs, p, callback) => {
  533. fs.mkdir(p, (err) => {
  534. if (err) {
  535. if (err.code === "ENOENT") {
  536. const dir = dirname(fs, p);
  537. if (dir === p) {
  538. callback(err);
  539. return;
  540. }
  541. mkdirp(fs, dir, (err) => {
  542. if (err) {
  543. callback(err);
  544. return;
  545. }
  546. fs.mkdir(p, (err) => {
  547. if (err) {
  548. // EEXIST: parent already created it; EISDIR: memfs/BSD for an
  549. // existing dir such as the root "/" (#10544)
  550. if (err.code === "EEXIST" || err.code === "EISDIR") {
  551. callback();
  552. return;
  553. }
  554. callback(err);
  555. return;
  556. }
  557. callback();
  558. });
  559. });
  560. return;
  561. } else if (err.code === "EEXIST" || err.code === "EISDIR") {
  562. callback();
  563. return;
  564. }
  565. callback(err);
  566. return;
  567. }
  568. callback();
  569. });
  570. };
  571. /**
  572. * Processes the provided f.
  573. * @param {IntermediateFileSystem} fs a file system
  574. * @param {string} p an absolute path
  575. * @returns {void}
  576. */
  577. const mkdirpSync = (fs, p) => {
  578. try {
  579. fs.mkdirSync(p);
  580. } catch (err) {
  581. if (err) {
  582. if (/** @type {NodeJS.ErrnoException} */ (err).code === "ENOENT") {
  583. const dir = dirname(fs, p);
  584. if (dir === p) {
  585. throw err;
  586. }
  587. mkdirpSync(fs, dir);
  588. try {
  589. fs.mkdirSync(p);
  590. } catch (retryErr) {
  591. // EEXIST: parent already created it; EISDIR: memfs/BSD for an
  592. // existing dir such as the root "/" (#10544)
  593. const code = /** @type {NodeJS.ErrnoException} */ (retryErr).code;
  594. if (code !== "EEXIST" && code !== "EISDIR") {
  595. throw retryErr;
  596. }
  597. }
  598. return;
  599. } else if (
  600. /** @type {NodeJS.ErrnoException} */ (err).code === "EEXIST" ||
  601. /** @type {NodeJS.ErrnoException} */ (err).code === "EISDIR"
  602. ) {
  603. return;
  604. }
  605. throw err;
  606. }
  607. }
  608. };
  609. /**
  610. * Processes the provided f.
  611. * @param {InputFileSystem} fs a file system
  612. * @param {string} p an absolute path
  613. * @param {ReadJsonCallback} callback callback
  614. * @returns {void}
  615. */
  616. const readJson = (fs, p, callback) => {
  617. if ("readJson" in fs) {
  618. return /** @type {NonNullable<InputFileSystem["readJson"]>} */ (
  619. fs.readJson
  620. )(p, callback);
  621. }
  622. fs.readFile(p, (err, buf) => {
  623. if (err) return callback(err);
  624. /** @type {JsonObject} */
  625. let data;
  626. try {
  627. data = JSON.parse(/** @type {Buffer} */ (buf).toString("utf8"));
  628. } catch (err1) {
  629. return callback(/** @type {Error} */ (err1));
  630. }
  631. return callback(null, data);
  632. });
  633. };
  634. /**
  635. * Lstat readlink absolute.
  636. * @param {InputFileSystem} fs a file system
  637. * @param {string} p an absolute path
  638. * @param {(err: NodeJS.ErrnoException | Error | null, stats?: IStats | string) => void} callback callback
  639. * @returns {void}
  640. */
  641. const lstatReadlinkAbsolute = (fs, p, callback) => {
  642. let i = 3;
  643. const doReadLink = () => {
  644. fs.readlink(p, (err, target) => {
  645. if (err && --i > 0) {
  646. // It might was just changed from symlink to file
  647. // we retry 2 times to catch this case before throwing the error
  648. return doStat();
  649. }
  650. if (err) return callback(err);
  651. const value = /** @type {string} */ (target).toString();
  652. // An absolute target must not be joined onto the link's directory (#21636)
  653. callback(
  654. null,
  655. isAbsolute(value) ? value : join(fs, dirname(fs, p), value)
  656. );
  657. });
  658. };
  659. const doStat = () => {
  660. if ("lstat" in fs) {
  661. return /** @type {NonNullable<InputFileSystem["lstat"]>} */ (fs.lstat)(
  662. p,
  663. (err, stats) => {
  664. if (err) return callback(err);
  665. if (/** @type {IStats} */ (stats).isSymbolicLink()) {
  666. return doReadLink();
  667. }
  668. callback(null, stats);
  669. }
  670. );
  671. }
  672. return fs.stat(p, callback);
  673. };
  674. if ("lstat" in fs) return doStat();
  675. doReadLink();
  676. };
  677. /**
  678. * Checks whether this object is absolute.
  679. * @param {string} pathname a path
  680. * @returns {boolean} is absolute
  681. */
  682. const isAbsolute = (pathname) => ABSOLUTE_PATH_REGEXP.test(pathname);
  683. module.exports.dirname = dirname;
  684. module.exports.isAbsolute = isAbsolute;
  685. module.exports.join = join;
  686. module.exports.lstatReadlinkAbsolute = lstatReadlinkAbsolute;
  687. module.exports.mkdirp = mkdirp;
  688. module.exports.mkdirpSync = mkdirpSync;
  689. module.exports.readJson = readJson;
  690. module.exports.relative = relative;