一、PathType 路径类型
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
函数中用到的变量定义
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判断
switch (p.length) {case 0:return PathType.Empty; // 当length ==0 时, 返回 PathType.Emptycase 1: {const c0 = p.charCodeAt(0);switch (c0) {case CHAR_DOT:return PathType.Relative; // 如果开头的第一个字符为‘.’时,返回PathType.Relativecase CHAR_SLASH:return PathType.AbsolutePosix; // 如果开头的第一个字符为‘/’时,返回PathType.AbsolutePosixcase 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.AbsolutePosixcase 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时
const c0 = p.charCodeAt(0); // 获取第一个字符switch (c0) {case CHAR_DOT: {const c1 = p.charCodeAt(1);switch (c1) {case CHAR_SLASH:return PathType.Relative; // 当第一个字符为‘.’第二个字符为‘/’时,返回PathType.Relativecase CHAR_DOT: {const c2 = p.charCodeAt(2);if (c2 === CHAR_SLASH) return PathType.Relative; // 当第一个字符为‘.’第二个字符为‘.’和第三个字符为‘/’时,返回PathType.Relativereturn PathType.Normal; // 没有匹配到返回兜底值PathType.Normal}}return PathType.Normal;// 没有匹配到返回兜底值PathType.Normal}case CHAR_SLASH:return PathType.AbsolutePosix; // 当第一个字符为‘/’时,返回PathType.AbsolutePosixcase 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三种类型进行处理
switch (getType(p)) {case PathType.Empty:case PathType.AbsoluteWin:case PathType.Relative: }
- 当为路径类型为PathType.Empty
return p; // 直接返回
- 当为路径类型为PathType.AbsoluteWin
return winNormalize(p); // path.win32.normalize
- 当为路径类型为PathType.Relative
const r = posixNormalize(p); // path.posix.normalize return getType(r) === PathType.Relative ? r : `./${r}`; // 因为 ‘./webpack’ 这样的路径被 posixNormalize 函数处理后 变为 ‘webpack’,所有需要这一行进行特殊处理
源码
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 变量没有传入
if (!request) return normalize(rootPath); // 直接调用normalize 函数返回
- 根据rootPath和request类型判断
const 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}`;} }
- 兜底
return posixNormalize(rootPath);
源码
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 是否在缓存中
const 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;