大型代码仓库中删掉废弃的文件

750 阅读2分钟

webpack-deadcode-plugin

webpack-deadcode-plugin获取中未使用文件及未使用的已暴露变量

stats 是通过 webpack 编译源文件时,生成的包含有关于模块的统计数据的 JSON 文件,这些统计数据不仅可以帮助开发者来分析应用的依赖图表,还可以优化编译的速度.

提到分析stats.json,但因为是原始数据,数据量比较大,有一定处理和清洗成本,所以可以使用开源的webpack-deadcode-plugin[5]这个插件

useless-files-webpack-plugin

随着项目开发的进行会经历一个混乱到整齐划一的过程,这个过程中会产生一系列冗余代码,这时候通过useless-files-webpack-plugin这个插件,可以帮助我们删除项目中无用的文件;

大多数项目随着时间的推移,经常会摒弃掉一部分功能, 但大多数只是入口关闭, 或注释掉功能代码块, 依赖的文件如果没删除掉,项目其他成员一般不会去处>理. 在日常开发中, 通过webpack 生成的 stats 文件实现了去除多余文件的插件useless-files-webpack-plugin

const UselessFile = require('useless-files-webpack-plugin')

plugins: [
  new UselessFile({
    root: './src', // 项目目录
    out?: './fileList.json', // 输出文件列表
    out?: (files) => deal(files), // 或者回调处理
    clean?: false // 删除文件,
    exclude?: path // 排除文件列表, 格式为文件路径数组
  })
]

find-unused-module

基于 babel 和 postcss 查找项目中的无用模块

参考链接:jishuin.proginn.com/p/763bfbd5a…

ts-prune

TypeScript 服务提供了一个实用的 API:findAllReferences[14] ,我们平时在 VSCode 里右键点击一个变量,选择 “Find All References” 时,就会调用这个底层 API 找出所有的引用。

ts-morph[15] 这个库封装了包括 findAllReferences 在内的一些底层 API,提供更加简洁易用的调用方式。

ts-prune 就是基于 ts-morph 封装而成。

一段最简化的基于 ts-morph 的检测 dead exports 的代码如下:

// this could be improved... (ex. ignore interfaces/type aliases that describe a parameter type in the same file)
import { Project, TypeGuards, Node } from "ts-morph";

const project = new Project({ tsConfigFilePath: "tsconfig.json" });

for (const file of project.getSourceFiles()) {
  file.forEachChild((child) => {
    if (TypeGuards.isVariableStatement(child)) {
      if (isExported(child)) child.getDeclarations().forEach(checkNode);
    } else if (isExported(child)) checkNode(child);
  });
}

function isExported(node: Node) {
  return TypeGuards.isExportableNode(node) && node.isExported();
}

function checkNode(node: Node) {
  if (!TypeGuards.isReferenceFindableNode(node)) return;

  const file = node.getSourceFile();
  if (
    node.findReferencesAsNodes().filter((n) => n.getSourceFile() !== file)
      .length === 0
  )
    console.log(
      `[${file.getFilePath()}:${node.getStartLineNumber()}: ${
        TypeGuards.hasName(node) ? node.getName() : node.getText()
      }`
    );
}