写了个插件,给代码里藏东西,线上可用!

插件用途

大家好,我是石小石!


相信你一定也遇到过这样的场景:有时候我们想在代码中"夹带一点私货",比如留下一些自定义信息或标识,如部署文档、关联资料、构建时间等信息,让这些内容跟着上线代码一并部署。当然,这些信息的查看方式只有我们知道,这样的话,代码开发会方便不少!

基于这个想法,我开发了一个小插件,能够自动记录当前代码的 Git 信息,还可以注入任意自定义内容,同时兼容 Webpack 和 Vite 项目。

使用这个插件后,只需要在浏览器控制台输入 info,就能轻松查看代码中的构建信息或你的专属小彩蛋啦!

使用Trae实现插件

插件原理

要实现插件的效果,我们的大致思路如下:

  • 打包或编译时,通过node获取当前的git信息
  • 打包结束后,将获取的git信息注入打包后的代码中
  • 在浏览器控制台,通过特殊方式展示打包信息
js 复制代码
├── 核心逻辑层
│   ├── Git信息采集(借助node能力)
│   ├── 信息处理与注入(通过webapck或vite钩子实现)
├── 构建工具适配层(判断构建工具类型)
│   ├── Webpack插件实现
│   └── Vite插件实现
└── 输出呈现层
    ├── 控制台样式优化(console美化)
    └── 信息展示机制(window劫持)

使用Trae-Builder快速搭建项目

在开始项目之前,我们先创建dist-info文件夹,然后用Trae打开项目。

要开发一个webpack或vite插件,我们的项目应该包含下面的结构:

js 复制代码
├── index.js           // 插件核心逻辑
├── README.md          // 插件使用说明
├── rollup.config.js   // 打包配置
├── package.json       // 依赖管理

Trae提供了一个Trae-Builder模式 ,可以轻松完成从零到一的项目构建。在 Builder 模式下,我们对代码文件的任何更改都会自动保存。

我们可以借助Trae-Builder直接在空项目中创建上述项目结构

Trae-Builder运行完毕后,一个完成的项目就被快速创建出来了。现在,我们只需要审查项目,保留删减代码即可。如下图,Trae-Builder生成的代码基本完全满足我的需求,直接点击【接受】即可。

至此,我们借助Trae-Builder就快速实现了项目的搭建。

使用Trae实现核心代码

  • 代码基本架构

根据插件的需求,我们的index.js基本架构应该如下:

js 复制代码
const isVite =
  process.argv.some((arg) => arg.includes("vite")) ||
  process.env.VITE_ !== undefined;

function DistInfoPlugin() {
  try {
    //获取js内容(内部包含git信息)
    const jsContent = getJsContent();
    // 根据脚手架类型,执行不同js注入逻辑
    return isVite ? vitePlugin(jsContent) : webpackPlugin(jsContent);
  } catch (err) {
    console.log("DistInfoPlugin", err);
  }
}
// 确保插件作为默认导出
module.exports.default = DistInfoPlugin;
module.exports = DistInfoPlugin;

当项目打包时,插件会判断当前脚手架类型,执行webapck或vite对应的钩子函数。打包过程中,node会获取当前的git信息,并生成js内容(getJsContent)。在打包结束时,对应的webapck或vite钩子激活,生成的js脚本被注入在打包文件的index.html中。

  • git信息的获取

getJsContent的第一步应该是通过node获取git信息,我们通过Trae-Builder让Trae帮我们完善代码。

Trae-Builder生成的代码基本可用,我们接受修改后的代码,然后做适当修改:

js 复制代码
const { execSync } = require('child_process') //同步子进程

const getGitInfo = (gitMeta) => {
  try {
    return execSync(gitMeta)?.toString().trim();
  } catch {
    return "--";
  }
};

const getJsContent = () => {
  const consoleList = [
    {
      description: "提交人员",
      value: getGitInfo("git show -s --format=%cn"),
    },
    {
      description: "版本信息",
      value: getGitInfo("git show -s --format=%h"),
    },
    {
      description: "代码分支",
      value: getGitInfo("git symbolic-ref --short -q HEAD"),
    },
    {
      description: "提交说明",
      value: getGitInfo("git show -s --format=%s"),
    },
    {
      description: "提交时间",
      value: getGitInfo("git show -s --format=%cd"),
    },
  ];
};

