I recently published a Go binary to NPM, similar to how esbuild is a go binary that's also published to NPM. When I finally understood how publishing NPM packages worked (on a surface level), I had this... uncanny feeling on how much flexibility NPM provides to anyone who wants to publish a package.
It ultimately allows the user to upload a directory of whatever they would like and write an arbitary javascript code that is allowed to run on install.
The first question may be why I'm bothering to try this in the first place? I
hacked on a weekend project to spite
scratch an itch on how much work it would take to replace
pm2 for some basic process "management" for
local dev purposes. I wanted to maintain feature parity for things I cared
about, one of which was ease of installation for JS devs.
All that was really required to publish an NPM package is to have a directory with
a package.json
file, stick whatever else you'd like in that directory, and use
their CLI tool to publish it to their registry. To break down the full
requirements of publishing Go to NPM, it is:
Esbuild goes one step further that this and leverages the optionalDependencies
feature of NPM to only download the go binary for the exact OS/Arch of the
requesting system. I've opted for the dumb and simple route of just publishing
all the go binaries in the same package.
I started with a completely bottoms up approach because I knew nothing about publishing NPM modules going into this project. But as a result, I think what I ended up with is the most simple process you could have (though it's not automated and not really reproducible the way it's setup today).
First, I have a dedicated npm
directory that represents the skeleton of the
NPM package that will ultimately be published. The things that matter are:
These bits in the package.json
:
"scripts": {
"postinstall": "node install.js"
},
"bin": {
"gopm3": "src/index.js"
},
The postinstall
step runs a script to determine which go binary we need to
actually install and delete the rest.
The bin
specifies the entrypoint of the program which is a JS shim to
execute the go binary as a child process.
There is a dumb shell script that builds the go binaries for all the OS/Arch
pairs that we care about and places them in the NPM dir where the
postinstall
script expects them. It then templates out a package.json
just
to update the version. To finish, we tag the git repo and call the npm
CLI
to publish the npm
directory to their registry.
Feel free to check out the project repo to see the full picture! (And the npm package itself)