fs.js 24 KB

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