From 57936813b52d87859056235acb77c504f4e2da1e Mon Sep 17 00:00:00 2001 From: Mark Allen Date: Fri, 10 Jan 2025 13:20:57 +0000 Subject: [PATCH] Add BunLock FileParser Co-authored-by: Ashcon Partovi --- .../dependabot/npm_and_yarn/file_parser.rb | 10 +- .../npm_and_yarn/file_parser/bun_lock.rb | 141 ++++++++++++++++++ .../file_parser/lockfile_parser.rb | 60 ++++---- .../file_parser/lockfile_parser_spec.rb | 64 ++++++++ .../projects/bun/invalid_lockfile/bun.lock | 2 + .../bun/invalid_lockfile/package.json | 1 + .../bun/invalid_lockfile_version/bun.lock | 7 + .../bun/invalid_lockfile_version/package.json | 1 + .../fixtures/projects/bun/simple_v0/bun.lock | 36 +++++ .../projects/bun/simple_v0/package.json | 8 + .../projects/bun/simple_workspace_v0/bun.lock | 61 ++++++++ .../package-foo/package.json | 10 ++ .../bun/simple_workspace_v0/package.json | 14 ++ .../packages/bar/package.json | 12 ++ .../packages/baz/package.json | 7 + 15 files changed, 406 insertions(+), 28 deletions(-) create mode 100644 npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/bun.lock create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/bun.lock create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_v0/bun.lock create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_v0/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/bun.lock create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package-foo/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/bar/package.json create mode 100644 npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/baz/package.json diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb index 11f3bb0a7a..7f86e81eaf 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb @@ -110,7 +110,8 @@ def lockfiles { npm: package_lock || shrinkwrap, yarn: yarn_lock, - pnpm: pnpm_lock + pnpm: pnpm_lock, + bun: bun_lock } end @@ -167,6 +168,13 @@ def pnpm_lock end, T.nilable(Dependabot::DependencyFile)) end + sig { returns(T.nilable(Dependabot::DependencyFile)) } + def bun_lock + @bun_lock ||= T.let(dependency_files.find do |f| + f.name == BunPackageManager::LOCKFILE_NAME + end, T.nilable(Dependabot::DependencyFile)) + end + sig { returns(T.nilable(Dependabot::DependencyFile)) } def npmrc @npmrc ||= T.let(dependency_files.find do |f| diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb new file mode 100644 index 0000000000..2bd288986b --- /dev/null +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb @@ -0,0 +1,141 @@ +# typed: strict +# frozen_string_literal: true + +require "yaml" +require "dependabot/errors" +require "dependabot/npm_and_yarn/helpers" +require "sorbet-runtime" + +module Dependabot + module NpmAndYarn + class FileParser < Dependabot::FileParsers::Base + class BunLock + extend T::Sig + + sig { params(dependency_file: DependencyFile).void } + def initialize(dependency_file) + @dependency_file = dependency_file + end + + sig { returns(T::Hash[String, T.untyped]) } + def parsed + @parsed ||= begin + content = begin + # Since bun.lock is a JSONC file, which is a subset of YAML, we can use YAML to parse it + YAML.load(T.must(@dependency_file.content)) + rescue Psych::SyntaxError => e + raise_invalid!("malformed JSONC at line #{e.line}, column #{e.column}") + end + raise_invalid!("expected to be an object") unless content.is_a?(Hash) + + version = content["lockfileVersion"] + raise_invalid!("expected 'lockfileVersion' to be an integer") unless version.is_a?(Integer) + raise_invalid!("expected 'lockfileVersion' to be >= 0") unless version >= 0 + raise_invalid!("unsupported 'lockfileVersion' = #{version}") unless version.zero? + + T.let(content, T.untyped) + end + end + + sig { returns(Dependabot::FileParsers::Base::DependencySet) } + def dependencies + dependency_set = Dependabot::FileParsers::Base::DependencySet.new + + # bun.lock v0 format: + # https://github.com/oven-sh/bun/blob/c130df6c589fdf28f9f3c7f23ed9901140bc9349/src/install/bun.lock.zig#L595-L605 + + packages = parsed["packages"] + raise_invalid!("expected 'packages' to be an object") unless packages.is_a?(Hash) + + packages.each do |key, details| + raise_invalid!("expected 'packages.#{key}' to be an array") unless details.is_a?(Array) + + resolution = details.first + raise_invalid!("expected 'packages.#{key}[0]' to be a string") unless resolution.is_a?(String) + + name, version = resolution.split(/(?<=\w)\@/) + next if name.empty? + + semver = Version.semver_for(version) + next unless semver + + dependency_set << Dependency.new( + name: name, + version: semver.to_s, + package_manager: "npm_and_yarn", + requirements: [] + ) + end + + dependency_set + end + + sig do + params(dependency_name: String, requirement: T.untyped, _manifest_name: String) + .returns(T.nilable(T::Hash[String, T.untyped])) + end + def details(dependency_name, requirement, _manifest_name) + packages = parsed["packages"] + return unless packages.is_a?(Hash) + + candidates = + packages + .select { |name, _| name == dependency_name } + .values + + # If there's only one entry for this dependency, use it, even if + # the requirement in the lockfile doesn't match + if candidates.one? + parse_details(candidates.first) + else + candidate = candidates.find do |label, _| + label.scan(/(?<=\w)\@(?:npm:)?([^\s,]+)/).flatten.include?(requirement) + end&.last + parse_details(candidate) + end + end + + private + + sig { params(message: String).void } + def raise_invalid!(message) + raise Dependabot::DependencyFileNotParseable.new(@dependency_file.path, "Invalid bun.lock file: #{message}") + end + + sig do + params(entry: T.nilable(T::Array[T.untyped])).returns(T.nilable(T::Hash[String, T.untyped])) + end + def parse_details(entry) + return unless entry.is_a?(Array) + + # Either: + # - "{name}@{version}", registry, details, integrity + # - "{name}@{resolution}", details + resolution = entry.first + return unless resolution.is_a?(String) + + name, version = resolution.split(/(?<=\w)\@/) + semver = Version.semver_for(version) + + if semver + registry, details, integrity = entry[1..3] + { + "name" => name, + "version" => semver.to_s, + "registry" => registry, + "details" => details, + "integrity" => integrity + } + else + details = entry[1] + { + "name" => name, + "resolution" => version, + "details" => details + } + end + end + end + end + end +end diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb index f1e3b5f789..2e4f60ca10 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb @@ -15,6 +15,11 @@ class LockfileParser require "dependabot/npm_and_yarn/file_parser/yarn_lock" require "dependabot/npm_and_yarn/file_parser/pnpm_lock" require "dependabot/npm_and_yarn/file_parser/json_lock" + require "dependabot/npm_and_yarn/file_parser/bun_lock" + + DEFAULT_LOCKFILES = %w(package-lock.json yarn.lock pnpm-lock.yaml bun.lock npm-shrinkwrap.json).freeze + + LockFile = T.type_alias { T.any(JsonLock, YarnLock, PnpmLock, BunLock) } sig { params(dependency_files: T::Array[DependencyFile]).void } def initialize(dependency_files:) @@ -29,7 +34,7 @@ def parse_set # end up unique by name. That's not a perfect representation of # the nested nature of JS resolution, but it makes everything work # comparably to other flat-resolution strategies - (yarn_locks + pnpm_locks + package_locks + shrinkwraps).each do |file| + (yarn_locks + pnpm_locks + package_locks + bun_locks + shrinkwraps).each do |file| dependency_set += lockfile_for(file).dependencies end @@ -64,58 +69,59 @@ def lockfile_details(dependency_name:, requirement:, manifest_name:) sig { params(manifest_filename: String).returns(T::Array[DependencyFile]) } def potential_lockfiles_for_manifest(manifest_filename) dir_name = File.dirname(manifest_filename) - possible_lockfile_names = - %w(package-lock.json npm-shrinkwrap.json pnpm-lock.yaml yarn.lock).map do |f| - Pathname.new(File.join(dir_name, f)).cleanpath.to_path - end + - %w(yarn.lock pnpm-lock.yaml package-lock.json npm-shrinkwrap.json) + possible_lockfile_names = DEFAULT_LOCKFILES.map do |f| + Pathname.new(File.join(dir_name, f)).cleanpath.to_path + end + DEFAULT_LOCKFILES possible_lockfile_names.uniq .filter_map { |nm| dependency_files.find { |f| f.name == nm } } end - sig { params(file: DependencyFile).returns(T.any(JsonLock, YarnLock, PnpmLock)) } + sig { params(file: DependencyFile).returns(LockFile) } def lockfile_for(file) - @lockfiles ||= T.let({}, T.nilable(T::Hash[String, T.any(JsonLock, YarnLock, PnpmLock)])) - @lockfiles[file.name] ||= if [*package_locks, *shrinkwraps].include?(file) + @lockfiles ||= T.let({}, T.nilable(T::Hash[String, LockFile])) + @lockfiles[file.name] ||= case file.name + when *package_locks.map(&:name), *shrinkwraps.map(&:name) JsonLock.new(file) - elsif yarn_locks.include?(file) + when *yarn_locks.map(&:name) YarnLock.new(file) - else + when *pnpm_locks.map(&:name) PnpmLock.new(file) + when *bun_locks.map(&:name) + BunLock.new(file) + else + raise "Unexpected lockfile: #{file.name}" end end + sig { params(extension: String).returns(T::Array[DependencyFile]) } + def select_files_by_extension(extension) + dependency_files.select { |f| f.name.end_with?(extension) } + end + sig { returns(T::Array[DependencyFile]) } def package_locks - @package_locks ||= T.let( - dependency_files - .select { |f| f.name.end_with?("package-lock.json") }, T.nilable(T::Array[DependencyFile]) - ) + @package_locks ||= T.let(select_files_by_extension("package-lock.json"), T.nilable(T::Array[DependencyFile])) end sig { returns(T::Array[DependencyFile]) } def pnpm_locks - @pnpm_locks ||= T.let( - dependency_files - .select { |f| f.name.end_with?("pnpm-lock.yaml") }, T.nilable(T::Array[DependencyFile]) - ) + @pnpm_locks ||= T.let(select_files_by_extension("pnpm-lock.yaml"), T.nilable(T::Array[DependencyFile])) + end + + sig { returns(T::Array[DependencyFile]) } + def bun_locks + @bun_locks ||= T.let(select_files_by_extension("bun.lock"), T.nilable(T::Array[DependencyFile])) end sig { returns(T::Array[DependencyFile]) } def yarn_locks - @yarn_locks ||= T.let( - dependency_files - .select { |f| f.name.end_with?("yarn.lock") }, T.nilable(T::Array[DependencyFile]) - ) + @yarn_locks ||= T.let(select_files_by_extension("yarn.lock"), T.nilable(T::Array[DependencyFile])) end sig { returns(T::Array[DependencyFile]) } def shrinkwraps - @shrinkwraps ||= T.let( - dependency_files - .select { |f| f.name.end_with?("npm-shrinkwrap.json") }, T.nilable(T::Array[DependencyFile]) - ) + @shrinkwraps ||= T.let(select_files_by_extension("npm-shrinkwrap.json"), T.nilable(T::Array[DependencyFile])) end sig { returns(T.class_of(Dependabot::NpmAndYarn::Version)) } diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_parser/lockfile_parser_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_parser/lockfile_parser_spec.rb index 7b36891c13..2a443c178b 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_parser/lockfile_parser_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_parser/lockfile_parser_spec.rb @@ -319,6 +319,70 @@ end end end + + context "when dealing with bun.lock" do + context "when the lockfile is invalid" do + let(:dependency_files) { project_dependency_files("bun/invalid_lockfile") } + + it "raises a DependencyFileNotParseable error" do + expect { dependencies } + .to raise_error(Dependabot::DependencyFileNotParseable) do |error| + expect(error.file_name).to eq("bun.lock") + expect(error.message).to eq("Invalid bun.lock file: malformed JSONC at line 3, column 1") + end + end + end + + context "when the lockfile version is invalid" do + let(:dependency_files) { project_dependency_files("bun/invalid_lockfile_version") } + + it "raises a DependencyFileNotParseable error" do + expect { dependencies } + .to raise_error(Dependabot::DependencyFileNotParseable) do |error| + expect(error.file_name).to eq("bun.lock") + expect(error.message).to include("lockfileVersion") + end + end + end + + context "when dealing with v0 format" do + context "with a simple project" do + let(:dependency_files) { project_dependency_files("bun/simple_v0") } + + it "parses dependencies properly" do + expect(dependencies.find { |d| d.name == "fetch-factory" }).to have_attributes( + name: "fetch-factory", + version: "0.0.1" + ) + expect(dependencies.find { |d| d.name == "etag" }).to have_attributes( + name: "etag", + version: "1.8.1" + ) + expect(dependencies.length).to eq(11) + end + end + + context "with a simple workspace project" do + let(:dependency_files) { project_dependency_files("bun/simple_workspace_v0") } + + it "parses dependencies properly" do + expect(dependencies.find { |d| d.name == "etag" }).to have_attributes( + name: "etag", + version: "1.8.1" + ) + expect(dependencies.find { |d| d.name == "lodash" }).to have_attributes( + name: "lodash", + version: "1.3.1" + ) + expect(dependencies.find { |d| d.name == "chalk" }).to have_attributes( + name: "chalk", + version: "0.3.0" + ) + expect(dependencies.length).to eq(5) + end + end + end + end end describe "#lockfile_details" do diff --git a/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/bun.lock b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/bun.lock new file mode 100644 index 0000000000..e9c60b0b72 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/bun.lock @@ -0,0 +1,2 @@ +# This is an invalid bun.lock file! +[ diff --git a/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/package.json b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/package.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile/package.json @@ -0,0 +1 @@ +{} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/bun.lock b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/bun.lock new file mode 100644 index 0000000000..e2751f3a81 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/bun.lock @@ -0,0 +1,7 @@ +{ + "lockfileVersion": -1, + "workspaces": { + "": {}, + }, + "dependencies": {}, +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/package.json b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/package.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/invalid_lockfile_version/package.json @@ -0,0 +1 @@ +{} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/bun.lock b/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/bun.lock new file mode 100644 index 0000000000..690cf8ed31 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/bun.lock @@ -0,0 +1,36 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "fetch-factory": "^0.0.1", + }, + "devDependencies": { + "etag": "^1.0.0", + }, + }, + }, + "packages": { + "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + + "es6-promise": ["es6-promise@3.3.1", "", {}, "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "fetch-factory": ["fetch-factory@0.0.1", "", { "dependencies": { "es6-promise": "^3.0.2", "isomorphic-fetch": "^2.1.1", "lodash": "^3.10.1" } }, "sha512-gexRwqIhwzDJ2pJvL0UYfiZwW06/bdYWxAmswFFts7C87CF8i6liApihTk7TZFYMDcQjvvDIvyHv0q379z0aWA=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], + + "isomorphic-fetch": ["isomorphic-fetch@2.2.1", "", { "dependencies": { "node-fetch": "^1.0.1", "whatwg-fetch": ">=0.10.0" } }, "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA=="], + + "lodash": ["lodash@3.10.1", "", {}, "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ=="], + + "node-fetch": ["node-fetch@1.7.3", "", { "dependencies": { "encoding": "^0.1.11", "is-stream": "^1.0.1" } }, "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="], + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/package.json b/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/package.json new file mode 100644 index 0000000000..3a1465824d --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_v0/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "fetch-factory": "^0.0.1" + }, + "devDependencies": { + "etag": "^1.0.0" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/bun.lock b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/bun.lock new file mode 100644 index 0000000000..1515d202da --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/bun.lock @@ -0,0 +1,61 @@ +{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "bar": "packages/bar", + "baz": "packages/baz", + "foo": "package-foo", + }, + "devDependencies": { + "etag": "^1.1.0", + }, + }, + "package-foo": { + "name": "foo", + "version": "0.0.1", + "dependencies": { + "lodash": "^1.2.1", + }, + "devDependencies": { + "etag": "^1.0.0", + }, + }, + "packages/bar": { + "name": "bar", + "version": "0.0.1", + "dependencies": { + "chalk": "0.3.0", + "foo": "package-foo", + "lodash": "^1.2.1", + }, + "devDependencies": { + "etag": "^1.1.0", + }, + }, + "packages/baz": { + "name": "baz", + "version": "0.0.1", + "dependencies": { + "bar": "packages/bar", + }, + }, + }, + "packages": { + "ansi-styles": ["ansi-styles@0.2.0", "", {}, "sha512-YyQBeLj0juxUC9uUXRpQ1ZAzPT1dnsn5vVeJLHYFq4Ct1p0rymUSyvckKCXCH9I0bh3jWDIETA5nXIaZVKlDyA=="], + + "bar": ["bar@workspace:packages/bar", { "dependencies": { "chalk": "0.3.0", "foo": "package-foo", "lodash": "^1.2.1" }, "devDependencies": { "etag": "^1.1.0" } }], + + "baz": ["baz@workspace:packages/baz", { "dependencies": { "bar": "packages/bar" } }], + + "chalk": ["chalk@0.3.0", "", { "dependencies": { "ansi-styles": "~0.2.0", "has-color": "~0.1.0" } }, "sha512-OcfgS16PHpCu2Q4TNMtk0aZNx8PyeNiiB+6AgGH91fhT9hJ3v6pIIJ3lxlaOEDHlTm8t3wDe6bDGamvtIokQTg=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "foo": ["foo@workspace:package-foo", { "dependencies": { "lodash": "^1.2.1" }, "devDependencies": { "etag": "^1.0.0" } }], + + "has-color": ["has-color@0.1.7", "", {}, "sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw=="], + + "lodash": ["lodash@1.3.1", "", {}, "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw=="], + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package-foo/package.json b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package-foo/package.json new file mode 100644 index 0000000000..c3b788617b --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package-foo/package.json @@ -0,0 +1,10 @@ +{ + "name": "foo", + "version": "0.0.1", + "dependencies": { + "lodash": "^1.2.1" + }, + "devDependencies": { + "etag": "^1.0.0" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package.json b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package.json new file mode 100644 index 0000000000..eb64ee52b7 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/package.json @@ -0,0 +1,14 @@ +{ + "workspaces": [ + "packages/*", + "package-*" + ], + "dependencies": { + "foo": "workspace:*", + "bar": "workspace:*", + "baz": "workspace:*" + }, + "devDependencies": { + "etag": "^1.1.0" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/bar/package.json b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/bar/package.json new file mode 100644 index 0000000000..89c6efbdf5 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/bar/package.json @@ -0,0 +1,12 @@ +{ + "name": "bar", + "version": "0.0.1", + "dependencies": { + "chalk": "0.3.0", + "lodash": "^1.2.1", + "foo": "workspace:*" + }, + "devDependencies": { + "etag": "^1.1.0" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/baz/package.json b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/baz/package.json new file mode 100644 index 0000000000..cee71f3766 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/bun/simple_workspace_v0/packages/baz/package.json @@ -0,0 +1,7 @@ +{ + "name": "baz", + "version": "0.0.1", + "dependencies": { + "bar": "workspace:*" + } +}