• 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// mkdirSync successfully creates directory from given path
57{
58  const pathname = path.join(tmpdir.path, nextdir());
59
60  fs.mkdirSync(pathname);
61
62  const exists = fs.existsSync(pathname);
63  assert.strictEqual(exists, true);
64}
65
66// mkdirSync and mkdir require path to be a string, buffer or url.
67// Anything else generates an error.
68[false, 1, {}, [], null, undefined].forEach((i) => {
69  assert.throws(
70    () => fs.mkdir(i, common.mustNotCall()),
71    {
72      code: 'ERR_INVALID_ARG_TYPE',
73      name: 'TypeError'
74    }
75  );
76  assert.throws(
77    () => fs.mkdirSync(i),
78    {
79      code: 'ERR_INVALID_ARG_TYPE',
80      name: 'TypeError'
81    }
82  );
83});
84
85// mkdirpSync when both top-level, and sub-folders do not exist.
86{
87  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
88
89  fs.mkdirSync(pathname, { recursive: true });
90
91  const exists = fs.existsSync(pathname);
92  assert.strictEqual(exists, true);
93  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
94}
95
96// mkdirpSync when folder already exists.
97{
98  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
99
100  fs.mkdirSync(pathname, { recursive: true });
101  // Should not cause an error.
102  fs.mkdirSync(pathname, { recursive: true });
103
104  const exists = fs.existsSync(pathname);
105  assert.strictEqual(exists, true);
106  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
107}
108
109// mkdirpSync ../
110{
111  const pathname = `${tmpdir.path}/${nextdir()}/../${nextdir()}/${nextdir()}`;
112  fs.mkdirSync(pathname, { recursive: true });
113  const exists = fs.existsSync(pathname);
114  assert.strictEqual(exists, true);
115  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
116}
117
118// mkdirpSync when path is a file.
119{
120  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
121
122  fs.mkdirSync(path.dirname(pathname));
123  fs.writeFileSync(pathname, '', 'utf8');
124
125  assert.throws(
126    () => { fs.mkdirSync(pathname, { recursive: true }); },
127    {
128      code: 'EEXIST',
129      message: /EEXIST: .*mkdir/,
130      name: 'Error',
131      syscall: 'mkdir',
132    }
133  );
134}
135
136// mkdirpSync when part of the path is a file.
137{
138  const filename = path.join(tmpdir.path, nextdir(), nextdir());
139  const pathname = path.join(filename, nextdir(), nextdir());
140
141  fs.mkdirSync(path.dirname(filename));
142  fs.writeFileSync(filename, '', 'utf8');
143
144  assert.throws(
145    () => { fs.mkdirSync(pathname, { recursive: true }); },
146    {
147      code: 'ENOTDIR',
148      message: /ENOTDIR: .*mkdir/,
149      name: 'Error',
150      syscall: 'mkdir',
151      path: pathname // See: https://github.com/nodejs/node/issues/28015
152    }
153  );
154}
155
156// `mkdirp` when folder does not yet exist.
157{
158  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
159
160  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err) {
161    assert.strictEqual(err, null);
162    assert.strictEqual(fs.existsSync(pathname), true);
163    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
164  }));
165}
166
167// `mkdirp` when path is a file.
168{
169  const pathname = path.join(tmpdir.path, nextdir(), nextdir());
170
171  fs.mkdirSync(path.dirname(pathname));
172  fs.writeFileSync(pathname, '', 'utf8');
173  fs.mkdir(pathname, { recursive: true }, common.mustCall((err) => {
174    assert.strictEqual(err.code, 'EEXIST');
175    assert.strictEqual(err.syscall, 'mkdir');
176    assert.strictEqual(fs.statSync(pathname).isDirectory(), false);
177  }));
178}
179
180// `mkdirp` when part of the path is a file.
181{
182  const filename = path.join(tmpdir.path, nextdir(), nextdir());
183  const pathname = path.join(filename, nextdir(), nextdir());
184
185  fs.mkdirSync(path.dirname(filename));
186  fs.writeFileSync(filename, '', 'utf8');
187  fs.mkdir(pathname, { recursive: true }, common.mustCall((err) => {
188    assert.strictEqual(err.code, 'ENOTDIR');
189    assert.strictEqual(err.syscall, 'mkdir');
190    assert.strictEqual(fs.existsSync(pathname), false);
191    // See: https://github.com/nodejs/node/issues/28015
192    // The path field varies slightly in Windows errors, vs., other platforms
193    // see: https://github.com/libuv/libuv/issues/2661, for this reason we
194    // use startsWith() rather than comparing to the full "pathname".
195    assert(err.path.startsWith(filename));
196  }));
197}
198
199// mkdirpSync dirname loop
200// XXX: windows and smartos have issues removing a directory that you're in.
201if (common.isMainThread && (common.isLinux || common.isOSX)) {
202  const pathname = path.join(tmpdir.path, nextdir());
203  fs.mkdirSync(pathname);
204  process.chdir(pathname);
205  fs.rmdirSync(pathname);
206  assert.throws(
207    () => { fs.mkdirSync('X', { recursive: true }); },
208    {
209      code: 'ENOENT',
210      message: /ENOENT: .*mkdir/,
211      name: 'Error',
212      syscall: 'mkdir',
213    }
214  );
215  fs.mkdir('X', { recursive: true }, (err) => {
216    assert.strictEqual(err.code, 'ENOENT');
217    assert.strictEqual(err.syscall, 'mkdir');
218  });
219}
220
221// mkdirSync and mkdir require options.recursive to be a boolean.
222// Anything else generates an error.
223{
224  const pathname = path.join(tmpdir.path, nextdir());
225  ['', 1, {}, [], null, Symbol('test'), () => {}].forEach((recursive) => {
226    const received = common.invalidArgTypeHelper(recursive);
227    assert.throws(
228      () => fs.mkdir(pathname, { recursive }, common.mustNotCall()),
229      {
230        code: 'ERR_INVALID_ARG_TYPE',
231        name: 'TypeError',
232        message: 'The "options.recursive" property must be of type boolean.' +
233          received
234      }
235    );
236    assert.throws(
237      () => fs.mkdirSync(pathname, { recursive }),
238      {
239        code: 'ERR_INVALID_ARG_TYPE',
240        name: 'TypeError',
241        message: 'The "options.recursive" property must be of type boolean.' +
242          received
243      }
244    );
245  });
246}
247
248// `mkdirp` returns first folder created, when all folders are new.
249{
250  const dir1 = nextdir();
251  const dir2 = nextdir();
252  const firstPathCreated = path.join(tmpdir.path, dir1);
253  const pathname = path.join(tmpdir.path, dir1, dir2);
254
255  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
256    assert.strictEqual(err, null);
257    assert.strictEqual(fs.existsSync(pathname), true);
258    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
259    assert.strictEqual(path, firstPathCreated);
260  }));
261}
262
263// `mkdirp` returns first folder created, when last folder is new.
264{
265  const dir1 = nextdir();
266  const dir2 = nextdir();
267  const pathname = path.join(tmpdir.path, dir1, dir2);
268  fs.mkdirSync(path.join(tmpdir.path, dir1));
269  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
270    assert.strictEqual(err, null);
271    assert.strictEqual(fs.existsSync(pathname), true);
272    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
273    assert.strictEqual(path, pathname);
274  }));
275}
276
277// `mkdirp` returns undefined, when no new folders are created.
278{
279  const dir1 = nextdir();
280  const dir2 = nextdir();
281  const pathname = path.join(tmpdir.path, dir1, dir2);
282  fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), { recursive: true });
283  fs.mkdir(pathname, { recursive: true }, common.mustCall(function(err, path) {
284    assert.strictEqual(err, null);
285    assert.strictEqual(fs.existsSync(pathname), true);
286    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
287    assert.strictEqual(path, undefined);
288  }));
289}
290
291// `mkdirp.sync` returns first folder created, when all folders are new.
292{
293  const dir1 = nextdir();
294  const dir2 = nextdir();
295  const firstPathCreated = path.join(tmpdir.path, dir1);
296  const pathname = path.join(tmpdir.path, dir1, dir2);
297  const p = fs.mkdirSync(pathname, { recursive: true });
298  assert.strictEqual(fs.existsSync(pathname), true);
299  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
300  assert.strictEqual(p, firstPathCreated);
301}
302
303// `mkdirp.sync` returns first folder created, when last folder is new.
304{
305  const dir1 = nextdir();
306  const dir2 = nextdir();
307  const pathname = path.join(tmpdir.path, dir1, dir2);
308  fs.mkdirSync(path.join(tmpdir.path, dir1), { recursive: true });
309  const p = fs.mkdirSync(pathname, { recursive: true });
310  assert.strictEqual(fs.existsSync(pathname), true);
311  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
312  assert.strictEqual(p, pathname);
313}
314
315// `mkdirp.sync` returns undefined, when no new folders are created.
316{
317  const dir1 = nextdir();
318  const dir2 = nextdir();
319  const pathname = path.join(tmpdir.path, dir1, dir2);
320  fs.mkdirSync(path.join(tmpdir.path, dir1, dir2), { recursive: true });
321  const p = fs.mkdirSync(pathname, { recursive: true });
322  assert.strictEqual(fs.existsSync(pathname), true);
323  assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
324  assert.strictEqual(p, undefined);
325}
326
327// `mkdirp.promises` returns first folder created, when all folders are new.
328{
329  const dir1 = nextdir();
330  const dir2 = nextdir();
331  const firstPathCreated = path.join(tmpdir.path, dir1);
332  const pathname = path.join(tmpdir.path, dir1, dir2);
333  async function testCase() {
334    const p = await fs.promises.mkdir(pathname, { recursive: true });
335    assert.strictEqual(fs.existsSync(pathname), true);
336    assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
337    assert.strictEqual(p, firstPathCreated);
338  }
339  testCase();
340}
341
342// Keep the event loop alive so the async mkdir() requests
343// have a chance to run (since they don't ref the event loop).
344process.nextTick(() => {});
345