使用webpack-如何清理无用源文件

1,177 阅读2分钟

背景

项目随着时间的推移不断更新迭代,或者新项目从老项目迁移开发,项目变得越来越臃肿,无形中就存在了很多无用的文件.
大型的项目,如果仅靠人肉去识别,难度相当大,那么怎样能够自动检测哪些是无用的文件呢?

下面介绍使用useless-files-webpack-plugin插件查找无用文件(版本1.0.1)

安装

// npm 
npm i useless-files-webpack-plugin -D

// yarn
yarn add -D useless-files-webpack-plugin

webpack配置

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

plugins: [
  new UselessFile({
    root: './src', // 项目目录
    output: './unused-files.json', // 输出文件列表
    clean: false // 删除文件,
  })
]

vue.config.js中配置

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

module.exports = {
  configureWebpack: (config) => {
    config.plugins.push(
      new UnusedFilesWebpackPlugin({
        root: './src', // 项目目录
        output: './unused-files.json', // 输出文件列表
        clean: false, // 是否删除文件,
      })
    )
  },
  devServer: {
  }
};

运行npm run build

打包完成后会在项目的根目录下生成一个unused-files.json文件, 保存着无用文件的列表。
然后自行根据列表中的提供路径,核对相应文件是否需要保留,手动删除即可。
clean配置true会自动删除,此选项请慎用。

源代码(1.0.1)

const fs = require('fs')
const glob = require('glob')
const path = require('path')
const shelljs = require('shelljs')

class CleanUnusedFilesPlugin {
  constructor (options) {
    this.opts = options
  }
  apply (compiler) {
    let _this = this
    compiler.plugin('after-emit', function (compilation, done) {
      _this.findUnusedFiles(compilation, _this.opts)
      done()
    })
  }

/**
 * 获取依赖的文件
 */
  getDependFiles (compilation) {
    return new Promise((resolve, reject) => {
      const dependedFiles = [...compilation.fileDependencies].reduce(
        (acc, usedFilepath) => {
          if (!~usedFilepath.indexOf('node_modules')) {
            acc.push(usedFilepath)
          }
          return acc
        },
        []
      )
      resolve(dependedFiles)
    })
  }

/**
 * 获取项目目录所有的文件
 */
  getAllFiles (pattern) {
    return new Promise((resolve, reject) => {
      glob(pattern, {
        nodir: true
      }, (err, files) => {
        if (err) {
          throw err
        }
        const out = files.map(item => path.resolve(item))
        resolve(out)
      })
    })
  }

  dealExclude (path, unusedList) {
    const file = fs.readFileSync(path, 'utf-8')
    const files = JSON.parse(file) || []
    const result = unusedList.filter(unused => {
      return !files.some(item => ~unused.indexOf(item))
    })
    return result
  }

  async findUnusedFiles (compilation, config = {}) {
    const { root = './src', clean = false, output = './unused-files.json', exclude = false } = config
    const pattern = root + '/**/*'
    try {
      const allChunks = await this.getDependFiles(compilation)
      const allFiles = await this.getAllFiles(pattern)
      let unUsed = allFiles
        .filter(item => !~allChunks.indexOf(item))
      if (exclude && typeof exclude === 'string') {
        unUsed = this.dealExclude(exclude, unUsed)
      }
      if (typeof output === 'string') {
        fs.writeFileSync(output, JSON.stringify(unUsed, null, 4))
      } else if (typeof output === 'function') {
        output(unUsed)
      }
      if (clean) {
        unUsed.forEach(file => {
          shelljs.rm(file)
          console.log(`remove file: ${file}`)
        })
      }
      return unUsed
    } catch (err) {
      throw (err)
    }
  }
}

module.exports = CleanUnusedFilesPlugin