-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathmkdirp.js
143 lines (109 loc) · 3.05 KB
/
mkdirp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
'use strict';
var path = require('path');
var fs = require('graceful-fs');
var MASK_MODE = parseInt('7777', 8);
// Utility for passing dirpath that was used with `fs.stat`
function stat(dirpath, cb) {
fs.stat(dirpath, onStat);
function onStat(err, stats) {
cb(err, dirpath, stats);
}
}
// Utility for passing dirpath that was used with `fs.lstat`
function lstat(dirpath, cb) {
fs.lstat(dirpath, onStat);
function onStat(err, stats) {
cb(err, dirpath, stats);
}
}
function mkdirp(dirpath, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = undefined;
}
if (typeof mode === 'string') {
mode = parseInt(mode, 8);
}
dirpath = path.resolve(dirpath);
fs.mkdir(dirpath, mode, onMkdir);
function onMkdir(mkdirErr) {
if (!mkdirErr) {
return stat(dirpath, onStat);
}
switch (mkdirErr.code) {
case 'ENOENT': {
return mkdirp(path.dirname(dirpath), onRecurse);
}
case 'EEXIST': {
return stat(dirpath, onStat);
}
case 'ENOTDIR': {
// On ENOTDIR, this will traverse up the tree until it finds something it can stat
return stat(dirpath, onErrorRecurse);
}
default: {
return callback(mkdirErr);
}
}
function onErrorRecurse(err, dirpath, stats) {
if (err) {
return stat(path.dirname(dirpath), onErrorRecurse);
}
onStat(err, dirpath, stats);
}
function onStat(statErr, dirpath, stats) {
if (statErr) {
// If we have ENOENT here it might be a symlink,
// so we need to recurse to error with the target file name
if (statErr.code === 'ENOENT') {
return lstat(dirpath, onStat);
}
return callback(statErr);
}
if (!stats.isDirectory()) {
return lstat(dirpath, onNonDirectory);
}
if (!mode) {
return callback();
}
if ((stats.mode & MASK_MODE) === mode) {
return callback();
}
fs.chmod(dirpath, mode, onChmod);
}
function onChmod(chmodErr) {
if (chmodErr && chmodErr.code !== 'ENOSUP') {
return callback(chmodErr);
}
callback();
}
function onNonDirectory(err, dirpath, stats) {
if (err) {
// Just being cautious by bubbling the mkdir error
return callback(mkdirErr);
}
if (stats.isSymbolicLink()) {
return fs.readlink(dirpath, onReadlink);
}
// Trying to readdir will surface the ENOTDIR we want
// TODO: Use `opendir` when we support node >12
fs.readdir(dirpath, callback);
}
function onReadlink(err, link) {
if (err) {
// Just being cautious by bubbling the mkdir error
return callback(mkdirErr);
}
// Trying to readdir will surface the ENOTDIR we want
// TODO: Use `opendir` when we support node >12
fs.readdir(link, callback);
}
}
function onRecurse(recurseErr) {
if (recurseErr) {
return callback(recurseErr);
}
mkdirp(dirpath, mode, callback);
}
}
module.exports = mkdirp;