• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24const assert = require('assert');
25const fs = require('fs');
26const path = require('path');
27
28const tmpdir = require('../common/tmpdir');
29tmpdir.refresh();
30
31let dirc = 0;
32function nextdir() {
33  return `test${++dirc}`;
34}
35
36// fs.mkdir creates directory using assigned path
37{
38  const pathname = path.join(tmpdir.path, nextdir());
39
40  fs.mkdir(pathname, common.mustCall(function(err) {
41    assert.strictEqual(err, null);
42    assert.strictEqual(fs.existsSync(pathname), true);
43  }));
44}
45
46// fs.mkdir creates directory with assigned mode value
47{
48  const pathname = path.join(tmpdir.path, nextdir());
49
50  fs.mkdir(pathname, 0o777, common.mustCall(function(err) {
51    assert.strictEqual(err, null);
52    assert.strictEqual(fs.existsSync(pathname), true);
53  }));
54}
55
56// fs.mkdir creates directory with mode passed as an options object
57{
58  const pathname = path.join(tmpdir.path, nextdir());
59
60  fs.mkdir(pathname, { mode: 0o777 }, common.mustCall(function(err) {
61    assert.strictEqual(err, null);
62    assert.strictEqual(fs.existsSync(pathname), true);
63  }));
64}
65
66// fs.mkdirSync creates directory with mode passed as an options object
67{
68  const pathname = path.join(tmpdir.path, nextdir());
69
70  fs.mkdirSync(pathname, { mode: 0o777 });
71
72  assert.strictEqual(fs.existsSync(pathname), true);
73}
74
75// mkdirSync successfully creates directory from given path
76{
77  const pathname = path.join(tmpdir.path, nextdir());
78
79  fs.mkdirSync(pathname);
80
81  const exists = fs.existsSync(pathname);
82  assert.strictEqual(exists, true);
83}
84
85// mkdirSync and mkdir require path to be a string, buffer or url.
86// Anything else generates an error.
87[false, 1, {}, [], null, undefined].forEach((i) => {
88  assert.throws(
89    () => fs.mkdir(i, common.mustNotCall()),
90    {
91      code: 'ERR_INVALID_ARG_TYPE',
92      name: 'TypeError'
93    }
94  );
95  assert.throws(
96    () => fs.mkdirSync(i),
97    {
98      code: 'ERR_INVALID_ARG_TYPE',
99      name: 'TypeError'
100    }
101  );
102});
103
104// mkdirpSync when both top-level, and sub-folders do not exist.
105{
106  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
107
108  fs.mkdirSync(pathname, { recursive: true });
109
110  const exists = fs.existsSync(pathname);
111  assert.strictEqual(exists, true);
112  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
113}
114
115// mkdirpSync when folder already exists.
116{
117  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
118
119  fs.mkdirSync(pathname, { recursive: true });
120  // Should not cause an error.
121  fs.mkdirSync(pathname, { recursive: true });
122
123  const exists = fs.existsSync(pathname);
124  assert.strictEqual(exists, true);
125  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
126}
127
128// mkdirpSync ../
129{
130  const pathname = `${tmpdir.path}/${nextdir()}/../${nextdir()}/${nextdir()}`;
131  fs.mkdirSync(pathname, { recursive: true });
132  const exists = fs.existsSync(pathname);
133  assert.strictEqual(exists, true);
134  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
135}
136
137// mkdirpSync when path is a file.
138{
139  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
140
141  fs.mkdirSync(path.dirname(pathname));
142  fs.writeFileSync(pathname, '', 'utf8');
143
144  assert.throws(
145    () => { fs.mkdirSync(pathname, { recursive: true }); },
146    {
147      code: 'EEXIST',
148      message: /EEXIST: .*mkdir/,
149      name: 'Error',
150      syscall: 'mkdir',
151    }
152  );
153}
154
155// mkdirpSync when part of the path is a file.
156{
157  const filename = path.join(tmpdir.path, nextdir(), nextdir());
158  const pathname = path.join(filename, nextdir(), nextdir());
159
160  fs.mkdirSync(path.dirname(filename));
161  fs.writeFileSync(filename, '', 'utf8');
162
163  assert.throws(
164    () => { fs.mkdirSync(pathname, { recursive: true }); },
165    {
166      code: 'ENOTDIR',
167      message: /ENOTDIR: .*mkdir/,
168      name: 'Error',
169      syscall: 'mkdir',
170      path: pathname // See: https://github.com/nodejs/node/issues/28015
171    }
172  );
173}
174
175// `mkdirp` when folder does not yet exist.
176{
177  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
178
179  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err) {
180    assert.strictEqual(err, null);
181    assert.strictEqual(fs.existsSync(pathname), true);
182    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
183  }));
184}
185
186// `mkdirp` when path is a file.
187{
188  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
189
190  fs.mkdirSync(path.dirname(pathname));
191  fs.writeFileSync(pathname, '', 'utf8');
192  fs.mkdir(pathname, { recursive: true }, common.mustCall((err) => {
193    assert.strictEqual(err.code, 'EEXIST');
194    assert.strictEqual(err.syscall, 'mkdir');
195    assert.strictEqual(fs.statSync(pathname).isDirectory(), false);
196  }));
197}
198
199// `mkdirp` when part of the path is a file.
200{
201  const filename = path.join(tmpdir.path, nextdir(), nextdir());
202  const pathname = path.join(filename, nextdir(), nextdir());
203
204  fs.mkdirSync(path.dirname(filename));
205  fs.writeFileSync(filename, '', 'utf8');
206  fs.mkdir(pathname, { recursive: true }, common.mustCall((err) => {
207    assert.strictEqual(err.code, 'ENOTDIR');
208    assert.strictEqual(err.syscall, 'mkdir');
209    assert.strictEqual(fs.existsSync(pathname), false);
210    // See: https://github.com/nodejs/node/issues/28015
211    // The path field varies slightly in Windows errors, vs., other platforms
212    // see: https://github.com/libuv/libuv/issues/2661, for this reason we
213    // use startsWith() rather than comparing to the full "pathname".
214    assert(err.path.startsWith(filename));
215  }));
216}
217
218// mkdirpSync dirname loop
219// XXX: windows and smartos have issues removing a directory that you're in.
220if (common.isMainThread && (common.isLinux || common.isOSX)) {
221  const pathname = path.join(tmpdir.path, nextdir());
222  fs.mkdirSync(pathname);
223  process.chdir(pathname);
224  fs.rmdirSync(pathname);
225  assert.throws(
226    () => { fs.mkdirSync('X', { recursive: true }); },
227    {
228      code: 'ENOENT',
229      message: /ENOENT: .*mkdir/,
230      name: 'Error',
231      syscall: 'mkdir',
232    }
233  );
234  fs.mkdir('X', { recursive: true }, (err) => {
235    assert.strictEqual(err.code, 'ENOENT');
236    assert.strictEqual(err.syscall, 'mkdir');
237  });
238}
239
240// mkdirSync and mkdir require options.recursive to be a boolean.
241// Anything else generates an error.
242{
243  const pathname = path.join(tmpdir.path, nextdir());
244  ['', 1, {}, [], null, Symbol('test'), () => {}].forEach((recursive) => {
245    const received = common.invalidArgTypeHelper(recursive);
246    assert.throws(
247      () => fs.mkdir(pathname, { recursive }, common.mustNotCall()),
248      {
249        code: 'ERR_INVALID_ARG_TYPE',
250        name: 'TypeError',
251        message: 'The "options.recursive" property must be of type boolean.' +
252          received
253      }
254    );
255    assert.throws(
256      () => fs.mkdirSync(pathname, { recursive }),
257      {
258        code: 'ERR_INVALID_ARG_TYPE',
259        name: 'TypeError',
260        message: 'The "options.recursive" property must be of type boolean.' +
261          received
262      }
263    );
264  });
265}
266
267// `mkdirp` returns first folder created, when all folders are new.
268{
269  const dir1 = nextdir();
270  const dir2 = nextdir();
271  const firstPathCreated = path.join(tmpdir.path, dir1);
272  const pathname = path.join(tmpdir.path, dir1, dir2);
273
274  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
275    assert.strictEqual(err, null);
276    assert.strictEqual(fs.existsSync(pathname), true);
277    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
278    assert.strictEqual(path, firstPathCreated);
279  }));
280}
281
282// `mkdirp` returns first folder created, when last folder is new.
283{
284  const dir1 = nextdir();
285  const dir2 = nextdir();
286  const pathname = path.join(tmpdir.path, dir1, dir2);
287  fs.mkdirSync(path.join(tmpdir.path, dir1));
288  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
289    assert.strictEqual(err, null);
290    assert.strictEqual(fs.existsSync(pathname), true);
291    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
292    assert.strictEqual(path, pathname);
293  }));
294}
295
296// `mkdirp` returns undefined, when no new folders are created.
297{
298  const dir1 = nextdir();
299  const dir2 = nextdir();
300  const pathname = path.join(tmpdir.path, dir1, dir2);
301  fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), { recursive: true });
302  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
303    assert.strictEqual(err, null);
304    assert.strictEqual(fs.existsSync(pathname), true);
305    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
306    assert.strictEqual(path, undefined);
307  }));
308}
309
310// `mkdirp.sync` returns first folder created, when all folders are new.
311{
312  const dir1 = nextdir();
313  const dir2 = nextdir();
314  const firstPathCreated = path.join(tmpdir.path, dir1);
315  const pathname = path.join(tmpdir.path, dir1, dir2);
316  const p = fs.mkdirSync(pathname, { recursive: true });
317  assert.strictEqual(fs.existsSync(pathname), true);
318  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
319  assert.strictEqual(p, firstPathCreated);
320}
321
322// `mkdirp.sync` returns first folder created, when last folder is new.
323{
324  const dir1 = nextdir();
325  const dir2 = nextdir();
326  const pathname = path.join(tmpdir.path, dir1, dir2);
327  fs.mkdirSync(path.join(tmpdir.path, dir1), { recursive: true });
328  const p = fs.mkdirSync(pathname, { recursive: true });
329  assert.strictEqual(fs.existsSync(pathname), true);
330  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
331  assert.strictEqual(p, pathname);
332}
333
334// `mkdirp.sync` returns undefined, when no new folders are created.
335{
336  const dir1 = nextdir();
337  const dir2 = nextdir();
338  const pathname = path.join(tmpdir.path, dir1, dir2);
339  fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), { recursive: true });
340  const p = fs.mkdirSync(pathname, { recursive: true });
341  assert.strictEqual(fs.existsSync(pathname), true);
342  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
343  assert.strictEqual(p, undefined);
344}
345
346// `mkdirp.promises` returns first folder created, when all folders are new.
347{
348  const dir1 = nextdir();
349  const dir2 = nextdir();
350  const firstPathCreated = path.join(tmpdir.path, dir1);
351  const pathname = path.join(tmpdir.path, dir1, dir2);
352  async function testCase() {
353    const p = await fs.promises.mkdir(pathname, { recursive: true });
354    assert.strictEqual(fs.existsSync(pathname), true);
355    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
356    assert.strictEqual(p, firstPathCreated);
357  }
358  testCase();
359}
360
361// Keep the event loop alive so the async mkdir() requests
362// have a chance to run (since they don't ref the event loop).
363process.nextTick(() => {});
364