Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node-mkdirp项目源码阅读 #23

Open
huruji opened this issue Mar 6, 2018 · 0 comments
Open

node-mkdirp项目源码阅读 #23

huruji opened this issue Mar 6, 2018 · 0 comments

Comments

@huruji
Copy link
Owner

huruji commented Mar 6, 2018

node-mkdirp是一个linux命令 mkdir -p的node版本,也就是创建多级目录。node-mkdirp值得新手学习的地方在于学习对于错误码的利用和基本的API使用。我曾经也写过一个创建多级目录的方法,不过自己都只是通过split方法对目录分隔开后逐层判断是否存在,再创建。node-mkdirp的方式则是通过fs.mkdir的错误码来判断,挺巧妙的。

var path = require('path');
var fs = require('fs');
var _0777 = parseInt('0777', 8);

module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;

function mkdirP (p, opts, f, made) {
    // 如果opts是函数,则说明这是指定的回调,非函数且非对象则opt是指定的mode
    if (typeof opts === 'function') {
        f = opts;
        opts = {};
    }
    else if (!opts || typeof opts !== 'object') {
        opts = { mode: opts };
    }

    var mode = opts.mode;
    var xfs = opts.fs || fs;

    if (mode === undefined) {
        mode = _0777 & (~process.umask());
    }
    if (!made) made = null;

    var cb = f || function () {};
    p = path.resolve(p);

    // 尝试创建目录,mkdir创建一个不存在的目录时候会返回的错误码是ENOENT
    xfs.mkdir(p, mode, function (er) {
        // 无错误则表明创建的就是最后一级目录了
        if (!er) {
            made = made || p;
            return cb(null, made);
        }
        switch (er.code) {
            // 错误码是ENOENT表明无此文件或目录,则不断尝试创建父级目录
            case 'ENOENT':
                mkdirP(path.dirname(p), opts, function (er, made) {
                    // 无错误则继续尝试创建传入的目录,有错误则说明是已经存在,则直接执行回调
                    if (er) cb(er, made);
                    else mkdirP(p, opts, cb, made);
                });
                break;

            // In the case of any other error, just see if there's a dir
            // there already.  If so, then hooray!  If not, then something
            // is borked.
            // 出现其他错误主要是目录存在,则获取stat
            default:
                xfs.stat(p, function (er2, stat) {
                    // if the stat fails, then that's super weird.
                    // let the original error be the failure reason.
                    if (er2 || !stat.isDirectory()) cb(er, made)
                    else cb(null, made);
                });
                break;
        }
    });
}

mkdirP.sync = function sync (p, opts, made) {
    if (!opts || typeof opts !== 'object') {
        opts = { mode: opts };
    }

    var mode = opts.mode;
    var xfs = opts.fs || fs;

    if (mode === undefined) {
        mode = _0777 & (~process.umask());
    }
    if (!made) made = null;

    p = path.resolve(p);

    // 同步版本类似于异步版本的处理,不过需要使用try...catch...来捕捉错误
    try {
        xfs.mkdirSync(p, mode);
        made = made || p;
    }
    catch (err0) {
        switch (err0.code) {
            case 'ENOENT' :
                made = sync(path.dirname(p), opts, made);
                sync(p, opts, made);
                break;

            // In the case of any other error, just see if there's a dir
            // there already.  If so, then hooray!  If not, then something
            // is borked.
            default:
                var stat;
                try {
                    stat = xfs.statSync(p);
                }
                catch (err1) {
                    throw err0;
                }
                if (!stat.isDirectory()) throw err0;
                break;
        }
    }

    return made;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant