mk-dirs@3.0.0源码阅读

7 阅读3分钟

mk-dirs@3.0.0 发布日期: 2020年8月19日

mk-dirs 是一个轻量级的工具包,核心作用是一键创建多级嵌套目录,解决了 Node.js 旧版本原生 fs.mkdir 无法直接创建嵌套目录的痛点,是对原生文件系统 API 的友好封装,让目录创建更简洁、跨平台、容错性更高。

API 之 sync/mkdir

mk-dirs-3.0.0/src/sync.js

function mkdir(
  str, // 要创建的目录路径(相对 / 绝对)
  opts={} // 配置项(可选)
) {
  // 1、跨平台路径合法性校验(Windows 专属)
  // 仅在 Windows 系统中,检查路径字符串去除根路径后是否包含 Windows 禁止的文件名 / 路径字符,若包含则抛出 “无效字符” 的错误
  if (
    process.platform === 'win32' && 
    /[<>:"|?*]/.test(str.replace(parse(str).root, ''))
  ) {
    throws('EINVAL', 'invalid characters', str);
  }

  // 2、初始化工作目录 & 权限
  let cwd = resolve(opts.cwd || '.'); // 解析工作目录(绝对路径)

  // 声明变量:seg(当前遍历的路径段)、mode(目录权限)
  let seg, mode = opts.mode || 0o777 & (~process.umask());

  // 解析目标路径为绝对路径 → 移除工作目录前缀 → 拆分为路径段数组
  let arr = resolve(cwd, normalize(str)).replace(cwd, '').split(/\/|\\/);

  // 3、递归创建目录(核心循环)
  for (seg of arr) {
    // 拼接当前路径(如 project → project/dist → project/dist/esm)
    cwd = join(cwd, seg);
    // 场景1:路径已存在
    if (existsSync(cwd)) {
      // 校验:存在但不是目录(是文件)→ 抛出错误
      if (!statSync(cwd).isDirectory()) {
        throws('ENOTDIR', 'not a directory', cwd);
      }
    } else {
      // 场景2:路径不存在 → 创建目录
      mkdirSync(cwd, mode);
    }
  }

  return cwd;
}
import { existsSync, mkdirSync, statSync } from 'fs';
import { join, normalize, parse, resolve } from 'path';

API 之 async/mkdir

mk-dirs-3.0.0/src/async.js

async function mkdir(str, opts={}) {
  if (
    process.platform === 'win32' && 
    /[<>:"|?*]/.test(str.replace(parse(str).root, ''))
  ) {
    throws('EINVAL', 'invalid characters', str);
  }

  let cwd = resolve(opts.cwd || '.');
  let seg, stats, mode = opts.mode || 0o777 & (~process.umask());
  let arr = resolve(cwd, normalize(str)).replace(cwd, '').split(/[\\\/]+/);

  // 递归创建目录
  for (seg of arr) {
    cwd = join(cwd, seg);
    if (existsSync(cwd)) {
      // 查看文件信息
      stats = await statp(cwd);
      if (!stats.isDirectory()) {
        throws('ENOTDIR', 'not a directory', cwd);
      }
    } else {
      await mkdirp(cwd, mode); // 创建目录
    }
  }

  return cwd;
}
import { promisify } from 'util';
import { existsSync, mkdir as mk, stat } from 'fs';
import { join, normalize, parse, resolve } from 'path';

// 转为promise风格
const statp = promisify(stat);

// 转为promise风格
const mkdirp = promisify(mk);

node 创建目录API有哪些?

node10.0.0+开始支持递归创建子目录。

// 创建目录(recursive: true可创建多级目录)
fs.mkdir(path[, options], callback) // 异步API 回调
fs.mkdirSync(path[, options]) // 同步API
fsPromises.mkdir(path[, options]) // promise

// 删除空目录(非空目录需先删除内容)
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
fsPromises.rmdir(path[, options])

// 删除文件或目录(recursive: true 可删除非空目录,替代 rmdir)
fs.rm(path[, options], callback)
fs.rmSync(path[, options])
fsPromises.rm(path[, options])

  1. path(必选):要读取的目录路径,可以是字符串、Buffer 或 URL 对象。

  2. options(可选):配置对象或字符串(指定编码格式):

    • encoding:默认 'utf8'(返回字符串路径),设为 null 则返回 Buffer。
    • withFileTypes:布尔值(默认 false),设为 true 时返回 fs.Dirent 对象数组。
    • recursive:布尔值(默认 false),设为 true 时递归读取所有子目录(Node.js v10.0.0+ 支持)。
  3. callback 回调函数:格式为 (err, files) => {},其中:

    • err:读取失败时的错误对象(成功时为 null)。
    • files:读取成功时的结果(路径字符串数组或 fs.Dirent 对象数组,取决于 options)。