UNPKG

5.35 kBJavaScriptView Raw
1"use strict";
2
3const path = require('path');
4
5const {
6 createHash
7} = require('crypto');
8
9const errors = require('errno');
10
11const util = require('util');
12
13const {
14 ReadableStream,
15 WritableStream
16} = require('stream');
17
18const MemoryFileSystemError = require('memory-fs/lib/MemoryFileSystem');
19
20const debug = util.debuglog('astroturf:memory-fs');
21
22const returnsTrue = () => true;
23
24const returnsFalse = () => false;
25
26const md5 = input => {
27 const hash = createHash('md5');
28 hash.update(input);
29 return hash.digest('hex');
30};
31
32const read = (file, optsOrEncoding) => {
33 const encoding = (optsOrEncoding === null || optsOrEncoding === void 0 ? void 0 : optsOrEncoding.encoding) || optsOrEncoding;
34 file.atime = new Date();
35 return encoding ? file.contents.toString(encoding) : file.contents;
36};
37/**
38 * A simple in memorry ponyfill of the node fs that stores objects in memory.
39 * We don't use `memory-fs` because our use-case is much narrower (only need files not dirs)
40 * and it doesn't support stat timestamps
41 */
42
43
44let MemoryFs =
45/*#__PURE__*/
46function () {
47 function MemoryFs() {
48 this.addFile = (p, data, updateMtime = false) => {
49 p = path.normalize(p);
50 const hash = md5(data);
51 const existing = this.paths.get(p);
52 const keepTime = !updateMtime && existing && existing.hash === hash;
53 if (!keepTime) debug(`${existing ? 'modifying' : 'writing'} file ${path.relative(process.cwd(), p)} [${hash}]`);
54 const mtime = keepTime ? existing.mtime : new Date();
55 this.paths.set(p, {
56 hash,
57 contents: Buffer.isBuffer(data) ? data : Buffer.from(data),
58 birthtime: existing ? existing.birthtime : new Date(),
59 ctime: existing ? existing.ctime : new Date(),
60 atime: existing ? existing.atime : new Date(),
61 mtime
62 });
63 return mtime;
64 };
65
66 this.getPaths = () => this.paths;
67
68 this.exists = (p, cb) => cb(this.existsSync(p));
69
70 this.existsSync = p => this.paths.has(path.normalize(p));
71
72 this.statSync = p => {
73 const file = this.paths.get(path.normalize(p));
74 if (file) return {
75 mtime: file.mtime,
76 atime: file.atime,
77 ctime: file.ctime,
78 birthtime: file.birthtime,
79 mtimeMs: file.mtime.getTime(),
80 atimeMs: file.atime.getTime(),
81 ctimeMs: file.ctime.getTime(),
82 birthtimeMs: file.birthtime.getTime(),
83 isFile: returnsTrue,
84 isDirectory: returnsFalse,
85 isBlockDevice: returnsFalse,
86 isCharacterDevice: returnsFalse,
87 isSymbolicLink: returnsFalse,
88 isFIFO: returnsFalse,
89 isSocket: returnsFalse
90 };
91 throw new MemoryFileSystemError(errors.code.ENOENT, p, 'stat');
92 };
93
94 this.readFileSync = (p, optsOrEncoding) => {
95 p = path.normalize(p);
96 if (!this.existsSync(p)) throw new MemoryFileSystemError(errors.code.ENOENT, p, 'readFile');
97 return read(this.paths.get(p), optsOrEncoding);
98 };
99
100 this.readdirSync = p => {
101 const results = [];
102 p = path.normalize(p);
103 this.paths.forEach((_, key) => {
104 if (key.startsWith(p)) results.push(key);
105 });
106 };
107
108 this.rmdirSync = p => {
109 p = path.normalize(p);
110 this.paths.forEach((_, key) => {
111 if (p.startsWith(key)) this.unlinkSync(key);
112 });
113 };
114
115 this.unlinkSync = p => this.paths.delete(p);
116
117 this.writeFileSync = (p, data) => {
118 this.addFile(p, data, true);
119 };
120
121 this.paths = new Map();
122 ['stat', 'readdir', 'rmdir', 'unlink', 'readFile', 'writeFile'].forEach(fn => {
123 this[fn] = (...args) => {
124 const cb = args.pop();
125 let result;
126
127 try {
128 result = this[`${fn}Sync`](...args);
129 } catch (err) {
130 setImmediate(() => cb(err));
131 return;
132 }
133
134 setImmediate(() => cb(null, result));
135 };
136 });
137 }
138
139 var _proto = MemoryFs.prototype;
140
141 /** stream methods taken from memory-fs */
142 _proto.createReadStream = function createReadStream(p, options) {
143 const stream = new ReadableStream();
144 let done = false;
145 let data;
146
147 try {
148 data = this.readFileSync(p);
149 } catch (e) {
150 // eslint-disable-next-line no-underscore-dangle
151 stream._read = function $read() {
152 if (done) return;
153 done = true;
154 this.emit('error', e);
155 this.push(null);
156 };
157
158 return stream;
159 }
160
161 options = options || {};
162 options.start = options.start || 0;
163 options.end = options.end || data.length; // eslint-disable-next-line no-underscore-dangle
164
165 stream._read = function $read() {
166 if (done) return;
167 done = true;
168 this.push(data.slice(options.start, options.end));
169 this.push(null);
170 };
171
172 return stream;
173 };
174
175 _proto.createWriteStream = function createWriteStream(p) {
176 const stream = new WritableStream();
177
178 try {
179 this.writeFileSync(p, Buffer.from(0));
180 } catch (e) {
181 stream.once('prefinish', () => {
182 stream.emit('error', e);
183 });
184 return stream;
185 }
186
187 const bl = [];
188 let len = 0; // eslint-disable-next-line no-underscore-dangle
189
190 stream._write = (chunk, encoding, callback) => {
191 bl.push(chunk);
192 len += chunk.length;
193 this.writeFile(p, Buffer.concat(bl, len), callback);
194 };
195
196 return stream;
197 };
198
199 return MemoryFs;
200}();
201
202module.exports = MemoryFs;
\No newline at end of file