-
Notifications
You must be signed in to change notification settings - Fork 948
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5001 from dependabot/translate-types-to-libraries
Map Package Name to Types Package Name
- Loading branch information
Showing
2 changed files
with
184 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# frozen_string_literal: true | ||
|
||
module Dependabot | ||
module NpmAndYarn | ||
class PackageName | ||
DEFINITELY_TYPED_SCOPE = /types/i.freeze | ||
PACKAGE_NAME_REGEX = %r{ | ||
\A # beginning of string | ||
(?=.{1,214}\z) # enforce length (1 - 214) | ||
(@(?<scope>[a-z0-9\-~][a-z0-9\-\._~]*)\/)? # capture 'scope' if present | ||
(?<name>[a-z0-9\-~][a-z0-9\-._~]*) # capture package name | ||
\z # end of string | ||
}xi.freeze # multi-line/case-insensitive | ||
|
||
class InvalidPackageName < StandardError; end | ||
|
||
def initialize(string) | ||
match = PACKAGE_NAME_REGEX.match(string.to_s) | ||
raise InvalidPackageName unless match | ||
|
||
@scope = match[:scope] | ||
@name = match[:name] | ||
end | ||
|
||
def to_s | ||
if scoped? | ||
"@#{@scope}/#{@name}" | ||
else | ||
@name.to_s | ||
end | ||
end | ||
|
||
def <=>(other) | ||
to_s.casecmp(other.to_s) | ||
end | ||
|
||
def eql?(other) | ||
to_s.eql?(other.to_s) | ||
end | ||
|
||
def types_package_name | ||
return self if types_package? | ||
|
||
@types_package_name ||= | ||
if scoped? | ||
self.class.new("@types/#{@scope}__#{@name}") | ||
else | ||
self.class.new("@types/#{@name}") | ||
end | ||
end | ||
|
||
private | ||
|
||
def scoped? | ||
!@scope.nil? | ||
end | ||
|
||
def types_package? | ||
DEFINITELY_TYPED_SCOPE.match?(@scope) | ||
end | ||
end | ||
end | ||
end |
121 changes: 121 additions & 0 deletions
121
npm_and_yarn/spec/dependabot/npm_and_yarn/package_name_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
require "dependabot/npm_and_yarn/package_name" | ||
|
||
RSpec.describe Dependabot::NpmAndYarn::PackageName do | ||
describe "initialization" do | ||
it "raises a meaningful error if the input is not a valid package name" do | ||
expect { described_class.new("馃し") }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new([]) }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new({}) }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new(nil) }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new("") }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new(" prefixed-with-a-space") }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new(".prefixed-with-a-dot") }.to raise_error(described_class::InvalidPackageName) | ||
expect { described_class.new("!invalid") }.to raise_error(described_class::InvalidPackageName) | ||
end | ||
end | ||
|
||
describe "#to_s" do | ||
it "returns the name when no scope is present" do | ||
jquery = "jquery" | ||
|
||
package_name = described_class.new(jquery).to_s | ||
|
||
expect(package_name).to eq(jquery) | ||
end | ||
|
||
it "returns the name with scope when a scope is present" do | ||
babel_core = "@babel/core" | ||
|
||
package_name_with_scope = described_class.new(babel_core).to_s | ||
|
||
expect(package_name_with_scope).to eq(babel_core) | ||
end | ||
end | ||
|
||
describe "#types_package_name" do | ||
it "returns the corresponding types package name" do | ||
lodash = "lodash" | ||
lodash_types = "@types/lodash" | ||
|
||
types_package_name = described_class.new(lodash).types_package_name | ||
|
||
expect(types_package_name.to_s).to eq(lodash_types) | ||
end | ||
|
||
it "returns self if it is already a types package" do | ||
stereo_types = "@types/stereo" | ||
|
||
types_package_name = described_class.new(stereo_types).types_package_name | ||
|
||
expect(types_package_name.to_s).to eq(stereo_types) | ||
end | ||
|
||
context "when given a scoped dependency name" do | ||
it "returns the corresponding scoped types package name" do | ||
babel_core = "@babel/core" | ||
babel_core_types = "@types/babel__core" | ||
|
||
types_package_name = described_class.new(babel_core).types_package_name | ||
|
||
expect(types_package_name.to_s).to eq(babel_core_types) | ||
end | ||
end | ||
end | ||
|
||
describe "#eql?" do | ||
it "compares the string representation of the package name" do | ||
package = described_class.new("package") | ||
package_again = described_class.new("package") | ||
|
||
equality_check = package.eql?(package_again) | ||
|
||
expect(equality_check).to be true | ||
end | ||
|
||
it "returns true for equivalent package names" do | ||
react = described_class.new("react") | ||
react_again = described_class.new("react") | ||
|
||
equality_check = react.eql?(react_again) | ||
|
||
expect(equality_check).to be true | ||
end | ||
|
||
it "returns false for non-equivalent package names" do | ||
react = described_class.new("react") | ||
vue = described_class.new("vue") | ||
|
||
equality_check = react.eql?(vue) | ||
|
||
expect(equality_check).to be false | ||
end | ||
end | ||
|
||
describe "#<=>" do | ||
it "provides affordances for sorting/comparison" do | ||
first = described_class.new("first") | ||
second = described_class.new("second") | ||
third = described_class.new("third") | ||
|
||
expect([third, second, first].sort).to eq([first, second, third]) | ||
end | ||
|
||
it "ignores case" do | ||
package_name_string = "jquery" | ||
all_caps = described_class.new(package_name_string.upcase) | ||
all_lower = described_class.new(package_name_string.downcase) | ||
|
||
expect(all_lower <=> all_caps).to be_zero | ||
end | ||
|
||
it "allows for comparison with types packages" do | ||
library = described_class.new("my-library") | ||
|
||
expect([library, library.types_package_name].sort). | ||
to eq([library.types_package_name, library]) | ||
end | ||
end | ||
end |