上述代码的核心功能是通过 child_process 模块执行 Git 命令,并提取 Git 信息。

child_process 是 Node.js 的一个核心模块,允许你、我们在 Node.js 中启动子进程并与之进行交互。execSync(gitMeta) 执行传入的 Git 命令,返回的是一个 Buffer 对象,通过 .toString() 将其转换为字符串。

由于git show生成的时间是原始数据类型,我们可以将时间进行格式化,方便阅读。

  • 网页端的信息输出与js生成

把代码的打包信息直接输出在控制台是不安全的,我们并不希望所有人打开控制台都可以看到打包信息。参考vue2的数据劫持原理,我们可以对window对象的特殊属性进行数据劫持:

js 复制代码
Object.defineProperty(window, 'info', {
  get: function() {
    // console.log("window的info属性被访问了")
  }
})

根据上述代码的效果,当我们读取window的info属性时(相当于控制台输入info),会触发getter的回调函数。

那么,如果我们在get的方法中输出所有的git信息,然后将这段js注入打包后的index.html,就可以实现如下效果了

在控制台输入"info",控制台打印代码的git信息。

根据上述原理,我们的getJsContent方法完善如下:

js 复制代码
const getJsContent = () => {

  const consoleList = [
    // ...
  ];

  return `(function(window){
    const BUILD_INFO_CONSOLE_LIST = ${consoleList}
    Object.defineProperty(window, 'info', {
        get: function() {
            console.clear();
            BUILD_INFO_CONSOLE_LIST.forEach(res=>{
                console.log (res.description, res.value) 
            })
        }
    })
})(window)`;
  
};
  • 打包完成时的js脚本注入

我们插件的最后一步就是将生成的js脚本,在打包完毕后插入项目的index.html中实现注入,这一过程的实现要根据不同的脚手架类型做不同处理。

js 复制代码
function webpackPlugin(jsContent) {
	const createAsset = (content) => {
		return {
			source: () => content,
			size: () => content.length
		}
	}
	return {
		apply(compiler) {
			compiler.hooks.emit.tap('distInfoPlugin', (compilation) => {
				// 获取 HTML 和 JS 文件的路径
				const jsAsset = Object.keys(compilation.assets).find((assetPath) => assetPath.endsWith('.js'))
				const htmlAsset = Object.keys(compilation.assets).find((assetPath) => assetPath.endsWith('.html'))

				if (!jsAsset || !htmlAsset) return

				// 生成唯一的 JS 文件路径
				const jsPathParts = jsAsset.split('/')
				const timestamp = Date.now().toString()
				jsPathParts[jsPathParts.length - 1] = `dist-info-${timestamp}.js`
				const jsFilePath = jsPathParts.join('/')

				// 修改 HTML 文件内容,插入新的 JS 文件路径
				const originalHtmlContent = compilation.assets[htmlAsset]?.source()
				if (!originalHtmlContent) return

				const updatedHtmlContent = originalHtmlContent.replace(
					/(<head[^>]*>)/,
					`$1<script src="${compiler.options.output.publicPath}${jsFilePath}"></script>`
				)

				// 更新 HTML 文件内容到编译输出
				compilation.assets[htmlAsset] = createAsset(updatedHtmlContent)

				compilation.assets[jsFilePath] = createAsset(jsContent)
			})
		}
	}
}

上面的代码实现了一个自定义的 Webpack 插件,用于在构建时处理 HTML 和 JavaScript 文件的输出,并动态地插入一个新的 JavaScript 文件到 HTML 中。

上面的代码使用到了webpack插件开发的一些基础知识,如果有疑问,可以参考博主的webpack插件开发文章

  • vite脚本插入

由于vite提供了transformIndexHtml钩子

将js注入到打包后的html中是非常容易的

