Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nodejs] add bun backend #101

Merged
merged 9 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/backends/backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
var languageBackends = []api.LanguageBackend{
python.Python3Backend,
python.Python2Backend,
nodejs.BunBackend,
nodejs.NodejsNPMBackend,
nodejs.NodejsYarnBackend,
ruby.RubyBackend,
Expand Down
64 changes: 64 additions & 0 deletions internal/backends/nodejs/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"net/url"
"os"
"os/exec"
"regexp"

"github.com/hashicorp/go-version"
Expand Down Expand Up @@ -369,3 +370,66 @@ var NodejsNPMBackend = api.LanguageBackend{
GuessRegexps: nodejsGuessRegexps,
Guess: nodejsGuess,
}

// BunBackend is a UPM backend for Node.js that uses Yarn.
var BunBackend = api.LanguageBackend{
Name: "bun",
Specfile: "package.json",
Lockfile: "bun.lockb",
FilenamePatterns: nodejsPatterns,
Quirks: api.QuirksAddRemoveAlsoLocks |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

additional env vars that might be helpful:

  • BUN_CONFIG_SKIP_SAVE_LOCKFILE
  • BUN_CONFIG_SKIP_LOAD_LOCKFILE
  • BUN_CONFIG_SKIP_INSTALL_PACKAGES
  • BUN_INSTALL_VERBOSE
  • GOMAXPROCS (control max number of threads used, not in go but didn't want to make up a new env var for this)
  • BUN_MANIFEST_CACHE=0: no cache
  • BUN_MANIFEST_CACHE=1: read the cache, but ignore Cache-Control headers
  • BUN_MANIFEST_CACHE=2: read the cache & follow Cache-Control headers (default)
  • BUN_INSTALL_CACHE_DIR
  • BUN_INSTALL (directory bun was installed in, not bun install)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also bun pm ls --all could be used to print out the installed package order in node_modules (due to hoisting, will often contain duplicate versions)

image

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@cdmistman cdmistman Apr 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

due to hoisting, will often contain duplicate versions

That's fine, right now UPM only accepts one version per package, which is problematic anyways. The package version that's last in the output will be the one that's reported by UPM

So there seems to be 2 ways of reading the packages and versions in the bun lockfile:

  • executing the bun.lockb file
    • this is what's done by UPM in this PR
    • is there a way to ensure that the output format is yarn's from the command line, ie without relying on the bunfig.toml file?
  • via bun pm ls --all
    • I can probably switch to this, and that would be preferred in my opinion
    • is the output format considered stable? ie, if I write a regex for reading the package name and version eg (@[a-z]+\/)?[a-z]+@[0-9]+\.[0-9]+\.[0-9]+ is there any possibility that the regex will break in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those env vars are definitely helpful for some other things we have planned with bun, thank you!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bun pm hash-string might be easier for parsing and we cannot change it because doing so would invalidate lockfiles unexpectedly (we use that to determine if we should bother saving the lockfile)

❯ bun pm hash-string

-- BEGIN SHA512/256(`${alphabetize(name)}@${order(version)}`) --
@eslint/[email protected]
@humanwhocodes/[email protected]
@humanwhocodes/[email protected]
@humanwhocodes/[email protected]
@nodelib/[email protected]
@nodelib/[email protected]
@nodelib/[email protected]
@types/[email protected]
@types/[email protected]
@types/[email protected]
@types/[email protected]
@types/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
@typescript-eslint/[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
bun-webkit-linux-amd64@0.0.1-595dd33692e02001631f6c3ab6f2bbb199278d26
bun-webkit-linux-arm64@0.0.1-595dd33692e02001631f6c3ab6f2bbb199278d26
bun-webkit-macos-amd64@0.0.1-595dd33692e02001631f6c3ab6f2bbb199278d26
bun-webkit-macos-arm64@0.0.1-595dd33692e02001631f6c3ab6f2bbb199278d26
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
-- END HASH--

We probably should just add a way to print this info as JSON though.

Also you should set COLORTERM=0 to ensure ansi color codes don't get included in output. It should be smart enough to detect this, but good to proactively disable that

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though bun pm hash-string returns all packages in the lockfile, but not all these packages will ultimately install (bun-webkit-linux-arm64, for example, is not installed on linux x64 but exists in the lockfile so that the lockfile is consistent across operating systems and CPU architectures)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, it could be a future feature for UPM to detect platform-specific dependencies and filter/collate them, but that's not a large concern at the moment. Seems bun pm hash-string is the best option here - thanks!

api.QuirksAddRemoveAlsoInstalls |
api.QuirksLockAlsoInstalls,
GetPackageDir: func() string {
return "node_modules"
},
Search: nodejsSearch,
Info: nodejsInfo,
Add: func(pkgs map[api.PkgName]api.PkgSpec, projectName string) {
if !util.Exists("package.json") {
util.RunCmd([]string{"bun", "init", "-y"})
}
cmd := []string{"bun", "add"}
for name, spec := range pkgs {
arg := string(name)
if spec != "" {
arg += "@" + string(spec)
}
cmd = append(cmd, arg)
}
util.RunCmd(cmd)
},
Remove: func(pkgs map[api.PkgName]bool) {
cmd := []string{"bun", "remove"}
for name := range pkgs {
cmd = append(cmd, string(name))
}
util.RunCmd(cmd)
},
Lock: func() {
util.RunCmd([]string{"bun", "install"})
},
Install: func() {
util.RunCmd([]string{"bun", "install"})
},
ListSpecfile: nodejsListSpecfile,
ListLockfile: func() map[api.PkgName]api.PkgVersion {
hashString, err := exec.Command("bun", "pm", "hash-string").Output()
if err != nil {
util.Die("bun pm hash-string: %s", err)
}

r := regexp.MustCompile(`(?m)^(@?[^@ \n]+)@(.+)$`)
pkgs := map[api.PkgName]api.PkgVersion{}

for _, match := range r.FindAllStringSubmatch(string(hashString), -1) {
name := api.PkgName(match[1])
pkgs[name] = api.PkgVersion(match[2])
}

return pkgs
},
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
GuessRegexps: nodejsGuessRegexps,
Guess: nodejsGuess,
}