项目中无用export的检测方案

背景

export是js中用于导出模块的关键字,用法如export const dataexport function getData等,在项目版本迭代过程中,开发人员会出现把引用处代码给删了,但是忘记删除export const的情况。

如下图,export const没删的话,打包会把代码打进去增加代码体积。

本文提供一种方案,可以快速检测项目中无用export的情况,开发人员可以根据实际考量进行清除。

export导出的检测

js 复制代码
const { parse, Lang } = require('@ast-grep/napi')
const fg = require('fast-glob')
const path = require('node:path')
const fs = require('node:fs')

const files = fg.sync([path.join(__dirname, '/src/**/*.js')])

const data = []
const addData = (node) => {
  const keyword = node?.getMatch?.('A')?.text?.() || ''
  if (keyword) {
    data.push(keyword)
  }
}
files.forEach((file) => {
  const source = fs.readFileSync(file, 'utf-8')
  const ast = parse(Lang.JavaScript, source)
  const root = ast.root()
  let node = root.find('export const $A = $B')
  addData(node)
  node = root.find('export function $A ($B) { $C }')
  addData(node)
  node = root.find('export let $A = $B')
  addData(node)
})

fs.writeFileSync('./data.json', JSON.stringify(data, null, 2))

上述代码很简单,执行流程就是用fast-glob把所有js文件扫出来,然后借助ast-grep,把所有的export const代码找出来。

无用export的识别

第一步我们是把所有的export const都找出来了,第二步我们需要从中识别出来哪些是有用的,哪些是无用的。从性能、执行速度考虑,我们采用golang实现本步骤,而不用js。

go 复制代码
package main

import (
  "encoding/json"
  "fmt"
  "os"
  "path/filepath"
  "strings"
  "sync"
)

// 结构用于存储计数结果
type StringCount struct {
  String string
  Count  int
}

func main() {
  // 1. 读取 json 文件
  jsonData, err := os.ReadFile("data.json")
  if err != nil {
    fmt.Printf("无法读取json: %v\n", err)
    return
  }

  // 2. 解析 JSON 数据
  var data []string
  if err := json.Unmarshal(jsonData, &data); err != nil {
    fmt.Printf("解析 JSON 失败: %v\n", err)
    return
  }

  // 3. 存储结果的映射
  resultMap := make(map[string]int)
  var mutex sync.Mutex
  var wg sync.WaitGroup

  // 4. 遍历指定目录中的文件
  searchDir := "./src"
  fileCount := 0
  isJsVue := func(filePath string) bool {
    return strings.HasSuffix(filePath, ".js") || strings.HasSuffix(filePath, ".vue")
  }

  // 5. 文件处理函数
  processFile := func(filePath string) {
    defer wg.Done()
    
    if !isJsVue(filePath) {
      return
    }
    content, err := os.ReadFile(filePath)
    if err != nil {
      return
    }    
    contentStr := string(content)
        
    // 检查每个字符串在文件中的出现次数
    for _, searchStr := range data {
      count := strings.Count(contentStr, searchStr)
      if count > 0 {
        mutex.Lock()
        resultMap[searchStr] += count
        mutex.Unlock()
      }
    }
  }

  err = filepath.Walk(searchDir, func(path string, info os.FileInfo, err error) error {
    if err != nil {
      return err
    }   
    // 跳过目录
    if info.IsDir() {
      return nil
    }
    if isJsVue(path) {
      fileCount++
      wg.Add(1)
      go processFile(path)
    }
    return nil
  })

  if err != nil {
    fmt.Printf("目录遍历错误: %v\n", err)
    return
  }

  // 等待所有文件处理完成
  wg.Wait()

  results := make([]StringCount, 0, len(resultMap))
  for str, count := range resultMap {
    if count == 1 {
      results = append(results, StringCount{String: str, Count: count})
    }
  }
  // 输出结果
  for _, result := range results {
    fmt.Printf("%s: %d次\n", result.String, result.Count)
  }
}

不会golang也没关系,交给ai生成golang代码就行,上面的go代码其实我也是用ai生成的,哈哈哈。我们不必过于担忧ai会替代我们,而是拥抱ai,有ai这个强大的辅助,开发人员所要做的就是在开发过程中思考、挖掘可提升改进的点,需要自身深入业务深入项目,深入之后发挥自己的创新力。

相关推荐
拉不动的猪4 分钟前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy1 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom1 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom1 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom1 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom2 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom2 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试
LaoZhangAI3 小时前
2025最全GPT-4o图像生成API指南:官方接口配置+15个实用提示词【保姆级教程】
前端
ONE_Gua3 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
LaoZhangAI3 小时前
2025最全Cherry Studio使用MCP指南:8种强大工具配置方法与实战案例
前端