头图

前言

上一小节,我们发现直接引入defineConfig会导致程序报错,于是分析了产生报错的原因并查看了vite中的解决思路

本节,在svite中解决这个问题

好文推荐

源码获取

传送门

更新进度

公众号:更新至第17

博客:更新至第7

代码实现

结合上一节分析,我们知道问题出在buildBoundle函数中,并且我们已经知道了通过esbuild pluginonResolve钩子可以解决这个问题

如下,第一步我们需要对一些条件进行守卫:入口点本身、绝对路径、node内置模块

// packages\vite\src\node\config.ts
async function buildBoundle(fileName: string) {
  const result = await build({
    ...
    plugins:[{
      name: 'externalize-deps',
      setup(build) {
        build.onResolve({filter:/^[^.].*/},async ({path:id,importer,kind})=>{
            // id:导入的依赖地址,如import xxx from 'svite'语句中指的是svite
            // importer:当前正在处理的文件地址,如svite所在的文件地址为playground\config\svite.config.ts
            // kind:导入的类型,如entry-point表示导入入口点;entry-point表示导入依赖
            if (kind === "entry-point" || isAbsolute(id) || isBuiltin(id)) {
                return;
            }
            return {
                path:analizePathValue(id,importer,kind),
                external:true
            }
        })
      }
    }]
  });
  const { text } = result.outputFiles[0];
  return text;
}

当程序通过if守卫条件后,则说明命中了裸依赖,在我们的svite.config.ts文件中即指命中了svite。此时通过将钩子返回的对象的external设置为true从而将依赖从boundle中排除则能有效避免上一小节中我们遇到的报错问题:Dynamic require of "fs" is not supported

由于我们跳过了svite的处理,则最终代码里将无法找到defineConfig而引发报错,为了解决这个问题,我们要将导入的路径替换为绝对路径,这样node就能正确加载啦

因此我们的关键点就是去生成path的值来对引入模块路径做替换,即analizePathValue是如何实现的

function analizePathValue(id: string, importer: string, kind: string) {
  ...
}

想要实现这一点,我们需要从包的元数据中找相关的定义,即packages\vite\package.json中与模块引入相关的字段

{
  "main": "./dist/node/index.js",
  "types": "./dist/node/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/node/index.d.ts",
      "import": "./dist/node/index.js"
    }
  }
}

vite中是自己去读取该json文件并对导出字段做比对的,且针对svite/xxx的形式也需要做额外的兼容处理。这里我们借助local-pkg来达到同样的目的

pnpm i local-pkg

该包提供的resolveModule函数会自动帮我们完成路径的识别和转换工作,当然,我们要先判断对应的依赖包是否存在,当获取到路径之后,还需要通过node内置的pathToFileURL将其转换为URL路径

if (isPackageExists(id)) {
  const fileUrl = resolveModule(id);
  if (fileUrl) {
    return pathToFileURL(fileUrl).href;
  }
}

如果你是跟着笔者一起实现的话,当你运行的时候,你会得到如下的错误

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in C:\Users\11574\Desktop\git\mini-vite\node_modules\svite\package.json

这是因为resolveModule本质上是cjs的导入方式,而我们一开始在创建项目时缺少了对require的支持,为因此,需要找到packages\vite\package.json文件的exports并增加如下代码

{
  "exports": {
    ".": {
      ...
      "require":"./dist/node/index.js"
    }
  },
}

设置好后,再次启动,程序正常运行即可

总结

本小节,依据上一节的分析思路解决了svite中引入ts辅助函数的报错问题,其实就是在打包处理该配置文件时将引入排除就可以了


Sir_苏
4 声望2 粉丝