一、PathType 路径类型
js
const PathType = Object.freeze({
Empty: 0, // 空
Normal: 1, // 默认值
Relative: 2, // 相对路径
AbsoluteWin: 3, // win 下的绝对路径
AbsolutePosix: 4, // posix 下的绝对路径
Internal: 5 // enhanced-resolve 内部自定义的一种类型,具体是用来 escaping,具体说明:https://www.npmjs.com/package/enhanced-resolve
});
注:什么是Posix?点击查看path
函数中用到的变量定义
js
const path = require("path");
const CHAR_HASH = "#".charCodeAt(0);
const CHAR_SLASH = "/".charCodeAt(0);
const CHAR_BACKSLASH = "\\".charCodeAt(0);
const CHAR_A = "A".charCodeAt(0);
const CHAR_Z = "Z".charCodeAt(0);
const CHAR_LOWER_A = "a".charCodeAt(0);
const CHAR_LOWER_Z = "z".charCodeAt(0);
const CHAR_DOT = ".".charCodeAt(0);
const CHAR_COLON = ":".charCodeAt(0);
const posixNormalize = path.posix.normalize;
const winNormalize = path.win32.normalize;
二、getType 函数
该函数通过传入的路径,判断并返回其类型值。返回的类型值为PathType中定义的值之一
- 根据传入的路径length判断
js
switch (p.length) {
case 0:
return PathType.Empty; // 当length ==0 时, 返回 PathType.Empty
case 1: {
const c0 = p.charCodeAt(0);
switch (c0) {
case CHAR_DOT:
return PathType.Relative; // 如果开头的第一个字符为'.'时,返回PathType.Relative
case CHAR_SLASH:
return PathType.AbsolutePosix; // 如果开头的第一个字符为'/'时,返回PathType.AbsolutePosix
case CHAR_HASH:
return PathType.Internal; // 如果开头的第一个字符为'#'时,返回PathType.Internal
}
return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
}
case 2: {
const c0 = p.charCodeAt(0);
switch (c0) {
case CHAR_DOT: { // 当第一个字符为'.'
const c1 = p.charCodeAt(1);
switch (c1) {
case CHAR_DOT:
case CHAR_SLASH:
return PathType.Relative; // 当第二个字符为'.'或'/'时。返回PathType.Relative
}
return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
}
case CHAR_SLASH:
return PathType.AbsolutePosix; // 当第二个字符为'/'时。返回PathType.AbsolutePosix
case CHAR_HASH:
return PathType.Internal; // 当第二个字符为'#'时。返回PathType.Internal
}
const c1 = p.charCodeAt(1);
if (c1 === CHAR_COLON) { // 判断是否时win平台
if (
(c0 >= CHAR_A && c0 <= CHAR_Z) ||
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
) {
return PathType.AbsoluteWin; // 是 win 返回PathType.AbsoluteWin
}
}
return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
}
}
- 当路径length大于2时
js
const c0 = p.charCodeAt(0); // 获取第一个字符
switch (c0) {
case CHAR_DOT: {
const c1 = p.charCodeAt(1);
switch (c1) {
case CHAR_SLASH:
return PathType.Relative; // 当第一个字符为'.'第二个字符为'/'时,返回PathType.Relative
case CHAR_DOT: {
const c2 = p.charCodeAt(2);
if (c2 === CHAR_SLASH) return PathType.Relative; // 当第一个字符为'.'第二个字符为'.'和第三个字符为'/'时,返回PathType.Relative
return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
}
}
return PathType.Normal;// 没有匹配到返回兜底值PathType.Normal
}
case CHAR_SLASH:
return PathType.AbsolutePosix; // 当第一个字符为'/'时,返回PathType.AbsolutePosix
case CHAR_HASH:
return PathType.Internal;// 当第一个字符为'#'时,返回PathType.Internal
}
const c1 = p.charCodeAt(1);
if (c1 === CHAR_COLON) { // 判断是否在win下,并且为绝对路径
const c2 = p.charCodeAt(2);
if (
(c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
((c0 >= CHAR_A && c0 <= CHAR_Z) ||
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
) {
return PathType.AbsoluteWin;
}
}
return PathType.Normal;// 没有匹配到返回兜底值PathType.Normal
例1:win上的绝对路径
例2:posix上的绝对路径
三、normalize函数
该函数通过调用node的path.normalize方法规范化给定的 path
-
对传入的路径调用getType函数根据返回值,对Empty、AbsoluteWin和Relative三种类型进行处理
jsswitch (getType(p)) { case PathType.Empty: case PathType.AbsoluteWin: case PathType.Relative: }
-
当为路径类型为PathType.Empty
jsreturn p; // 直接返回
-
当为路径类型为PathType.AbsoluteWin
jsreturn winNormalize(p); // path.win32.normalize
-
当为路径类型为PathType.Relative
jsconst r = posixNormalize(p); // path.posix.normalize return getType(r) === PathType.Relative ? r : `./${r}`; // 因为 './webpack' 这样的路径被 posixNormalize 函数处理后 变为 'webpack',所有需要这一行进行特殊处理
源码
js
const normalize = p => {
switch (getType(p)) {
case PathType.Empty:
return p;
case PathType.AbsoluteWin:
return winNormalize(p);
case PathType.Relative: {
const r = posixNormalize(p);
return getType(r) === PathType.Relative ? r : `./${r}`;
}
}
return posixNormalize(p);
};
四、join函数
该函数进行路径进行拼接
-
如果 request 变量没有传入
jsif (!request) return normalize(rootPath); // 直接调用normalize 函数返回
-
根据rootPath和request类型判断
jsconst requestType = getType(request); // 首先判断 requestType 类型 switch (requestType) { // 如果时绝对路径,就不需要拼接了,直接调用 posixNormalize/winNormalize 返回 case PathType.AbsolutePosix: return posixNormalize(request); case PathType.AbsoluteWin: return winNormalize(request); } switch (getType(rootPath)) { // 判断 rootPath 类型,上面 request 类型已经排除了绝对路径的情况,所有判断 rootPath 类型后直接和request进行拼接 case PathType.Normal: case PathType.Relative: case PathType.AbsolutePosix: return posixNormalize(`${rootPath}/${request}`); case PathType.AbsoluteWin: return winNormalize(`${rootPath}\\${request}`); } /** * request 类型不为 AbsolutePosix和AbsoluteWin * rootPath 类型不为 Normal、Relative、AbsolutePosix和AbsoluteWin时 * 进入下面阶段 */ switch (requestType) { case PathType.Empty: // request 为空时(这里不存在因为在函数顶部已经错了空的判断),直接返回 rootPath。但是 rootPath 也有可能为空 return rootPath; case PathType.Relative: { const r = posixNormalize(rootPath); return getType(r) === PathType.Relative ? r : `./${r}`; } }
-
兜底
jsreturn posixNormalize(rootPath);
源码
js
const join = (rootPath, request) => {
if (!request) return normalize(rootPath);
const requestType = getType(request);
switch (requestType) {
case PathType.AbsolutePosix:
return posixNormalize(request);
case PathType.AbsoluteWin:
return winNormalize(request);
}
switch (getType(rootPath)) {
case PathType.Normal:
case PathType.Relative:
case PathType.AbsolutePosix:
return posixNormalize(`${rootPath}/${request}`);
case PathType.AbsoluteWin:
return winNormalize(`${rootPath}\\${request}`);
}
switch (requestType) {
case PathType.Empty:
return rootPath;
case PathType.Relative: {
const r = posixNormalize(rootPath);
return getType(r) === PathType.Relative ? r : `./${r}`;
}
}
return posixNormalize(rootPath);
};
五、cachedJoin函数
该函数在 join 函数的基础上加上缓存
-
判断 rootPath 是否在缓存中
jsconst joinCache = new Map(); let cache = joinCache.get(rootPath); // 从 map 中获取 rootPath if (cache === undefined) { // rootPath 没有在缓存中 joinCache.set(rootPath, (cache = new Map())); // 新增缓存 } else { cacheEntry = cache.get(request); // 在缓存中,根据request获取rootPath对应缓存的值 if (cacheEntry !== undefined) return cacheEntry; } cacheEntry = join(rootPath, request); cache.set(request, cacheEntry); // 没有在缓存中时,对当前路径进行缓存 return cacheEntry;