fs.js 23 KB

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