Webpack——依赖分析

2021-03-04  本文已影响0人  我是Msorry

依赖分析

单文件依赖分析

通过 path.node.type === 'ImportDeclaration' 判断依赖,再用哈希表(对象)来存储依赖

// 设置根目录
const projectRoot = resolve(__dirname, 'project_1')
// 类型声明
type DepRelation = { [key: string]: { deps: string[], code: string } }
// 初始化一个空的 depRelation,用于收集依赖
const depRelation: DepRelation = {}

// 将入口文件的绝对路径传入函数,如 D:\demo\fixture_1\index.js
collectCodeAndDeps(resolve(projectRoot, 'index.js'))

console.log(depRelation)
console.log('done')


function collectCodeAndDeps(filepath: string) {
  // 项目入口文件路径,如 index.js
  const key = getProjectPath(filepath)
  // 获取文件内容,将内容放至 depRelation
  const code = readFileSync(filepath).toString()
  // 初始化 depRelation[key]
  depRelation[key] = { deps: [], code: code }
  // 将代码转为 AST
  const ast = parse(code, { sourceType: 'module' })
  // 分析文件依赖,将内容放至 depRelation
  traverse(ast, {
    enter: path => {
      if (path.node.type === 'ImportDeclaration') {
        // path.node.source.value 往往是一个相对路径,如 ./a.js,需要先把它转为一个绝对路径
        const depAbsolutePath = resolve(dirname(filepath), path.node.source.value)
        // 依赖转为相对于项目根目录的相对路径
        const depProjectPath = getProjectPath(depAbsolutePath)
        // 把依赖写进 depRelation
        depRelation[key].deps.push(depProjectPath)
      }
    }
  })
}
// 获取文件相对于根目录的相对路径
function getProjectPath(path: string) {
  return relative(projectRoot, path).replace(/\\/g, '/')
}

嵌套依赖分析

使用递归获取嵌套的依赖,递归有 callstack 溢出风险,比如嵌套层数超过 20000 时,程序直接挂掉

function collectCodeAndDeps(filepath: string) {
  // 项目入口文件路径,如 index.js
  const key = getProjectPath(filepath)
  // 获取文件内容,将内容放至 depRelation
  const code = readFileSync(filepath).toString()
  // 初始化 depRelation[key]
  depRelation[key] = { deps: [], code: code }
  // 将代码转为 AST
  const ast = parse(code, { sourceType: 'module' })
  // 分析文件依赖,将内容放至 depRelation
  traverse(ast, {
    enter: path => {
      if (path.node.type === 'ImportDeclaration') {
        // path.node.source.value 往往是一个相对路径,如 ./a.js,需要先把它转为一个绝对路径
        const depAbsolutePath = resolve(dirname(filepath), path.node.source.value)
        // 依赖转为相对于项目根目录的相对路径
        const depProjectPath = getProjectPath(depAbsolutePath)
        // 把依赖写进 depRelation
        depRelation[key].deps.push(depProjectPath)
        // 递归收集依赖
        collectCodeAndDeps(depAbsolutePath)
      }
    }
  })
}

循环依赖分析

有的循环依赖代码可以正常执行,有的不能正常执行,只要避免重复读同一文件,都能做静态依赖分析

function collectCodeAndDeps(filepath: string) {
  // 项目入口文件路径,如 index.js
  const key = getProjectPath(filepath)
  // 避免重复读同一文件
  if(Object.keys(depRelation).includes(key)){
    console.warn(`duplicated dependency: ${key}`) // 注意,重复依赖不一定是循环依赖
    return
  }
  // 获取文件内容,将内容放至 depRelation
  const code = readFileSync(filepath).toString()
  // 初始化 depRelation[key]
  depRelation[key] = { deps: [], code: code }
  // 将代码转为 AST
  const ast = parse(code, { sourceType: 'module' })
  // 分析文件依赖,将内容放至 depRelation
  traverse(ast, {
    enter: path => {
      if (path.node.type === 'ImportDeclaration') {
        // path.node.source.value 往往是一个相对路径,如 ./a.js,需要先把它转为一个绝对路径
        const depAbsolutePath = resolve(dirname(filepath), path.node.source.value)
        // 依赖转为相对于项目根目录的相对路径
        const depProjectPath = getProjectPath(depAbsolutePath)
        // 把依赖写进 depRelation
        depRelation[key].deps.push(depProjectPath)
        // 递归收集依赖
        collectCodeAndDeps(depAbsolutePath)
      }
    }
  })
}
上一篇 下一篇

猜你喜欢

热点阅读