js 复制代码
function vitePlugin(jsContent) {
  return {
    name: "vite-plugin-dist-info",
    transformIndexHtml(html) {
      const scriptTag = `<script>${jsContent}</script>`;
      return html.replace(/</body>/, `${scriptTag}</body>`);
    },
  };
}

如果你对vite的插件有疑问,可以参考博主的vite插件入门教程

  • 效果验证

插件开发完毕后,我们可以在任意一个webpck5或vite项目中引入index.js中的代码,查看效果。

  • webpack项目

如图,在webpack项目中引入代码(distInfo就是开发的插件代码)

然后启动项目,打开控制台,输入"info"可以看到,打包的信息已经实现了。

  • vite项目

如图,在vite项目中引入代码(distInfo就是开发的插件代码)

启动项目后,输入指令"info"也可以看到git信息。

代码优化

通过上述的代码,我们已经实现了一个功能完备的网站打包信息追踪插件,但它还不完善,以下是一些可以改进的地方:

  • 信息加密

生成的js脚本是明文的,这意味着用户的git信息也会被明文展示,这显然是不安全的。

为了解决上述问题,我们可以对生成的信息在编译时加密,运行时解密。

  • console美化

直接通过console.log输出的信息不够醒目,在控制台与普通打印文本不好区分。我们可以通过重写log方法实现输出信息的美化:

js 复制代码
function log (description, value) {
  console.log(
    "%c 信息名称 %c 信息值 ",
    "background:#ff4d4f;border:1px solid #ff4d4f; padding: 1px; border-radius: 2px 0 0 2px; color: #fff",
    "border:1px solid #ff4d4f; padding: 1px; border-radius: 0 2px 2px 0; color: #ff4d4f",
  )
}

参考博主的console.log的美化教程

  • 增加配置项

根据基础代码,我们只能通过在控制台输入"info"触发信息打印,而且不能够自定义输出信息。因此,我们可以给BuildInfoPlugin插件增加options配置项即可。

js 复制代码
function BuildInfoPlugin(options = {}) {
    try {
        const jsContent = getJsContent(options);
        return isVite ? vitePlugin(jsContent) : webpackPlugin(jsContent);
    } catch (err) {
        console.log('BuildInfo', err);
    }
}

篇幅问题,不在此文章中具体展示优化过程,感兴趣的同学可以直接fork源码。

源码地址:github.com/1139874527/...

参与项目与代码贡献

本文中提到的插件已经基本完善,并且稳定,可以供大家使用。目前,插件已发布至npm,欢迎安装使用。dist-info是一个简单而值得学习的项目,它仍有不少优化空间。如果你有兴趣参与维护并成为贡献者,欢迎🌟star并提交PR(合理必通过)。

插件使用地址:www.npmjs.com/package/dis...

github仓库地址:github.com/1139874527/...

相关推荐
阆遤3 小时前
利用TRAE对nanobot进行安全分析并优化
python·安全·ai·trae·nanobot
Molesidy10 小时前
【VSCode】VSCode或者Trae的扩展文件夹以及用户设置文件夹的路径更改到指定位置
ide·编辑器·trae
yosh'joy!!13 小时前
下载Trae使用
ai·trae
豆包MarsCode1 天前
只需一个指令,让 OpenClaw 安排 TRAE 干活
trae
sugar15691 天前
Trae快速构建自己项目的docker镜像
docker·容器·trae
sugar15691 天前
Trae 添加项目规则,快速完成crmeb项目本地开发环境搭建
docker·容器·trae
欧简墨2 天前
kotlin Android Extensions插件迁移到viewbinding总结
android·trae
arbboter2 天前
【AI编程】约束即设计:AI时代的人机边界重构
ai编程·ai工作流·人机协作·trae·声明式执行·流程编排
进击的雷神4 天前
Trae AI IDE 完全指南:从入门到精通
大数据·ide·人工智能·trae
圣殿骑士-Khtangc5 天前
Trae IDE AI 编程超全使用教程|从入门到精通,解锁 AI 开发新效率
ide·人工智能·ai编程·编程助手·trae