{"id":18137932,"url":"https://github.com/shaobeichen/grprogress","last_synced_at":"2025-08-21T21:31:58.682Z","repository":{"id":258461690,"uuid":"872771676","full_name":"shaobeichen/grprogress","owner":"shaobeichen","description":"⏳ Gradient progress for Nodejs by Go. 使用 Go 来实现 Nodejs 的渐变色进度条","archived":false,"fork":false,"pushed_at":"2024-11-19T01:11:55.000Z","size":8471,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-19T03:46:37.273Z","etag":null,"topics":["bubbletea","cli","esbuild","go","golang","gradient","grprogress","napi","node","nodejs","npm","progress","rspack","rust"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shaobeichen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-15T03:39:20.000Z","updated_at":"2024-12-11T08:34:13.000Z","dependencies_parsed_at":"2024-11-07T03:18:51.835Z","dependency_job_id":"7c1bf0fa-82ab-4217-97b7-ab7b61dc7f47","html_url":"https://github.com/shaobeichen/grprogress","commit_stats":null,"previous_names":["shaobeichen/grprogress"],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaobeichen%2Fgrprogress","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaobeichen%2Fgrprogress/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaobeichen%2Fgrprogress/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaobeichen%2Fgrprogress/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shaobeichen","download_url":"https://codeload.github.com/shaobeichen/grprogress/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230533718,"owners_count":18241025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bubbletea","cli","esbuild","go","golang","gradient","grprogress","napi","node","nodejs","npm","progress","rspack","rust"],"created_at":"2024-11-01T15:07:43.130Z","updated_at":"2024-12-20T04:26:47.724Z","avatar_url":"https://github.com/shaobeichen.png","language":"JavaScript","readme":"# grprogress\n\n一个使用 Go 来实现 Nodejs 的渐变色进度条\n\n\u003ca href=\"\" target=\"__blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/node/v/grprogress?style=flat-square\" alt=\"\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/grprogress\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/grprogress?style=flat-square\" alt=\"\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/grprogress\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/grprogress?style=flat-square\" alt=\"\"\u003e\n\u003c/a\u003e\n\u003ca href=\"\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/l/grprogress?style=flat-square\" alt=\"\"\u003e\n\u003c/a\u003e\n\n\u003cbr\u003e\n\u003cbr\u003e\n\u003cimg src=\".//images/demo.gif\" width=\"60%\" alt=\"Example\"\u003e\n\n## 安装\n\n```cmd\nnpm install grprogress\n```\n\n## 使用\n\n```js\nconst grprogress = require('grprogress')\nprogress.update(0.8)\n```\n\n## API\n\n\u003ctable class=\"table table-bordered table-striped\"\u003e\n  \u003cthead\u003e\n  \u003ctr\u003e\n    \u003cth style=\"width: 100px;\"\u003e函数名\u003c/th\u003e\n    \u003cth style=\"width: 100px;\"\u003e参数类型\u003c/th\u003e\n    \u003cth\u003e描述\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eupdate()\u003c/td\u003e\n      \u003ctd\u003enumber\u003c/td\u003e\n      \u003ctd\u003e进度条进度，值在0-1之间。\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n## 原理讲解\n\n### 视频讲解\n\n待补充\n\n### 核心步骤\n\n1. Go 实现渐变色进度条。\n2. Go 交叉编译可以生成不同平台的二进制文件，这种二进制文件在相应平台上可以直接命令行执行查看进度条效果。\n3. Nodejs 通过 child_process 的 spawnSync 调用 Go 的二进制文件，模拟第 2 步骤。\n\n### 为什么 npm 能运行 Go 代码？\n\n其实本质并不是运行 Go 代码，运行的是 Go 编译后的二进制文件，而 Nodejs 只是调用了 Go 的二进制文件而已。\n\n### npm 包里并没有二进制文件，如何运行？\n\n#### 1.主包和平台包分离设计\n\n二进制文件一般会比较大，动辄 3-50 Mb，如果需要兼容很多平台，比如兼容 Windows、MacOS、MacOS M、Linux 等，那么 npm 包的大小就会很大。\n\n使用 npm install 会下载很多其他平台的二进制文件，完全是没必要的，只需要下载当前平台的二进制文件即可。\n\n所以把主包和平台包分离，主包只包含核心代码，平台包包含对应平台的二进制文件，这样 npm install 就会下载当前平台的二进制文件，其他平台的二进制文件不会下载，从而提高下载速度。\n\n所以主包叫 grprogress，平台包的名字按照一定的规则自动生成。比如 @grprogress/win32-x64、@grprogress/darwin-x64、@grprogress/darwin-arm64、@grprogress/linux-x64。\n\n#### 2.生成平台包二进制文件及对应平台 package.json\n\n当你编写好 Go 文件后，可以使用 go build -o grprogress 生成二进制文件，如果要生成多个平台包，可以指定 os 和 cpu 参数。具体逻辑可以参考 [build 文件](./scripts/build.js)，包含了生成二进制文件和 package.json 的逻辑。\n\n在此仓库中 npm 文件夹下有个 package.json.template 文件，是用来生成各个平台包里的 package.json 文件的模板。\n\n- name 根据上面平台包类似的规则生成。\n- version 同步主包的版本。\n- file 是当前平台的二进制文件名称，除了 Windows 的二进制文件会带有.exe，其他平台均不带后缀名，一律叫 grprogress。\n- os 是平台，比如 Windows 平台是 win32、 MacOS 平台是 darwin、Linux 平台是 linux。\n- cpu 是 cpu 架构类型，比如 x64、arm64 等，MacOS M 系列芯片就用 arm64。\n\n当 package.json 中编写了 os 和 cpu 字段，那么在 install 某一个平台包时，它会自动检测当前平台是否与 os 和 cpu 匹配，如果匹配，则安装该平台包，否则不进行安装，并且提醒你当前平台和当前平台包里填写的 os 和 cpu 不匹配。\n\n#### 3.发布主包和平台包\n\n当所有平台包都生成好后，目录结构如下，只需要把主包和所有平台包一起发布到 npm 即可。\n\n```tree\n├─npm\n│  └─win32-x64\n│    └─grprogress.exe\n│    └─package.json // @grprogress/win32-x64 平台包\n│  └─darwin-x64\n│    └─grprogress\n│    └─package.json // @grprogress/darwin-x64 平台包\n└─package.json // grprogress 主包\n```\n\n按照常规的 npm 发布流程，在根目录 npm publish，再依次在 npm/win32-x64、...多个平台包目录 npm publish 即可发布成功。\n\n这里借助了 semantic-release 发布主包，因为发布多个包和 monorepo 的发布形式还不相同，所以不能使用 semantic-release-monorepo 插件。\n\n使用了@semantic-release/exec，自己写了一套平台包发布逻辑。具体可以参考 [.releaserc.js](./.releaserc.js)和 [release.js](./scripts/release.js)。\n\nrelease.js 中还包含更新主包 optionalDependencies 中平台包名称及版本号等逻辑，稍后详细解释其作用。\n\n#### 4.安装主包，自动安装对应平台包\n\n我们参考 esbuild 的安装逻辑，在 npm install grprogress 时，会自动安装对应平台的平台包，比如在 Windows 下 npm install grprogress 会自动安装 @grprogress/win32-x64 平台包。\n\n怎样实现自动安装平台包并可执行呢？\n\n1. 在主包的 package.json 中，添加一个 scripts 字段，里面有 postinstall 字段，当使用者使用 npm install 时，就会触发主包中的 postinstall 脚本，这个脚本就是[install.js](./install.js)。\n\n2. install.js 脚本大致逻辑是 先下载平台包，然后移动二进制文件到主包中。\n\n3. 我们参考了 esbuild 的 install.js，分为三级策略。\n\n4. 第一级，会通过`require.resolve(`@grprogress/win32-x64/grprogress.exe`)`自动触发 optionalDependencies 中平台包@grprogress/win32-x64 的安装，如果成功，则直接返回，不再执行第二级和第三级策略。\n\n5. 第二级，如果第一级失败，则在主包安装过程中，创建一个临时文件夹，再执行 npm install @grprogress/win32-x64 安装平台包，然后把平台包中的 grprogress 二进制文件移动到主包中，然后删除临时文件夹。如果成功，则直接返回，不再执行第三级策略。\n\n6. 第三级，如果第二级失败，则通过 npm 压缩包的形式手动使用 https 下载，过程是 下载压缩包 -\u003e 解压压缩包 -\u003e 删除压缩包 -\u003e 移动指定的文件 -\u003e 删除文件夹。使用 https 请求 `https://registry.npmjs.com/@grprogress/win32-x64/-/win32-x64-1.6.0.tgz`，下载好压缩包后，使用 zlib 解压，然后把 grprogress 二进制文件移动到主包中，然后删除临时文件夹。\n\n7. 最终都会在主包中找到 grprogress 二进制文件。\n\n8. 我们封装好了使用 spawn 运行 grprogress 二进制文件的逻辑，此时引入 grprogress，执行 grprogress.update(0.8) 即可在控制台看到渐变色进度条。\n\n这样就达到了 npm 包里并没有二进制文件，安装后可以运行二进制文件的逻辑。\n\n### 其他语言是如何实现的？\n\n我们使用 Go 来实现，其他语言也是类似逻辑，比如目前 Rust 语言来实现 Nodejs 的工具库，例如 rspack，采用的方式也是大同小异。@rspack/core 里依赖了@rspack/binding，@rspack/binding 分发了很多平台包，安装的逻辑在[binding.js](https://www.npmjs.com/package/@rspack/binding?activeTab=code)。\n\n### 没有用到 wasm 吗？\n\n因为 Go 语言本身就支持跨平台，所以不需要用到 wasm，有一种情况需要特殊处理，就是 android，android 需要使用 wasm。\n\n## 参考\n\n[渐变进度条 Go 库 BubbleTea - 非常适合简单和复杂的终端应用](https://github.com/charmbracelet/bubbletea)\n\n## 相关问题\n\n1. 使用 Go 编写的 esbuild 是如何发布到 npm 的？\n2. esbuild 的安装逻辑是什么？\n3. esbuild 支持多平台的思路是什么？\n4. Golang 是怎么编译为 npm 库的？\n5. Go 是怎么编译为 npm 库的？\n6. Go 与 npm 开发是如何结合的？\n7. 使用 Golang 语言编写 Nodejs 扩展的开发工具\n8. 基于 npm 进行跨平台分发 Golang 二进制程序\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaobeichen%2Fgrprogress","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshaobeichen%2Fgrprogress","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaobeichen%2Fgrprogress/lists"}