描述 grunt 插件标准目录结构与 tasks、lib 职责

解读

国内面试官问这道题,不是让你背文件夹名,而是验证三件事:

  1. 你是否真的写过并发布过 Grunt 插件(npm 包);
  2. 你是否理解 Grunt“约定优于配置”的设计哲学;
  3. 你是否能把“任务注册”与“核心逻辑解耦”,方便单元测试与持续集成。
    答不到“为什么这样分”,只会罗列目录,会被直接判为“用过但没深入”。

知识点

  1. npm 包规范Grunt 插件命名公约(grunt-contrib-xxx 或 grunt-xxx)。
  2. Grunt 插件入口只能是 tasks/ 目录下的 .js 文件,通过 grunt.loadTasks 自动加载。
  3. tasks/ 只放“任务壳”:注册 task、解析 options、调用 lib;零业务逻辑
  4. lib/ 放“纯函数”或“Class”,100% 脱离 grunt,可被 jest/mocha 直接 require。
  5. test/ 必须覆盖 lib,** Istanbul 覆盖率 ≥ 80%** 是国内大厂 CI 门禁。
  6. Gruntfile.js 只在插件自身用作自测脚手架不会发布到 npm
  7. README 中文示例CHANGELOG.md是国内私有 Nexus/Verdaccio 仓库的强制合规项。

答案

一个通过 npm publish 且被 cnpm 源正确同步的 Grunt 插件,目录结构如下:

grunt-xxx/
├── package.json          // 必须声明 "peerDependencies": {"grunt": ">=1.0.0"}
├── README.md             // 中文快速开始、options 表格、常见错误
├── CHANGELOG.md          // 符合国内审计要求
├── LICENSE               // 国内大厂只看 MIT 或 Apache-2.0
├── Gruntfile.js          // 插件作者自测用,**不会被打包**
├── tasks/
│   └── xxx.js            // **唯一入口**,只做三件事:
│                         // 1. grunt.registerMultiTask('xxx', description, function() {})
│                         // 2. 合并 this.options() 与 this.data
│                         // 3. 调用 lib/xxx.js 并回传 done()
├── lib/
│   └── xxx.js            // **纯逻辑**,无 grunt 依赖,**可独立单元测试**
│                         // 暴露一个 run(files, options) 返回 Promise
├── test/
│   ├── lib.test.js       // 只测 lib,**mock 掉 grunt**
│   ├── fixtures/         // 输入输出样例,**必须包含中文路径样本**
│   └── expected/
└── .github/workflows/ci.yml  // 国内 GitLab 同理,**Node 14/16/18 矩阵**

职责边界

  • tasks/:负责“怎么跑”,包括校验 grunt 版本、合并 options、回写 grunt.log。
  • lib/:负责“跑什么”,包括读文件、AST 处理、缓存优化、sourcemap 生成,完全无 console

拓展思考

  1. 微前端场景:把 lib 再拆成 @your-org/grunt-xxx-core同时供 gulp/webpack 复用,实现“一次核心,多构建器适配”,国内阿里、字节已落地。
  2. 性能优化:在 lib 里实现 mmap 方式读大文件 + worker_threads 压缩图片,可把 10 s 任务降到 2 s,CI 并行度提升 5 倍
  3. 合规审计:在 tasks 里加入 snyk 漏洞扫描钩子不符合等保 2.0 的直接中断构建,这是国内金融客户的强制要求。