fs.js 28 KB

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