Esbuild: Is there a way to install the tool using go get?

Created on 16 Apr 2020  Â·  12Comments  Â·  Source: evanw/esbuild

It would be good to install it using simple go get instead of installing it as npm package.
With this service,(https://gobinaries.com/) you can get binary without golang at all

Most helpful comment

Well, I was too naive to say, that just moving go.mod and go.sub would do. I inspected the project sources and [made some changes] to get a prototype running. They were pure restructuring. No code changes except for renaming the internal imports were needed.

  • Move go.mod and go.sub to the project root.
  • Name the module github.com/evanw/esbuild instead of esbuild.
  • Move the main package from /src/esbuild/main to /cmd/esbuild.
  • Move all other packages to /internal. Some of them can be made public later.
  • Import packages by github.com/evanw/esbuild/internal/... instead of esbuild/....
  • Adapt Makefile to work with the new structure.

In the end I was able to successfully build and test the package. When I tested it externally, I had to rename the package in my fork, but if you imagine evanw instead of prantlf there, you will get the idea :-)

go get -u github.com/prantlf/esbuild/...@308fdf459d65

I kept the change in a [separate branch]. Would you be interested in a PR? Of course, that that branch will work only with your [change d71e126], which I forked. I am not sure if rebase on a newer state of your repository will work with so many moved files. If you were interested, I would make the changes again to your latest sources, whenever you were ready.

All 12 comments

Adding a go.mod file is a step in the right direction, but unfortunately go get is not yet supported due to the project's structure.

$ go get github.com/evanw/esbuild
can't load package: package github.com/evanw/esbuild: no Go files in /Users/tooolbox/go/src/github.com/evanw/esbuild

That makes sense because all of the source files are contained in src. Go projects sometimes have a a cmd folder that contains the actual main package that you go get so let's try that:

$ go get github.com/evanw/esbuild/src/esbuild/main
unrecognized import path "esbuild/ast": import path does not begin with hostname
unrecognized import path "esbuild/bundler": import path does not begin with hostname
unrecognized import path "esbuild/fs": import path does not begin with hostname
unrecognized import path "esbuild/lexer": import path does not begin with hostname
unrecognized import path "esbuild/logging": import path does not begin with hostname
unrecognized import path "esbuild/parser": import path does not begin with hostname
unrecognized import path "esbuild/resolver": import path does not begin with hostname

(Just checking...)

$ go get github.com/evanw/esbuild/src/esbuild                                                                                                                                                                
can't load package: package github.com/evanw/esbuild/src/esbuild: no Go files in /Users/tooolbox/go/src/github.com/evanw/esbuild/src/esbuild

Unfortunately, Go Modules has what you might call a lack of support for import paths that do not begin with a domain (the Go Team wants to reserve such domain-less imports for the standard library). The first segment of the import path has to have a . in it or else most operations fail. The sole exception seems to be go build.

Thus, while esbuild has started to use Go Modules 🎉 the best way to enable go get and similar goodies is to change to a github.com/evanw/esbuild/... import path.

Note that because replace directives in go.mod files are not supported for go get and similar operations, this is not a matter of dropping in a go.mod or two and everything Just Worksâ„¢ as before. The actual import paths/project structure would need to be changed.

There are a couple of examples available of how to do this. Note that if the author is concerned about folks using the code as a library, it can be placed in a directory called internal and no other Go code can import it.


Appendix:

$ GO111MODULE=on go get github.com/evanw/esbuild/src/esbuild/main                                                                                                                                            
go: downloading github.com/evanw/esbuild v0.0.0-20200416222118-5edc934e7a09
go: downloading github.com/evanw/esbuild/src/esbuild v0.0.0-20200416222118-5edc934e7a09
go: found github.com/evanw/esbuild/src/esbuild/main in github.com/evanw/esbuild/src/esbuild v0.0.0-20200416222118-5edc934e7a09
go get: github.com/evanw/esbuild/src/[email protected]: parsing go.mod:
    module declares its path as: esbuild
            but was required as: github.com/evanw/esbuild/src/esbuild
$ GO111MODULE=on go get github.com/evanw/esbuild/src/esbuild
go: github.com/evanw/esbuild/src/esbuild upgrade => v0.0.0-20200416222118-5edc934e7a09
go get: github.com/evanw/esbuild/src/[email protected]: parsing go.mod:
    module declares its path as: esbuild
            but was required as: github.com/evanw/esbuild/src/esbuild



md5-ad46d2090d8bdc219f2f2e5a291aeffe



$ GO111MODULE=on go get github.com/evanw/esbuild                                                                                                                                                              
go: github.com/evanw/esbuild upgrade => v0.0.0-20200416222118-5edc934e7a09
# Doesn't actually build a binary

Having this available as a Go tool would be a real boon. Lack of speed isn't the only issue with JavaScript build systems. It would be great to be able to reduce JS dependencies to only those required by the actual project. Currently, those account for about 1% of the stuff in most of my node_modules directories.

I wrote a web browser application using Go to run a HTTP server and supply a REST API. It would be great being able to use Go for bundling the static assets too. I would not need Node.js as a build tool to at all. Just Go :-)

@evanw, how about moving go.mod and go.sub to the root of the Github repository, so that it would be recognized as a Go module? Renaming src to cmd might not be necessary:

$ go get -u github.com/evanw/esbuild/tree/master/src/esbuild/...
go get github.com/evanw/esbuild/tree/master/src/esbuild/...:
  module github.com/evanw/esbuild@upgrade found (v0.0.0-20200506071632-d71e12639799),
  but does not contain packages matching github.com/evanw/esbuild/tree/master/src/esbuild/...

