02 node:path 核心 API

1. join()resolve()

join() 拼接并规范化片段:

path.join("storage", "jobs", "42", "result.zip");

它不保证得到绝对路径。resolve() 从右向左解析,直到形成绝对路径:

path.resolve("storage", "jobs", "42");

若后面的片段本身是绝对路径,前面的部分会被丢弃:

path.resolve("D:\\safe", "C:\\outside");

因此 resolve(root, userInput) 不是安全校验,只是路径计算。

2. 拆解路径

const parsed = path.parse("docs/guide/start.md");

包含:

dir   docs/guide
base  start.md
name  start
ext   .md

生成对应 HTML:

const htmlPath = path.join(
  parsed.dir,
  `${parsed.name}.html`,
);

这能保留目录层级,不能只调用:

path.basename(inputPath)

否则不同目录下的 index.md 会互相覆盖。

3. relative() 用于比较边界

const relative = path.relative(root, target);

若目标在根目录外,结果通常以 .. 开头,或者在 Windows 上成为绝对路径。

function isInsideRoot(root: string, target: string): boolean {
  const relative = path.relative(root, target);

  return (
    relative !== "" &&
    !relative.startsWith(`..${path.sep}`) &&
    relative !== ".." &&
    !path.isAbsolute(relative)
  );
}

是否允许 target === root 应由调用者明确决定,删除操作通常不应允许。

4. 高频 API

API 作用 常见用途
dirname() 父目录 创建输出父目录
basename() 最后一段 获取展示文件名
extname() 扩展名 初步分类,不做安全证明
normalize() 规范化分隔和点段 观察路径,不等于安全
isAbsolute() 判断绝对路径 拒绝归档绝对路径
parse() 拆解路径 替换扩展名
format() 由组件构造路径 parse() 配合

5. 实操:保持相对层级

function createHtmlOutputPath(
  sourceRoot: string,
  outputRoot: string,
  markdownPath: string,
): string {
  const relative = path.relative(sourceRoot, markdownPath);
  const parsed = path.parse(relative);

  return path.join(
    outputRoot,
    parsed.dir,
    `${parsed.name}.html`,
  );
}

输入:

source/docs/start.md

输出:

output/docs/start.html

常见错误

练习题

  1. 分别输出五组参数经过 join()resolve() 的结果。
  2. 实现 .md.html 的路径转换,保留目录结构。
  3. isInsideRoot() 编写 Windows 和 POSIX 测试样例。
  4. 解释为什么 resolve(root, input) 之后仍要做边界检查。