diff --git a/README.md b/README.md index 815f918..6617549 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,22 @@ end package_json.manager.run("lint", ["--fix"]) ``` -The `PackageJson` class represents a `package.json` on disk within a directory, -creating the file if it does not already exist. - -Because it is expected that the `package.json` might be changed by external +The `PackageJson` class represents a `package.json` on disk within a directory; +because it is expected that the `package.json` might be changed by external sources such as package managers, `PackageJson` reads and writes to and from the `package.json` as needed rather than representing it in memory. +You can initialize a `PackageJson` with either `new` or `read` depending on if +you want to ensure the `package.json` exists or throw if it doesn't: + +```ruby +# this will create the `package.json` if it does not exist +PackageJson.new(:npm, "path/to/directory") + +# this will error if the `package.json` does not exist +PackageJson.read("path/to/directory", :npm) +``` + A `PackageJson` also comes with a `manager` that can be used to manage dependencies and run scripts. The manager will be inferred by the [`packageManager`](https://nodejs.org/api/packages.html#packagemanager) property diff --git a/lib/package_json.rb b/lib/package_json.rb index 14a50c4..8c1c7b6 100644 --- a/lib/package_json.rb +++ b/lib/package_json.rb @@ -14,6 +14,14 @@ class NotImplementedError < Error; end attr_reader :manager, :path + def self.read(path_to_directory = Dir.pwd, fallback_manager = :npm) + unless File.exist?("#{path_to_directory}/package.json") + raise Error, "#{path_to_directory} does not contain a package.json" + end + + new(fallback_manager, path_to_directory) + end + def initialize(fallback_manager = :npm, path_to_directory = Dir.pwd) @path = path_to_directory diff --git a/spec/package_json_spec.rb b/spec/package_json_spec.rb index 77878d2..97718ad 100644 --- a/spec/package_json_spec.rb +++ b/spec/package_json_spec.rb @@ -9,6 +9,118 @@ expect(PackageJson::VERSION).not_to be_nil end + describe ".read" do + context "when the package.json does not exist" do + it "raises an error" do + expect { described_class.read }.to raise_error( + PackageJson::Error, "#{Dir.pwd} does not contain a package.json" + ) + end + end + + context "when the package.json already exists with the packageManager property" do + it "does not error" do + with_package_json_file({ "version" => "1.0.0" }) do + expect { described_class.new }.not_to raise_error + end + end + + it "uses the packageManager property" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "pnpm" }) do + package_json = described_class.new + + expect(package_json.manager).to be_a PackageJson::Managers::PnpmLike + end + end + + it "ignores the fallback manager" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "pnpm" }) do + package_json = described_class.new(:yarn_classic) + + expect(package_json.manager).to be_a PackageJson::Managers::PnpmLike + end + end + + it "supports having a version specified" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "pnpm@1.2.3" }) do + package_json = described_class.new + + expect(package_json.manager).to be_a PackageJson::Managers::PnpmLike + end + end + + it "requires a major version for yarn" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "yarn" }) do + expect { described_class.new }.to raise_error(PackageJson::Error, "a major version must be present for Yarn") + end + end + + it "only supports yarn v1" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "yarn@2" }) do + expect { described_class.new }.to raise_error(PackageJson::Error, "only Yarn classic is supported") + end + end + + it "supports a full version being specified for yarn" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "yarn@1.2.3" }) do + package_json = described_class.new + + expect(package_json.manager).to be_a PackageJson::Managers::YarnClassicLike + end + end + + it "does not change the packageManager property" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "pnpm" }) do + described_class.new(:yarn_classic) + + expect_package_json_with_content({ "version" => "1.0.0", "packageManager" => "pnpm" }) + end + end + + it "raises an error if the package manager is not supported" do + with_package_json_file({ "version" => "1.0.0", "packageManager" => "unknown" }) do + expect { described_class.new }.to raise_error( + PackageJson::Error, + 'unsupported package manager "unknown"' + ) + end + end + end + + context "when the package.json already exists without the packageManager property" do + it "does not error" do + with_package_json_file({ "version" => "1.0.0" }) do + expect { described_class.new }.not_to raise_error + end + end + + it "uses the fallback manager" do + with_package_json_file({ "version" => "1.0.0" }) do + package_json = described_class.new(:yarn_classic) + + expect(package_json.manager).to be_a PackageJson::Managers::YarnClassicLike + end + end + + it "does not add the packageManager property" do + with_package_json_file({ "version" => "1.0.0" }) do + described_class.new(:yarn_classic) + + expect_package_json_with_content({ "version" => "1.0.0" }) + end + end + + it "raises an error if the fallback manager is not supported" do + with_package_json_file({ "version" => "1.0.0" }) do + expect { described_class.new(:unknown) }.to raise_error( + PackageJson::Error, + 'unsupported package manager "unknown"' + ) + end + end + end + end + describe ".new" do context "when the package.json does not exist" do it "does not error" do