$ go get -u github.com/evanw/esbuild/src/esbuild/...
go: downloading github.com/evanw/esbuild/src/esbuild v0.0.0-20200506071632-d71e12639799
go: found github.com/evanw/esbuild/src/esbuild/...
      in github.com/evanw/esbuild/src/esbuild v0.0.0-20200506071632-d71e12639799
go get: github.com/evanw/esbuild/src/[email protected]:
  parsing go.mod:
    module declares its path as: esbuild
    but was required as: github.com/evanw/esbuild/src/esbuild

$ go get -u github.com/evanw/esbuild/...
go get github.com/evanw/esbuild/...:
  module github.com/evanw/esbuild@upgrade found (v0.0.0-20200506071632-d71e12639799),
  but does not contain packages matching github.com/evanw/esbuild/...

Well, I was too naive to say, that just moving go.mod and go.sub would do. I inspected the project sources and [made some changes] to get a prototype running. They were pure restructuring. No code changes except for renaming the internal imports were needed.

  • Move go.mod and go.sub to the project root.
  • Name the module github.com/evanw/esbuild instead of esbuild.
  • Move the main package from /src/esbuild/main to /cmd/esbuild.
  • Move all other packages to /internal. Some of them can be made public later.
  • Import packages by github.com/evanw/esbuild/internal/... instead of esbuild/....
  • Adapt Makefile to work with the new structure.

In the end I was able to successfully build and test the package. When I tested it externally, I had to rename the package in my fork, but if you imagine evanw instead of prantlf there, you will get the idea :-)

go get -u github.com/prantlf/esbuild/...@308fdf459d65

I kept the change in a [separate branch]. Would you be interested in a PR? Of course, that that branch will work only with your [change d71e126], which I forked. I am not sure if rebase on a newer state of your repository will work with so many moved files. If you were interested, I would make the changes again to your latest sources, whenever you were ready.

Ok I attempted to do this. Can someone confirm if this works? I'm not familiar with Go modules, so I'm not sure what to test.

Can someone confirm if this works?

It does. You may need to run GO111MODULE=on go get -u github.com/prantlf/esbuild/...@308fdf459d65 to install it if you're getting the error go: cannot use path@version syntax in GOPATH mode

Thanks for confirming! I'll consider this issue fixed then.

As an additional data point, I was also able to successfully download an esbuild executable from https://gobinaries.com. It appeared to be around the correct size. I didn't run it though.

Thank you, @evanw, it works perfectly! When running in a Go module directory using Go 1.14, the binary esbuild gets installed and put to PATH (actually, GOBIN):

$ go get -u github.com/evanw/esbuild/...
go: downloading github.com/evanw/esbuild v0.0.0-20200507102806-766e48876293
go: found github.com/evanw/esbuild/...
      in github.com/evanw/esbuild v0.0.0-20200507102806-766e48876293
go: golang.org/x/sys upgrade => v0.0.0-20200501145240-bc7a7d42d5c3
$ which esbuild
/Users/prantlf/go/bin/esbuild

Beware of git tags, though. You have not tagged any commit in your repository yet. That is why if I run go get -u ... everyday, I will be able to enjoy your latest changes everyday (if I wanted) :-) As soon as you decide to tag your commits to match your releases, Go will use the latest of them according to the semantic versioning concept. It means that if you do not tag the commit which you want to release, Go users will not get an upgrade offered.

You can work without tags on the road to MVP. If somebody wants to stay with a particular change, they can refer to it by its commit hash, when they install the tool. For example:

go get -u github.com/evanw/esbuild/...@766e48876293

As soon as you decide to start tagging your commits, people who install esbuild by go get will get the latest release, not the latest master, just like NPM users do today. Once you decide, I recommend you the usual semantic versioning format used by both Go and NPM packages. For example, your current version that you released to NPM would be tagged byv0.2.8.

Thanks again for the quick implementation!

About downloading binaries - you can have a look at GoReleaser too. If you write a goreleaser.yml, tag a commit for a new release and run .goreleaser --rm-dist. It will build binaries for all platforms, upload them to GitHub and generate a changelog entry with hyperlinks. This is more for the future, when esbuild gets more stable.

By the way, NPM releases can be automated in a similar way with semantic-release, if you were interested. Basically, you prefix your commits with chore: (not versioning change), fix: (patch version), feat: (minor version) or include BREAKING CHANGE: in the commit description (major version) and run npx semantic-release, when you are ready to push all changes. It will bump the version properly and publish your module at npmjs.org.

Both go-release and semantic-release can be run in CI pipelines like Travis, so that you do not need to do the "paperwork" yourself.

Stupendous; can confirm this works.

@evanw It would be nice, when you do a version bump on NPM, to add a git tag for the same version.

I'll add tags for future releases. I want esbuild's command-line tool to be usable for real things, so I don't think downloading master is an appropriate default.

Unfortunately semantic versioning doesn't really apply before 1.0.0. The semver docs say this:

Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

However, during the MVP phase I'd like to be a bit more rigorous about this. I'm planning to use patch releases for backwards-compatible changes and bug fixes, and minor releases for either major features or backwards-incompatible changes. So if you stick to just patch releases than the command-line interface should remain pretty stable.

I'll add tags for future releases.

Awesome, thanks.

Unfortunately semantic versioning doesn't really apply before 1.0.0.

That's fine, users of Go Modules will understand that things may break before 1.0.

From https://blog.golang.org/v2-go-modules:

For projects that are still experimental — at major version v0 — occasional breaking changes are expected by users. For projects which are declared stable — at major version v1 or higher — breaking changes must be done in a new major version.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mohsen1 picture mohsen1  Â·  3Comments

tonyhb picture tonyhb  Â·  3Comments

egoist picture egoist  Â·  3Comments

fannheyward picture fannheyward  Â·  4Comments

a7ul picture a7ul  Â·  3Comments