FsPromises.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FsPromises = void 0;
  4. const util_1 = require("./util");
  5. const fs_node_utils_1 = require("@jsonjoy.com/fs-node-utils");
  6. const newAbortError = (signal) => {
  7. const error = new Error('The operation was aborted');
  8. error.name = 'AbortError';
  9. error.code = 'ABORT_ERR';
  10. error.cause = signal.reason;
  11. return error;
  12. };
  13. // AsyncIterator implementation for promises.watch
  14. class FSWatchAsyncIterator {
  15. constructor(fs, path, options = {}) {
  16. this.fs = fs;
  17. this.path = path;
  18. this.options = options;
  19. this.eventQueue = [];
  20. this.resolveQueue = [];
  21. this.finished = false;
  22. this.maxQueue = options.maxQueue || 2048;
  23. const overflow = options.overflow || 'ignore';
  24. if (overflow !== 'ignore' && overflow !== 'error' && overflow !== 'throw')
  25. throw new TypeError(`The argument 'options.overflow' must be one of: 'ignore', 'error'. Received '${overflow}'`);
  26. // Node's docs name the value 'throw' while its implementation validates
  27. // 'error', both are accepted here with the same semantics.
  28. this.overflow = overflow === 'throw' ? 'error' : overflow;
  29. const signal = options.signal;
  30. if (signal) {
  31. if (signal.aborted) {
  32. this.finish(newAbortError(signal));
  33. return;
  34. }
  35. this.onAbort = () => this.finish(newAbortError(signal));
  36. signal.addEventListener('abort', this.onAbort);
  37. }
  38. this.startWatching();
  39. }
  40. startWatching() {
  41. const { signal, ...watchOptions } = this.options;
  42. try {
  43. this.watcher = this.fs.watch(this.path, watchOptions, (eventType, filename) => {
  44. this.enqueueEvent({ eventType, filename });
  45. });
  46. }
  47. catch (error) {
  48. // If we can't start watching, finish immediately
  49. this.finish();
  50. throw error;
  51. }
  52. }
  53. enqueueEvent(event) {
  54. if (this.finished)
  55. return;
  56. // Handle queue overflow: the incoming event is dropped, the queue keeps
  57. // the oldest `maxQueue` events, matching Node.js.
  58. if (this.eventQueue.length >= this.maxQueue) {
  59. if (this.overflow === 'error') {
  60. const error = new Error(`Watch queue overflow: more than ${this.maxQueue} events queued`);
  61. error.code = 'ERR_FS_WATCH_QUEUE_OVERFLOW';
  62. this.finish(error);
  63. }
  64. else if (typeof process !== 'undefined' && typeof process.emitWarning === 'function') {
  65. process.emitWarning('fs.watch maxQueue exceeded');
  66. }
  67. return;
  68. }
  69. this.eventQueue.push(event);
  70. // If there's a waiting promise, resolve it
  71. if (this.resolveQueue.length > 0) {
  72. const { resolve } = this.resolveQueue.shift();
  73. const nextEvent = this.eventQueue.shift();
  74. resolve({ value: nextEvent, done: false });
  75. }
  76. }
  77. finish(error) {
  78. if (this.finished)
  79. return;
  80. this.finished = true;
  81. this.error = error;
  82. if (error)
  83. this.eventQueue.length = 0;
  84. if (this.onAbort) {
  85. this.options.signal.removeEventListener('abort', this.onAbort);
  86. this.onAbort = undefined;
  87. }
  88. if (this.watcher) {
  89. this.watcher.close();
  90. this.watcher = null;
  91. }
  92. // Resolve or reject all pending promises - a rejected error is considered
  93. // delivered and is not thrown again from a later next() call.
  94. const delivered = error && this.resolveQueue.length > 0;
  95. while (this.resolveQueue.length > 0) {
  96. const { resolve, reject } = this.resolveQueue.shift();
  97. if (error) {
  98. reject(error);
  99. }
  100. else {
  101. resolve({ value: undefined, done: true });
  102. }
  103. }
  104. if (delivered)
  105. this.error = undefined;
  106. }
  107. async next() {
  108. if (this.error) {
  109. const error = this.error;
  110. this.error = undefined;
  111. throw error;
  112. }
  113. if (this.finished) {
  114. return { value: undefined, done: true };
  115. }
  116. // If we have queued events, return one
  117. if (this.eventQueue.length > 0) {
  118. const event = this.eventQueue.shift();
  119. return { value: event, done: false };
  120. }
  121. // Otherwise, wait for the next event
  122. return new Promise((resolve, reject) => {
  123. this.resolveQueue.push({ resolve, reject });
  124. });
  125. }
  126. async return() {
  127. this.finish();
  128. return { value: undefined, done: true };
  129. }
  130. async throw(error) {
  131. this.finish(error);
  132. throw error;
  133. }
  134. [Symbol.asyncIterator]() {
  135. return this;
  136. }
  137. }
  138. class FsPromises {
  139. constructor(fs, FileHandle) {
  140. this.fs = fs;
  141. this.FileHandle = FileHandle;
  142. this.constants = fs_node_utils_1.constants;
  143. this.cp = (0, util_1.promisify)(this.fs, 'cp');
  144. this.opendir = (0, util_1.promisify)(this.fs, 'opendir');
  145. this.statfs = (0, util_1.promisify)(this.fs, 'statfs');
  146. this.lutimes = (0, util_1.promisify)(this.fs, 'lutimes');
  147. this.glob = (0, util_1.promisify)(this.fs, 'glob');
  148. this.access = (0, util_1.promisify)(this.fs, 'access');
  149. this.chmod = (0, util_1.promisify)(this.fs, 'chmod');
  150. this.chown = (0, util_1.promisify)(this.fs, 'chown');
  151. this.copyFile = (0, util_1.promisify)(this.fs, 'copyFile');
  152. this.lchmod = (0, util_1.promisify)(this.fs, 'lchmod');
  153. this.lchown = (0, util_1.promisify)(this.fs, 'lchown');
  154. this.link = (0, util_1.promisify)(this.fs, 'link');
  155. this.lstat = (0, util_1.promisify)(this.fs, 'lstat');
  156. this.mkdir = (0, util_1.promisify)(this.fs, 'mkdir');
  157. this.mkdtemp = (0, util_1.promisify)(this.fs, 'mkdtemp');
  158. this.readdir = (0, util_1.promisify)(this.fs, 'readdir');
  159. this.readlink = (0, util_1.promisify)(this.fs, 'readlink');
  160. this.realpath = (0, util_1.promisify)(this.fs, 'realpath');
  161. this.rename = (0, util_1.promisify)(this.fs, 'rename');
  162. this.rmdir = (0, util_1.promisify)(this.fs, 'rmdir');
  163. this.rm = (0, util_1.promisify)(this.fs, 'rm');
  164. this.stat = (0, util_1.promisify)(this.fs, 'stat');
  165. this.symlink = (0, util_1.promisify)(this.fs, 'symlink');
  166. this.truncate = (0, util_1.promisify)(this.fs, 'truncate');
  167. this.unlink = (0, util_1.promisify)(this.fs, 'unlink');
  168. this.utimes = (0, util_1.promisify)(this.fs, 'utimes');
  169. this.readFile = (id, options) => {
  170. return (0, util_1.promisify)(this.fs, 'readFile')(id instanceof this.FileHandle ? id.fd : id, options);
  171. };
  172. this.appendFile = (path, data, options) => {
  173. return (0, util_1.promisify)(this.fs, 'appendFile')(path instanceof this.FileHandle ? path.fd : path, data, options);
  174. };
  175. this.open = (path, flags = 'r', mode) => {
  176. return (0, util_1.promisify)(this.fs, 'open', fd => new this.FileHandle(this.fs, fd))(path, flags, mode);
  177. };
  178. this.writeFile = (id, data, options) => {
  179. const dataPromise = (0, util_1.isReadableStream)(data) ? (0, util_1.streamToBuffer)(data) : Promise.resolve(data);
  180. return dataPromise.then(data => (0, util_1.promisify)(this.fs, 'writeFile')(id instanceof this.FileHandle ? id.fd : id, data, options));
  181. };
  182. this.watch = (filename, options) => {
  183. const watchOptions = typeof options === 'string' ? { encoding: options } : options || {};
  184. return new FSWatchAsyncIterator(this.fs, filename, watchOptions);
  185. };
  186. }
  187. }
  188. exports.FsPromises = FsPromises;
  189. //# sourceMappingURL=FsPromises.js.map