diff --git a/.prettierignore b/.prettierignore index 5f1f61b1..d85a8469 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ +**/fixtures/bun/*.json5 **/fixtures/pnpm/*.yaml **/fixtures/**/invalid.json **/fixtures/configs-invalid/*.yaml diff --git a/README.md b/README.md index a957619f..a6d4ccaa 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ The detector supports parsing the following lockfiles: | `packages.lock.json` | `NuGet` | `dotnet` | | `package-lock.json` | `npm` | `npm` | | `yarn.lock` | `npm` | `yarn` | +| `bun.lock` | `npm` | `bun` | | `pnpm-lock.yaml` | `npm` | `pnpm` | | `composer.lock` | `Packagist` | `composer` | | `Gemfile.lock` | `RubyGems` | `bundler` | diff --git a/go.mod b/go.mod index ae83d2b2..fe2740b3 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/BurntSushi/toml v1.3.2 github.com/fatih/color v1.16.0 github.com/google/go-cmp v0.6.0 + github.com/tidwall/jsonc v0.3.2 golang.org/x/mod v0.14.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 4edf1962..cd0b575d 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/tidwall/jsonc v0.3.2 h1:ZTKrmejRlAJYdn0kcaFqRAKlxxFIC21pYq8vLa4p2Wc= +github.com/tidwall/jsonc v0.3.2/go.mod h1:dw+3CIxqHi+t8eFSpzzMlcVYxKp08UP5CD8/uSFCyJE= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main_test.go b/main_test.go index b237886d..a771962c 100644 --- a/main_test.go +++ b/main_test.go @@ -158,6 +158,7 @@ func TestRun(t *testing.T) { wantStderr: ` Don't know how to parse files as "my-file" - supported values are: buildscript-gradle.lockfile + bun.lock Cargo.lock composer.lock Gemfile.lock diff --git a/pkg/lockfile/ecosystems_test.go b/pkg/lockfile/ecosystems_test.go index 8f2b4d11..8da09b90 100644 --- a/pkg/lockfile/ecosystems_test.go +++ b/pkg/lockfile/ecosystems_test.go @@ -34,11 +34,11 @@ func TestKnownEcosystems(t *testing.T) { expectedCount := numberOfLockfileParsers(t) - // - npm, yarn, and pnpm, + // - npm, yarn, bun, and pnpm, // - pip, poetry, pdm and pipenv, // - maven and gradle, // all use the same ecosystem so "ignore" those parsers in the count - expectedCount -= 6 + expectedCount -= 7 ecosystems := lockfile.KnownEcosystems() diff --git a/pkg/lockfile/fixtures/bun/alias.json5 b/pkg/lockfile/fixtures/bun/alias.json5 new file mode 100644 index 00000000..0e761cd8 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/alias.json5 @@ -0,0 +1,21 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "supports-color": "^7.0.0", + "supports-color-old": "npm:supports-color@^6.0.0", + }, + }, + }, + "packages": { + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-color-old": ["supports-color@6.1.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ=="], + + "supports-color-old/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/bad-tuple.json5 b/pkg/lockfile/fixtures/bun/bad-tuple.json5 new file mode 100644 index 00000000..0edddabb --- /dev/null +++ b/pkg/lockfile/fixtures/bun/bad-tuple.json5 @@ -0,0 +1,16 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "wrappy": "^1.0.0", + }, + }, + }, + "packages": { + "wrappy-bad1": [123, "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "wrappy-bad2": [{}, "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/blog-sample.json5 b/pkg/lockfile/fixtures/bun/blog-sample.json5 new file mode 100644 index 00000000..058c443b --- /dev/null +++ b/pkg/lockfile/fixtures/bun/blog-sample.json5 @@ -0,0 +1,13 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "uWebSocket.js": "uNetworking/uWebSockets.js#v20.51.0", + }, + }, + }, + "packages": { + "uWebSocket.js": ["uWebSockets.js@github:uNetworking/uWebSockets.js#6609a88", {}, "uNetworking-uWebSockets.js-6609a88"], + } +} diff --git a/pkg/lockfile/fixtures/bun/commits.json5 b/pkg/lockfile/fixtures/bun/commits.json5 new file mode 100644 index 00000000..4e3c6c55 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/commits.json5 @@ -0,0 +1,58 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "lodash": "^1.2.1", + "stopwords": "0.0.1", + }, + "devDependencies": { + "@prettier/sync": "github:prettier/prettier-synchronized", + "babel-preset-php": "gitlab:kornelski/babel-preset-php#master", + "is-number-1": "https://github.com/jonschlinkert/is-number.git", + "is-number-2": "https://github.com/jonschlinkert/is-number.git#d5ac058", + "is-number-3": "https://github.com/jonschlinkert/is-number.git#2.0.0", + "raven-js": "getsentry/raven-js#3.23.1", + "slick-carousel": "git://github.com/brianfryer/slick", + }, + }, + }, + "packages": { + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@babel/parser": ["@babel/parser@7.26.5", "", { "dependencies": { "@babel/types": "^7.26.5" }, "bin": "./bin/babel-parser.js" }, "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw=="], + + "@babel/types": ["@babel/types@7.26.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg=="], + + "@prettier/sync": ["@prettier/sync@github:prettier/prettier-synchronized#527e8ce", { "dependencies": { "make-synchronized": "^0.2.8" }, "peerDependencies": { "prettier": "*" } }, "prettier-prettier-synchronized-527e8ce"], + + "babel-preset-php": ["babel-preset-php@git+ssh://gitlab:kornelski/babel-preset-php#1ae6dc1267500360b411ec711b8aeac8c68b2246", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.3", "@babel/parser": "^7.8.4", "php-parser": "^2.2.0" } }, "1ae6dc1267500360b411ec711b8aeac8c68b2246"], + + "is-number-1": ["is-number@github:jonschlinkert/is-number#98e8ff1", {}, "jonschlinkert-is-number-98e8ff1"], + + "is-number-2": ["is-number@github:jonschlinkert/is-number#d5ac058", {}, "jonschlinkert-is-number-d5ac058"], + + "is-number-3": ["is-number@github:jonschlinkert/is-number#b7aef34", {}, "jonschlinkert-is-number-b7aef34"], + + "jquery": ["jquery@3.7.1", "", {}, "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="], + + "lodash": ["lodash@1.3.1", "", {}, "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw=="], + + "make-synchronized": ["make-synchronized@0.2.9", "", {}, "sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA=="], + + "php-parser": ["php-parser@2.2.0", "", {}, "sha512-zwPZ5fN7Bw+6Od1EMC8JEON/F/nRDEwIGxFoFsssU+c8aTIlu+13JjmnXsNt+Cnsqn8A7Lvy3O8MMGAlsMO8jA=="], + + "prettier": ["prettier@3.4.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ=="], + + "raven-js": ["raven-js@github:getsentry/raven-js#91ef2d4", {}, "getsentry-sentry-javascript-91ef2d4"], + + "slick-carousel": ["slick-carousel@github:brianfryer/slick#fc6f7d8", { "peerDependencies": { "jquery": ">=1.8.0" } }, "brianfryer-slick-fc6f7d8"], + + "stopwords": ["stopwords@0.0.1", "", {}, "sha512-7YdkaV8Z9eg5zEz6/z4B7Nm3e1nQuOxAW6V/k+mzdVAuxNfBo9PgfXTlQ1iwBPEwrfY0rI2cfVcNkBxhgj05ZA=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/empty.json5 b/pkg/lockfile/fixtures/bun/empty.json5 new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/empty.json5 @@ -0,0 +1 @@ +{} diff --git a/pkg/lockfile/fixtures/bun/files.json5 b/pkg/lockfile/fixtures/bun/files.json5 new file mode 100644 index 00000000..b4dd016c --- /dev/null +++ b/pkg/lockfile/fixtures/bun/files.json5 @@ -0,0 +1,19 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "lodash": "^1.2.1", + }, + "devDependencies": { + "etag": "file:./deps/etag", + }, + }, + }, + "packages": { + "etag": ["etag@file:deps/etag", {}], + + "lodash": ["lodash@1.3.1", "", {}, "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/nested-dependencies-dup.json5 b/pkg/lockfile/fixtures/bun/nested-dependencies-dup.json5 new file mode 100644 index 00000000..953d23ce --- /dev/null +++ b/pkg/lockfile/fixtures/bun/nested-dependencies-dup.json5 @@ -0,0 +1,30 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "has-flag": "^2.0.0", + "supports-color": "^7.0.0", + }, + "devDependencies": { + "chalk": "^4.0.0", + }, + }, + }, + "packages": { + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "has-flag": ["has-flag@2.0.0", "", {}, "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/nested-dependencies.json5 b/pkg/lockfile/fixtures/bun/nested-dependencies.json5 new file mode 100644 index 00000000..d2088f62 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/nested-dependencies.json5 @@ -0,0 +1,34 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0", + }, + "devDependencies": { + "chalk": "^4.0.0", + }, + }, + }, + "packages": { + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "has-flag": ["has-flag@2.0.0", "", {}, "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng=="], + + "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + + "chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/no-packages.json5 b/pkg/lockfile/fixtures/bun/no-packages.json5 new file mode 100644 index 00000000..cf6b5031 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/no-packages.json5 @@ -0,0 +1,6 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": {}, + }, +} diff --git a/pkg/lockfile/fixtures/bun/not-json.txt b/pkg/lockfile/fixtures/bun/not-json.txt new file mode 100644 index 00000000..3ae3a213 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/not-json.txt @@ -0,0 +1 @@ +this is not json! diff --git a/pkg/lockfile/fixtures/bun/one-package-dev.json5 b/pkg/lockfile/fixtures/bun/one-package-dev.json5 new file mode 100644 index 00000000..a97cf3f8 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/one-package-dev.json5 @@ -0,0 +1,14 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "wrappy": "^1.0.0", + }, + }, + }, + "packages": { + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/one-package.json5 b/pkg/lockfile/fixtures/bun/one-package.json5 new file mode 100644 index 00000000..a97cf3f8 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/one-package.json5 @@ -0,0 +1,14 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "wrappy": "^1.0.0", + }, + }, + }, + "packages": { + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/optional-package.json5 b/pkg/lockfile/fixtures/bun/optional-package.json5 new file mode 100644 index 00000000..4f3e26f4 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/optional-package.json5 @@ -0,0 +1,21 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "acorn": "*", + }, + "optionalDependencies": { + "fsevents": "0.3.8", + }, + }, + }, + "packages": { + "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + + "fsevents": ["fsevents@0.3.8", "", { "dependencies": { "nan": "^2.0.2" }, "os": "darwin" }, "sha512-3vlmn1QaPoqSnhnorLFlp3+r3dUCZ8eZlaew+H8QhqB+0YBc9HSITh9wiZo76KYYExTC9DwG6otE/OzwbBLVIw=="], + + "nan": ["nan@2.22.0", "", {}, "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/peer-dependencies-explicit.json5 b/pkg/lockfile/fixtures/bun/peer-dependencies-explicit.json5 new file mode 100644 index 00000000..4a780282 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/peer-dependencies-explicit.json5 @@ -0,0 +1,17 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "acorn": "*", + "acorn-jsx": "*", + }, + }, + }, + "packages": { + "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/peer-dependencies-implicit.json5 b/pkg/lockfile/fixtures/bun/peer-dependencies-implicit.json5 new file mode 100644 index 00000000..6fd00ae0 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/peer-dependencies-implicit.json5 @@ -0,0 +1,16 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "acorn-jsx": "*", + }, + }, + }, + "packages": { + "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/same-package-different-groups.json5 b/pkg/lockfile/fixtures/bun/same-package-different-groups.json5 new file mode 100644 index 00000000..f98dc049 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/same-package-different-groups.json5 @@ -0,0 +1,19 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "supports-color": "^5.0.0", + }, + "devDependencies": { + "has-flag": "^3.0.0", + }, + }, + }, + "packages": { + "has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + + "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/scoped-packages-mixed.json5 b/pkg/lockfile/fixtures/bun/scoped-packages-mixed.json5 new file mode 100644 index 00000000..438dbe83 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/scoped-packages-mixed.json5 @@ -0,0 +1,25 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "@babel/code-frame": "^7.0.0", + }, + "devDependencies": { + "wrappy": "^1.0.0", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/scoped-packages.json5 b/pkg/lockfile/fixtures/bun/scoped-packages.json5 new file mode 100644 index 00000000..c2a8ea09 --- /dev/null +++ b/pkg/lockfile/fixtures/bun/scoped-packages.json5 @@ -0,0 +1,14 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "@typescript-eslint/types": "^5.0.0", + }, + }, + }, + "packages": { + "@typescript-eslint/types": ["@typescript-eslint/types@5.62.0", "", {}, "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ=="], + } +} diff --git a/pkg/lockfile/fixtures/bun/two-packages.json5 b/pkg/lockfile/fixtures/bun/two-packages.json5 new file mode 100644 index 00000000..1472508d --- /dev/null +++ b/pkg/lockfile/fixtures/bun/two-packages.json5 @@ -0,0 +1,17 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "bun-lockfile", + "dependencies": { + "has-flag": "*", + "wrappy": "^1.0.0", + }, + }, + }, + "packages": { + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + } +} diff --git a/pkg/lockfile/parse-bun-lock.go b/pkg/lockfile/parse-bun-lock.go new file mode 100644 index 00000000..fb5f7720 --- /dev/null +++ b/pkg/lockfile/parse-bun-lock.go @@ -0,0 +1,85 @@ +package lockfile + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/tidwall/jsonc" +) + +type BunLockfile struct { + Version int `json:"lockfileVersion"` + Packages map[string][]any `json:"packages"` +} + +const BunEcosystem = NpmEcosystem + +// structurePackageDetails returns the name, version, and commit of a package +// specified as a tuple in a bun.lock +func structurePackageDetails(a []any) (string, string, string) { + str, ok := a[0].(string) + + if !ok { + return "", "", "" + } + + str, isScoped := strings.CutPrefix(str, "@") + name, version, _ := strings.Cut(str, "@") + + if isScoped { + name = "@" + name + } + + version, commit, _ := strings.Cut(version, "#") + + // bun.lock does not track both the commit and version, + // so if we have a commit then we don't have a version + if commit != "" { + version = "" + } + + // file dependencies do not have a semantic version recorded + if strings.HasPrefix(version, "file:") { + version = "" + } + + return name, version, commit +} + +func ParseBunLock(pathToLockfile string) ([]PackageDetails, error) { + var parsedLockfile *BunLockfile + + lockfileContents, err := os.ReadFile(pathToLockfile) + + if err != nil { + return []PackageDetails{}, fmt.Errorf("could not read %s: %w", pathToLockfile, err) + } + + err = json.Unmarshal(jsonc.ToJSON(lockfileContents), &parsedLockfile) + + if err != nil { + return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, err) + } + + packages := make([]PackageDetails, 0, len(parsedLockfile.Packages)) + + for _, pkg := range parsedLockfile.Packages { + name, version, commit := structurePackageDetails(pkg) + + if name == "" && version == "" && commit == "" { + continue + } + + packages = append(packages, PackageDetails{ + Name: name, + Version: version, + Ecosystem: BunEcosystem, + CompareAs: BunEcosystem, + Commit: commit, + }) + } + + return packages, nil +} diff --git a/pkg/lockfile/parse-bun-lock_test.go b/pkg/lockfile/parse-bun-lock_test.go new file mode 100644 index 00000000..c6cf15b7 --- /dev/null +++ b/pkg/lockfile/parse-bun-lock_test.go @@ -0,0 +1,612 @@ +package lockfile_test + +import ( + "testing" + + "github.com/g-rath/osv-detector/pkg/lockfile" +) + +func TestParseBunLock_FileDoesNotExist(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/does-not-exist") + + expectErrContaining(t, err, "could not read") + expectPackages(t, packages, []lockfile.PackageDetails{}) +} + +func TestParseBunLock_InvalidJson(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/not-json.txt") + + expectErrContaining(t, err, "could not parse") + expectPackages(t, packages, []lockfile.PackageDetails{}) +} + +func TestParseBunLock_NoPackages(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/empty.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{}) +} + +func TestParseBunLock_OnePackage(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/one-package.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "wrappy", + Version: "1.0.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_OnePackageDev(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/one-package-dev.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "wrappy", + Version: "1.0.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_OnePackageBadTuple(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/bad-tuple.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "wrappy", + Version: "1.0.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_TwoPackages(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/two-packages.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "has-flag", + Version: "4.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "wrappy", + Version: "1.0.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_SamePackageDifferentGroups(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/same-package-different-groups.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "has-flag", + Version: "3.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "5.5.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_ScopedPackages(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/scoped-packages.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "@typescript-eslint/types", + Version: "5.62.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_ScopedPackagesMixed(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/scoped-packages-mixed.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "@babel/code-frame", + Version: "7.26.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "@babel/helper-validator-identifier", + Version: "7.25.9", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "js-tokens", + Version: "4.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "picocolors", + Version: "1.1.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "wrappy", + Version: "1.0.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_OptionalPackage(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/optional-package.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "acorn", + Version: "8.14.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "fsevents", + Version: "0.3.8", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "nan", + Version: "2.22.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_PeerDependenciesImplicit(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/peer-dependencies-implicit.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "acorn-jsx", + Version: "5.3.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "acorn", + Version: "8.14.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_PeerDependenciesExplicit(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/peer-dependencies-explicit.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "acorn-jsx", + Version: "5.3.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "acorn", + Version: "8.14.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_NestedDependenciesDups(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/nested-dependencies-dup.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "ansi-styles", + Version: "4.3.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "chalk", + Version: "4.1.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "color-convert", + Version: "2.0.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "color-name", + Version: "1.1.4", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "2.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "7.2.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "4.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_NestedDependencies(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/nested-dependencies.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "ansi-styles", + Version: "4.3.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "chalk", + Version: "4.1.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "color-convert", + Version: "2.0.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "color-name", + Version: "1.1.4", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "2.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "5.5.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "7.2.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "3.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "4.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_Aliases(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/alias.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "has-flag", + Version: "4.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "7.2.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "supports-color", + Version: "6.1.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + { + Name: "has-flag", + Version: "3.0.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + }, + }) +} + +func TestParseBunLock_Commits(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/commits.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "@babel/helper-plugin-utils", + Version: "7.26.5", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "@babel/helper-string-parser", + Version: "7.25.9", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "@babel/helper-validator-identifier", + Version: "7.25.9", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "@babel/parser", + Version: "7.26.5", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "@babel/types", + Version: "7.26.5", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "@prettier/sync", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "527e8ce", + }, + { + Name: "babel-preset-php", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "1ae6dc1267500360b411ec711b8aeac8c68b2246", + }, + { + Name: "is-number", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "98e8ff1", + }, + { + Name: "is-number", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "d5ac058", + }, + { + Name: "is-number", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "b7aef34", + }, + { + Name: "jquery", + Version: "3.7.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "lodash", + Version: "1.3.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "make-synchronized", + Version: "0.2.9", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "php-parser", + Version: "2.2.0", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "prettier", + Version: "3.4.2", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "raven-js", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "91ef2d4", + }, + { + Name: "slick-carousel", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "fc6f7d8", + }, + { + Name: "stopwords", + Version: "0.0.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + }) +} + +func TestParseBunLock_Files(t *testing.T) { + t.Parallel() + + packages, err := lockfile.ParseBunLock("fixtures/bun/files.json5") + + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + expectPackages(t, packages, []lockfile.PackageDetails{ + { + Name: "etag", + Version: "", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + { + Name: "lodash", + Version: "1.3.1", + Ecosystem: lockfile.BunEcosystem, + CompareAs: lockfile.BunEcosystem, + Commit: "", + }, + }) +} diff --git a/pkg/lockfile/parse.go b/pkg/lockfile/parse.go index e2bce5b6..254ca954 100644 --- a/pkg/lockfile/parse.go +++ b/pkg/lockfile/parse.go @@ -18,6 +18,7 @@ func FindParser(pathToLockfile string, parseAs string) (PackageDetailsParser, st //nolint:gochecknoglobals // this is an optimisation and read-only var parsers = map[string]PackageDetailsParser{ + "bun.lock": ParseBunLock, "Cargo.lock": ParseCargoLock, "composer.lock": ParseComposerLock, "Gemfile.lock": ParseGemfileLock, diff --git a/pkg/lockfile/parse_test.go b/pkg/lockfile/parse_test.go index 82f10bb8..c15ff275 100644 --- a/pkg/lockfile/parse_test.go +++ b/pkg/lockfile/parse_test.go @@ -48,6 +48,7 @@ func TestFindParser(t *testing.T) { "Cargo.lock", "package-lock.json", "yarn.lock", + "bun.lock", "pnpm-lock.yaml", "composer.lock", "Gemfile.lock", @@ -97,6 +98,7 @@ func TestParse_FindsExpectedParsers(t *testing.T) { "package-lock.json", "packages.lock.json", "yarn.lock", + "bun.lock", "pnpm-lock.yaml", "composer.lock", "Gemfile.lock",