webpack源码分析——enhanced-resolve库之getType、normalize、join和cachedJoin函数

发布于:2024-04-24 ⋅ 阅读:(29) ⋅ 点赞:(0)

一、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中定义的值之一

  1. 根据传入的路径length判断
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
		}
	}
  1. 当路径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.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

  1. 对传入的路径调用getType函数根据返回值,对Empty、AbsoluteWin和Relative三种类型进行处理
    switch (getType(p)) {
       case PathType.Empty:
       case PathType.AbsoluteWin:
       case PathType.Relative: 
    }
    
  2. 当为路径类型为PathType.Empty
    return p; // 直接返回
    
  3. 当为路径类型为PathType.AbsoluteWin
    return winNormalize(p); // path.win32.normalize
    
  4. 当为路径类型为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函数

该函数进行路径进行拼接

  1. 如果 request 变量没有传入
    if (!request) return normalize(rootPath); // 直接调用normalize 函数返回
    
  2. 根据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}`;
    	}
    }
    
  3. 兜底
    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 函数的基础上加上缓存

  1. 判断 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;