UNPKG

6.49 kBJavaScriptView Raw
1var os = require('os');
2var path = require('path');
3
4var Directory = require('./directory');
5var File = require('./file');
6var SymbolicLink = require('./symlink');
7
8
9var isWindows = process.platform === 'win32';
10
11function getPathParts(filepath) {
12 var parts = path._makeLong(path.resolve(filepath)).split(path.sep);
13 parts.shift();
14 if (isWindows) {
15 // parts currently looks like ['', '?', 'c:', ...]
16 parts.shift();
17 var q = parts.shift(); // should be '?'
18 var base = '\\\\' + q + '\\' + parts.shift().toLowerCase();
19 parts.unshift(base);
20 }
21 if (parts[parts.length - 1] === '') {
22 parts.pop();
23 }
24 return parts;
25}
26
27
28
29/**
30 * Create a new file system.
31 * @constructor
32 */
33function FileSystem() {
34
35 var root = new Directory();
36
37 // populate with default directories
38 var defaults = [os.tmpdir && os.tmpdir() || os.tmpDir(), process.cwd()];
39 defaults.forEach(function(dir) {
40 var parts = getPathParts(dir);
41 var directory = root;
42 var i, ii, name, candidate;
43 for (i = 0, ii = parts.length; i < ii; ++i) {
44 name = parts[i];
45 candidate = directory.getItem(name);
46 if (!candidate) {
47 directory = directory.addItem(name, new Directory());
48 } else if (candidate instanceof Directory) {
49 directory = candidate;
50 } else {
51 throw new Error('Failed to create directory: ' + dir);
52 }
53 }
54 });
55
56 /**
57 * Root directory.
58 * @type {Directory}
59 */
60 this._root = root;
61
62}
63
64
65/**
66 * Get a file system item.
67 * @param {string} filepath Path to item.
68 * @return {Item} The item (or null if not found).
69 */
70FileSystem.prototype.getItem = function(filepath) {
71 var parts = getPathParts(filepath);
72 var item = this._root;
73 for (var i = 0, ii = parts.length; i < ii; ++i) {
74 item = item.getItem(parts[i]);
75 if (!item) {
76 break;
77 }
78 }
79 return item;
80};
81
82
83/**
84 * Populate a directory with an item.
85 * @param {Directory} directory The directory to populate.
86 * @param {string} name The name of the item.
87 * @param {string|Buffer|function|Object} obj Instructions for creating the
88 * item.
89 */
90function populate(directory, name, obj) {
91 var item;
92 if (typeof obj === 'string' || Buffer.isBuffer(obj)) {
93 // contents for a file
94 item = new File();
95 item.setContent(obj);
96 } else if (typeof obj === 'function') {
97 // item factory
98 item = obj();
99 } else {
100 // directory with more to populate
101 item = new Directory();
102 for (var key in obj) {
103 populate(item, key, obj[key]);
104 }
105 }
106 /**
107 * Special exception for redundant adding of empty directories.
108 */
109 if (item instanceof Directory &&
110 item.list().length === 0 &&
111 directory.getItem(name) instanceof Directory) {
112 // pass
113 } else {
114 directory.addItem(name, item);
115 }
116}
117
118
119/**
120 * Configure a mock file system.
121 * @param {Object} paths Config object.
122 * @return {FileSystem} Mock file system.
123 */
124FileSystem.create = function(paths) {
125 var system = new FileSystem();
126
127 for (var filepath in paths) {
128 var parts = getPathParts(filepath);
129 var directory = system._root;
130 var i, ii, name, candidate;
131 for (i = 0, ii = parts.length - 1; i < ii; ++i) {
132 name = parts[i];
133 candidate = directory.getItem(name);
134 if (!candidate) {
135 directory = directory.addItem(name, new Directory());
136 } else if (candidate instanceof Directory) {
137 directory = candidate;
138 } else {
139 throw new Error('Failed to create directory: ' + filepath);
140 }
141 }
142 populate(directory, parts[i], paths[filepath]);
143 }
144
145 return system;
146};
147
148
149/**
150 * Generate a factory for new files.
151 * @param {Object} config File config.
152 * @return {function():File} Factory that creates a new file.
153 */
154FileSystem.file = function(config) {
155 config = config || {};
156 return function() {
157 var file = new File();
158 if (config.hasOwnProperty('content')) {
159 file.setContent(config.content);
160 }
161 if (config.hasOwnProperty('mode')) {
162 file.setMode(config.mode);
163 } else {
164 file.setMode(0666);
165 }
166 if (config.hasOwnProperty('uid')) {
167 file.setUid(config.uid);
168 }
169 if (config.hasOwnProperty('gid')) {
170 file.setGid(config.gid);
171 }
172 if (config.hasOwnProperty('atime')) {
173 file.setATime(config.atime);
174 }
175 if (config.hasOwnProperty('ctime')) {
176 file.setCTime(config.ctime);
177 }
178 if (config.hasOwnProperty('mtime')) {
179 file.setMTime(config.mtime);
180 }
181 return file;
182 };
183};
184
185
186/**
187 * Generate a factory for new symbolic links.
188 * @param {Object} config File config.
189 * @return {function():File} Factory that creates a new symbolic link.
190 */
191FileSystem.symlink = function(config) {
192 config = config || {};
193 return function() {
194 var link = new SymbolicLink();
195 if (config.hasOwnProperty('mode')) {
196 link.setMode(config.mode);
197 } else {
198 link.setMode(0666);
199 }
200 if (config.hasOwnProperty('uid')) {
201 link.setUid(config.uid);
202 }
203 if (config.hasOwnProperty('gid')) {
204 link.setGid(config.gid);
205 }
206 if (config.hasOwnProperty('path')) {
207 link.setPath(config.path);
208 } else {
209 throw new Error('Missing "path" property');
210 }
211 if (config.hasOwnProperty('atime')) {
212 link.setATime(config.atime);
213 }
214 if (config.hasOwnProperty('ctime')) {
215 link.setCTime(config.ctime);
216 }
217 if (config.hasOwnProperty('mtime')) {
218 link.setMTime(config.mtime);
219 }
220 return link;
221 };
222};
223
224
225/**
226 * Generate a factory for new directories.
227 * @param {Object} config File config.
228 * @return {function():Directory} Factory that creates a new directory.
229 */
230FileSystem.directory = function(config) {
231 config = config || {};
232 return function() {
233 var dir = new Directory();
234 if (config.hasOwnProperty('mode')) {
235 dir.setMode(config.mode);
236 } else {
237 dir.setMode(0777);
238 }
239 if (config.hasOwnProperty('uid')) {
240 dir.setUid(config.uid);
241 }
242 if (config.hasOwnProperty('gid')) {
243 dir.setGid(config.gid);
244 }
245 if (config.hasOwnProperty('items')) {
246 for (var name in config.items) {
247 populate(dir, name, config.items[name]);
248 }
249 }
250 if (config.hasOwnProperty('atime')) {
251 dir.setATime(config.atime);
252 }
253 if (config.hasOwnProperty('ctime')) {
254 dir.setCTime(config.ctime);
255 }
256 if (config.hasOwnProperty('mtime')) {
257 dir.setMTime(config.mtime);
258 }
259 return dir;
260 };
261};
262
263
264/**
265 * Module exports.
266 * @type {function}
267 */
268module.exports = FileSystem;