index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import EE from 'events';
  2. import fs from 'fs';
  3. import { Minipass } from 'minipass';
  4. const writev = fs.writev;
  5. const _autoClose = Symbol('_autoClose');
  6. const _close = Symbol('_close');
  7. const _ended = Symbol('_ended');
  8. const _fd = Symbol('_fd');
  9. const _finished = Symbol('_finished');
  10. const _flags = Symbol('_flags');
  11. const _flush = Symbol('_flush');
  12. const _handleChunk = Symbol('_handleChunk');
  13. const _makeBuf = Symbol('_makeBuf');
  14. const _mode = Symbol('_mode');
  15. const _needDrain = Symbol('_needDrain');
  16. const _onerror = Symbol('_onerror');
  17. const _onopen = Symbol('_onopen');
  18. const _onread = Symbol('_onread');
  19. const _onwrite = Symbol('_onwrite');
  20. const _open = Symbol('_open');
  21. const _path = Symbol('_path');
  22. const _pos = Symbol('_pos');
  23. const _queue = Symbol('_queue');
  24. const _read = Symbol('_read');
  25. const _readSize = Symbol('_readSize');
  26. const _reading = Symbol('_reading');
  27. const _remain = Symbol('_remain');
  28. const _size = Symbol('_size');
  29. const _write = Symbol('_write');
  30. const _writing = Symbol('_writing');
  31. const _defaultFlag = Symbol('_defaultFlag');
  32. const _errored = Symbol('_errored');
  33. export class ReadStream extends Minipass {
  34. [_errored] = false;
  35. [_fd];
  36. [_path];
  37. [_readSize];
  38. [_reading] = false;
  39. [_size];
  40. [_remain];
  41. [_autoClose];
  42. constructor(path, opt) {
  43. opt = opt || {};
  44. super(opt);
  45. this.readable = true;
  46. this.writable = false;
  47. if (typeof path !== 'string') {
  48. throw new TypeError('path must be a string');
  49. }
  50. this[_errored] = false;
  51. this[_fd] = typeof opt.fd === 'number' ? opt.fd : undefined;
  52. this[_path] = path;
  53. this[_readSize] = opt.readSize || 16 * 1024 * 1024;
  54. this[_reading] = false;
  55. this[_size] = typeof opt.size === 'number' ? opt.size : Infinity;
  56. this[_remain] = this[_size];
  57. this[_autoClose] =
  58. typeof opt.autoClose === 'boolean' ? opt.autoClose : true;
  59. if (typeof this[_fd] === 'number') {
  60. this[_read]();
  61. }
  62. else {
  63. this[_open]();
  64. }
  65. }
  66. get fd() {
  67. return this[_fd];
  68. }
  69. get path() {
  70. return this[_path];
  71. }
  72. //@ts-ignore
  73. write() {
  74. throw new TypeError('this is a readable stream');
  75. }
  76. //@ts-ignore
  77. end() {
  78. throw new TypeError('this is a readable stream');
  79. }
  80. [_open]() {
  81. fs.open(this[_path], 'r', (er, fd) => this[_onopen](er, fd));
  82. }
  83. [_onopen](er, fd) {
  84. if (er) {
  85. this[_onerror](er);
  86. }
  87. else {
  88. this[_fd] = fd;
  89. this.emit('open', fd);
  90. this[_read]();
  91. }
  92. }
  93. [_makeBuf]() {
  94. return Buffer.allocUnsafe(Math.min(this[_readSize], this[_remain]));
  95. }
  96. [_read]() {
  97. if (!this[_reading]) {
  98. this[_reading] = true;
  99. const buf = this[_makeBuf]();
  100. /* c8 ignore start */
  101. if (buf.length === 0) {
  102. return process.nextTick(() => this[_onread](null, 0, buf));
  103. }
  104. /* c8 ignore stop */
  105. fs.read(this[_fd], buf, 0, buf.length, null, (er, br, b) => this[_onread](er, br, b));
  106. }
  107. }
  108. [_onread](er, br, buf) {
  109. this[_reading] = false;
  110. if (er) {
  111. this[_onerror](er);
  112. }
  113. else if (this[_handleChunk](br, buf)) {
  114. this[_read]();
  115. }
  116. }
  117. [_close]() {
  118. if (this[_autoClose] && typeof this[_fd] === 'number') {
  119. const fd = this[_fd];
  120. this[_fd] = undefined;
  121. fs.close(fd, er => er ? this.emit('error', er) : this.emit('close'));
  122. }
  123. }
  124. [_onerror](er) {
  125. this[_reading] = true;
  126. this[_close]();
  127. this.emit('error', er);
  128. }
  129. [_handleChunk](br, buf) {
  130. let ret = false;
  131. // no effect if infinite
  132. this[_remain] -= br;
  133. if (br > 0) {
  134. ret = super.write(br < buf.length ? buf.subarray(0, br) : buf);
  135. }
  136. if (br === 0 || this[_remain] <= 0) {
  137. ret = false;
  138. this[_close]();
  139. super.end();
  140. }
  141. return ret;
  142. }
  143. emit(ev, ...args) {
  144. switch (ev) {
  145. case 'prefinish':
  146. case 'finish':
  147. return false;
  148. case 'drain':
  149. if (typeof this[_fd] === 'number') {
  150. this[_read]();
  151. }
  152. return false;
  153. case 'error':
  154. if (this[_errored]) {
  155. return false;
  156. }
  157. this[_errored] = true;
  158. return super.emit(ev, ...args);
  159. default:
  160. return super.emit(ev, ...args);
  161. }
  162. }
  163. }
  164. export class ReadStreamSync extends ReadStream {
  165. [_open]() {
  166. let threw = true;
  167. try {
  168. this[_onopen](null, fs.openSync(this[_path], 'r'));
  169. threw = false;
  170. }
  171. finally {
  172. if (threw) {
  173. this[_close]();
  174. }
  175. }
  176. }
  177. [_read]() {
  178. let threw = true;
  179. try {
  180. if (!this[_reading]) {
  181. this[_reading] = true;
  182. do {
  183. const buf = this[_makeBuf]();
  184. /* c8 ignore start */
  185. const br = buf.length === 0
  186. ? 0
  187. : fs.readSync(this[_fd], buf, 0, buf.length, null);
  188. /* c8 ignore stop */
  189. if (!this[_handleChunk](br, buf)) {
  190. break;
  191. }
  192. } while (true);
  193. this[_reading] = false;
  194. }
  195. threw = false;
  196. }
  197. finally {
  198. if (threw) {
  199. this[_close]();
  200. }
  201. }
  202. }
  203. [_close]() {
  204. if (this[_autoClose] && typeof this[_fd] === 'number') {
  205. const fd = this[_fd];
  206. this[_fd] = undefined;
  207. fs.closeSync(fd);
  208. this.emit('close');
  209. }
  210. }
  211. }
  212. export class WriteStream extends EE {
  213. readable = false;
  214. writable = true;
  215. [_errored] = false;
  216. [_writing] = false;
  217. [_ended] = false;
  218. [_queue] = [];
  219. [_needDrain] = false;
  220. [_path];
  221. [_mode];
  222. [_autoClose];
  223. [_fd];
  224. [_defaultFlag];
  225. [_flags];
  226. [_finished] = false;
  227. [_pos];
  228. constructor(path, opt) {
  229. opt = opt || {};
  230. super(opt);
  231. this[_path] = path;
  232. this[_fd] = typeof opt.fd === 'number' ? opt.fd : undefined;
  233. this[_mode] = opt.mode === undefined ? 0o666 : opt.mode;
  234. this[_pos] = typeof opt.start === 'number' ? opt.start : undefined;
  235. this[_autoClose] =
  236. typeof opt.autoClose === 'boolean' ? opt.autoClose : true;
  237. // truncating makes no sense when writing into the middle
  238. const defaultFlag = this[_pos] !== undefined ? 'r+' : 'w';
  239. this[_defaultFlag] = opt.flags === undefined;
  240. this[_flags] = opt.flags === undefined ? defaultFlag : opt.flags;
  241. if (this[_fd] === undefined) {
  242. this[_open]();
  243. }
  244. }
  245. emit(ev, ...args) {
  246. if (ev === 'error') {
  247. if (this[_errored]) {
  248. return false;
  249. }
  250. this[_errored] = true;
  251. }
  252. return super.emit(ev, ...args);
  253. }
  254. get fd() {
  255. return this[_fd];
  256. }
  257. get path() {
  258. return this[_path];
  259. }
  260. [_onerror](er) {
  261. this[_close]();
  262. this[_writing] = true;
  263. this.emit('error', er);
  264. }
  265. [_open]() {
  266. fs.open(this[_path], this[_flags], this[_mode], (er, fd) => this[_onopen](er, fd));
  267. }
  268. [_onopen](er, fd) {
  269. if (this[_defaultFlag] &&
  270. this[_flags] === 'r+' &&
  271. er &&
  272. er.code === 'ENOENT') {
  273. this[_flags] = 'w';
  274. this[_open]();
  275. }
  276. else if (er) {
  277. this[_onerror](er);
  278. }
  279. else {
  280. this[_fd] = fd;
  281. this.emit('open', fd);
  282. if (!this[_writing]) {
  283. this[_flush]();
  284. }
  285. }
  286. }
  287. end(buf, enc) {
  288. if (buf) {
  289. //@ts-ignore
  290. this.write(buf, enc);
  291. }
  292. this[_ended] = true;
  293. // synthetic after-write logic, where drain/finish live
  294. if (!this[_writing] &&
  295. !this[_queue].length &&
  296. typeof this[_fd] === 'number') {
  297. this[_onwrite](null, 0);
  298. }
  299. return this;
  300. }
  301. write(buf, enc) {
  302. if (typeof buf === 'string') {
  303. buf = Buffer.from(buf, enc);
  304. }
  305. if (this[_ended]) {
  306. this.emit('error', new Error('write() after end()'));
  307. return false;
  308. }
  309. if (this[_fd] === undefined || this[_writing] || this[_queue].length) {
  310. this[_queue].push(buf);
  311. this[_needDrain] = true;
  312. return false;
  313. }
  314. this[_writing] = true;
  315. this[_write](buf);
  316. return true;
  317. }
  318. [_write](buf) {
  319. fs.write(this[_fd], buf, 0, buf.length, this[_pos], (er, bw) => this[_onwrite](er, bw));
  320. }
  321. [_onwrite](er, bw) {
  322. if (er) {
  323. this[_onerror](er);
  324. }
  325. else {
  326. if (this[_pos] !== undefined && typeof bw === 'number') {
  327. this[_pos] += bw;
  328. }
  329. if (this[_queue].length) {
  330. this[_flush]();
  331. }
  332. else {
  333. this[_writing] = false;
  334. if (this[_ended] && !this[_finished]) {
  335. this[_finished] = true;
  336. this[_close]();
  337. this.emit('finish');
  338. }
  339. else if (this[_needDrain]) {
  340. this[_needDrain] = false;
  341. this.emit('drain');
  342. }
  343. }
  344. }
  345. }
  346. [_flush]() {
  347. if (this[_queue].length === 0) {
  348. if (this[_ended]) {
  349. this[_onwrite](null, 0);
  350. }
  351. }
  352. else if (this[_queue].length === 1) {
  353. this[_write](this[_queue].pop());
  354. }
  355. else {
  356. const iovec = this[_queue];
  357. this[_queue] = [];
  358. writev(this[_fd], iovec, this[_pos], (er, bw) => this[_onwrite](er, bw));
  359. }
  360. }
  361. [_close]() {
  362. if (this[_autoClose] && typeof this[_fd] === 'number') {
  363. const fd = this[_fd];
  364. this[_fd] = undefined;
  365. fs.close(fd, er => er ? this.emit('error', er) : this.emit('close'));
  366. }
  367. }
  368. }
  369. export class WriteStreamSync extends WriteStream {
  370. [_open]() {
  371. let fd;
  372. // only wrap in a try{} block if we know we'll retry, to avoid
  373. // the rethrow obscuring the error's source frame in most cases.
  374. if (this[_defaultFlag] && this[_flags] === 'r+') {
  375. try {
  376. fd = fs.openSync(this[_path], this[_flags], this[_mode]);
  377. }
  378. catch (er) {
  379. if (er?.code === 'ENOENT') {
  380. this[_flags] = 'w';
  381. return this[_open]();
  382. }
  383. else {
  384. throw er;
  385. }
  386. }
  387. }
  388. else {
  389. fd = fs.openSync(this[_path], this[_flags], this[_mode]);
  390. }
  391. this[_onopen](null, fd);
  392. }
  393. [_close]() {
  394. if (this[_autoClose] && typeof this[_fd] === 'number') {
  395. const fd = this[_fd];
  396. this[_fd] = undefined;
  397. fs.closeSync(fd);
  398. this.emit('close');
  399. }
  400. }
  401. [_write](buf) {
  402. // throw the original, but try to close if it fails
  403. let threw = true;
  404. try {
  405. this[_onwrite](null, fs.writeSync(this[_fd], buf, 0, buf.length, this[_pos]));
  406. threw = false;
  407. }
  408. finally {
  409. if (threw) {
  410. try {
  411. this[_close]();
  412. }
  413. catch {
  414. // ok error
  415. }
  416. }
  417. }
  418. }
  419. }
  420. //# sourceMappingURL=index.js.map