UNPKG

3.12 kBJavaScriptView Raw
1'use strict';
2const fs = require('fs');
3const path = require('path');
4const {promisify} = require('util');
5const semver = require('semver');
6
7const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0');
8
9// https://github.com/nodejs/node/issues/8987
10// https://github.com/libuv/libuv/pull/1088
11const checkPath = pth => {
12 if (process.platform === 'win32') {
13 const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, ''));
14
15 if (pathHasInvalidWinCharacters) {
16 const error = new Error(`Path contains invalid characters: ${pth}`);
17 error.code = 'EINVAL';
18 throw error;
19 }
20 }
21};
22
23const processOptions = options => {
24 // https://github.com/sindresorhus/make-dir/issues/18
25 const defaults = {
26 mode: 0o777,
27 fs
28 };
29
30 return {
31 ...defaults,
32 ...options
33 };
34};
35
36const permissionError = pth => {
37 // This replicates the exception of `fs.mkdir` with native the
38 // `recusive` option when run on an invalid drive under Windows.
39 const error = new Error(`operation not permitted, mkdir '${pth}'`);
40 error.code = 'EPERM';
41 error.errno = -4048;
42 error.path = pth;
43 error.syscall = 'mkdir';
44 return error;
45};
46
47const makeDir = async (input, options) => {
48 checkPath(input);
49 options = processOptions(options);
50
51 const mkdir = promisify(options.fs.mkdir);
52 const stat = promisify(options.fs.stat);
53
54 if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) {
55 const pth = path.resolve(input);
56
57 await mkdir(pth, {
58 mode: options.mode,
59 recursive: true
60 });
61
62 return pth;
63 }
64
65 const make = async pth => {
66 try {
67 await mkdir(pth, options.mode);
68
69 return pth;
70 } catch (error) {
71 if (error.code === 'EPERM') {
72 throw error;
73 }
74
75 if (error.code === 'ENOENT') {
76 if (path.dirname(pth) === pth) {
77 throw permissionError(pth);
78 }
79
80 if (error.message.includes('null bytes')) {
81 throw error;
82 }
83
84 await make(path.dirname(pth));
85
86 return make(pth);
87 }
88
89 try {
90 const stats = await stat(pth);
91 if (!stats.isDirectory()) {
92 throw new Error('The path is not a directory');
93 }
94 } catch (_) {
95 throw error;
96 }
97
98 return pth;
99 }
100 };
101
102 return make(path.resolve(input));
103};
104
105module.exports = makeDir;
106
107module.exports.sync = (input, options) => {
108 checkPath(input);
109 options = processOptions(options);
110
111 if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) {
112 const pth = path.resolve(input);
113
114 fs.mkdirSync(pth, {
115 mode: options.mode,
116 recursive: true
117 });
118
119 return pth;
120 }
121
122 const make = pth => {
123 try {
124 options.fs.mkdirSync(pth, options.mode);
125 } catch (error) {
126 if (error.code === 'EPERM') {
127 throw error;
128 }
129
130 if (error.code === 'ENOENT') {
131 if (path.dirname(pth) === pth) {
132 throw permissionError(pth);
133 }
134
135 if (error.message.includes('null bytes')) {
136 throw error;
137 }
138
139 make(path.dirname(pth));
140 return make(pth);
141 }
142
143 try {
144 if (!options.fs.statSync(pth).isDirectory()) {
145 throw new Error('The path is not a directory');
146 }
147 } catch (_) {
148 throw error;
149 }
150 }
151
152 return pth;
153 };
154
155 return make(path.resolve(input));
156};