Esbuild 调试 Golang 进程 和 NodeJs 通信 小白教程

背景

在 esbuild 中有很多同学,都进行了nodejs 插件的开发,但是具体怎么通过 nodejs 进程 通信到 golang 中呢?通过这篇文章能让你对 esbuild 插件源码有一个 200% 的了解。

Esbuild 隐藏的秘密

  • 隐藏在 issue 中 esbuild 二次开发特性

重新编译方法 ·installForTests·

installForTests示例
import {loadPlugin, resolvePlugin} from '../plugin'
import fs from 'fs';
import path from 'path';

// esbuild 源码中 目录 esbuild/scripts/esbuild.js
const esbuild = require('../../scripts/esbuild').installForTests()
// const esbuild = require('../../npm/esbuild/lib/main')

async function exec() {

  let res = await esbuild.build({
    entryPoints: ["src/index.tsx"],
    outfile: "dist/main.js",
    minify: false,
    bundle: true,
    // watch: true,
    incremental: true,
    sourcemap: true,
    plugins: [loadPlugin, resolvePlugin]
  });
}
installForTests
exports.installForTests = () => {
  // Build the "esbuild" binary and library
  const esbuildPath = exports.buildBinary()
  buildNeutralLib(esbuildPath)
  ...
}
buildBinarybuildBinarygithub/esbuild/scripts/esbuild.jsline 298
// 目前代码 不支持 gops 附加调试
childProcess.execFileSync('go', ['build', '-gcflags="all=-N -l"', './cmd/esbuild'], { cwd: repoDir, stdio: 'ignore' })

// 注意!!! 建议修改如下 增加 '-ldflags=-s -w' 参数 来支持 gops 调试

childProcess.execFileSync('go', ['build', '-ldflags=-s -w', './cmd/esbuild'], { cwd: repoDir, stdio: 'ignore' })

buildNeutralLibnpm/esbuild/lib/main.jsnpm run buildlib/shared/common.tsnpm/esbuild/lib/main.js
// line 662 这里是 nodejs 发送 streambuffer unit8array 通过 ipc 到 golang 中 核心方法
// 这里的 Req 就是 每个 nodejs 调用 golang 中,参数类型 
// sendRequest<protocol.RebuildRequest, protocol.BuildResponse> 发送重新构建 示例。
// 该方法 是一个 镜像方法 在golang 中 回调 nodejs 中有同样的实现。
 let sendRequest = <Req, Res>(
    refs: Refs | null,
    value: Req,
    callback: (error: string | null, response: Res | null) => void
  ): void => {
    ....
  }

rebuild 参数如下,esbuild 命令类型 “command” 执行 task 时,esbuild 会有个 'id' key 来标识多线程中 构建结果。

export interface RebuildRequest {
  command: "rebuild";
  key: number;
  //changefile: string[];
}

重定向 字节码文件的 环境变量 ESBUILD_BINARY_PATH

checkAndPreparePackagevalidateBinaryVersion

使用示例如下

{
  "scripts": {
		"build": "ESBUILD_BINARY_PATH=/Users/xxx/Desktop/github/esbuild/esbuild ts-node-dev ./build/index.ts"
	},
}

如何附加调试 esbuild 进程,包含(vscode,goland)

installForTests-ldflags=-s -wwatch: trueincremental: truedlv
$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/dlv
dlv
➜  ~ dlv
Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.

Pass flags to the program you are debugging using `--`, for example:

`dlv exec ./hello -- server --config conf/config.toml`

Usage:
  dlv [command]
  1. vscode 中 创建 .vscode/launch.json 并且填写如下内容
$: ps -ef|grep esbuild
501 72470 72428   0  6:58下午 ttys000    0:00.09 /Users/xxx/Desktop/github/esbuild/esbuild --service=0.14.25 --ping
第二个数字PID
internal/bundler/bundler.gofunc runOnLoadPlugins
vscode golang extension 扩展中已经自动帮你安装了 gops 附加工具,调试的时候自动启动,使用vsocde 同学可以自动忽略!不需要了解 gops 是何种工具,只要保证第5步 中 delve 成功安装即可
  1. Goland 附加调试
internal/bundler/bundler.gointernal/bundler/bundler.gofunc runOnLoadPlugins
go version