Autolink package names in package.json
This commit is contained in:
parent
02ad8c0c68
commit
83747783e2
6 changed files with 205 additions and 0 deletions
|
@ -3,6 +3,7 @@ module Gitlab
|
|||
LINKERS = [
|
||||
GemfileLinker,
|
||||
GemspecLinker,
|
||||
PackageJsonLinker,
|
||||
].freeze
|
||||
|
||||
def self.linker(blob_name)
|
||||
|
|
|
@ -2,6 +2,7 @@ module Gitlab
|
|||
module DependencyLinker
|
||||
class BaseLinker
|
||||
URL_REGEX = %r{https?://[^'"]+}.freeze
|
||||
REPO_REGEX = %r{[^/'"]+/[^/'"]+}.freeze
|
||||
|
||||
class_attribute :file_type
|
||||
|
||||
|
@ -36,6 +37,9 @@ module Gitlab
|
|||
Licensee::License.find(name)&.url
|
||||
end
|
||||
|
||||
def github_url(name)
|
||||
"https://github.com/#{name}"
|
||||
end
|
||||
|
||||
def link_tag(name, url)
|
||||
%{<a href="#{ERB::Util.html_escape_once(url)}" rel="nofollow noreferrer noopener" target="_blank">#{ERB::Util.html_escape_once(name)}</a>}
|
||||
|
|
63
lib/gitlab/dependency_linker/json_linker.rb
Normal file
63
lib/gitlab/dependency_linker/json_linker.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class JsonLinker < BaseLinker
|
||||
def link
|
||||
return highlighted_text unless json
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Links package names in a JSON key or values.
|
||||
#
|
||||
# Example:
|
||||
# link_json('name')
|
||||
# # Will link `package` in `"name": "package"`
|
||||
#
|
||||
# link_json('name', 'specific_package')
|
||||
# # Will link `specific_package` in `"name": "specific_package"`
|
||||
#
|
||||
# link_json('name', /[^\/]+\/[^\/]+/)
|
||||
# # Will link `user/repo` in `"name": "user/repo"`, but not `"name": "package"`
|
||||
#
|
||||
# link_json('specific_package', '1.0.1', link: :key)
|
||||
# # Will link `specific_package` in `"specific_package": "1.0.1"`
|
||||
def link_json(key, value = nil, link: :value, &url_proc)
|
||||
key =
|
||||
case key
|
||||
when Array
|
||||
Regexp.union(key.map { |name| Regexp.escape(name) })
|
||||
when String
|
||||
Regexp.escape(key)
|
||||
when nil
|
||||
'[^"]+'
|
||||
else
|
||||
key
|
||||
end
|
||||
|
||||
value =
|
||||
case value
|
||||
when String
|
||||
Regexp.escape(value)
|
||||
when nil
|
||||
'[^"]+'
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
if link == :value
|
||||
value = "(?<name>#{value})"
|
||||
else
|
||||
key = "(?<name>#{key})"
|
||||
end
|
||||
|
||||
link_regex(/"#{key}":\s*"#{value}"/, &url_proc)
|
||||
end
|
||||
|
||||
def json
|
||||
@json ||= JSON.parse(plain_text) rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
44
lib/gitlab/dependency_linker/package_json_linker.rb
Normal file
44
lib/gitlab/dependency_linker/package_json_linker.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class PackageJsonLinker < JsonLinker
|
||||
self.file_type = :package_json
|
||||
|
||||
private
|
||||
|
||||
def link_dependencies
|
||||
link_json('name', json["name"], &method(:package_url))
|
||||
link_json('license', &method(:license_url))
|
||||
link_json(%w[homepage url], URL_REGEX, &:itself)
|
||||
|
||||
link_packages
|
||||
end
|
||||
|
||||
def link_packages
|
||||
link_packages_at_key("dependencies", &method(:package_url))
|
||||
link_packages_at_key("devDependencies", &method(:package_url))
|
||||
end
|
||||
|
||||
def link_packages_at_key(key, &url_proc)
|
||||
dependencies = json[key]
|
||||
return unless dependencies
|
||||
|
||||
dependencies.each do |name, version|
|
||||
link_json(name, version, link: :key, &url_proc)
|
||||
|
||||
link_json(name) do |value|
|
||||
case value
|
||||
when /\A#{URL_REGEX}\z/
|
||||
value
|
||||
when /\A#{REPO_REGEX}\z/
|
||||
github_url(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def package_url(name)
|
||||
"https://npmjs.com/package/#{name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Gitlab::DependencyLinker::PackageJsonLinker, lib: true do
|
||||
describe '.support?' do
|
||||
it 'supports package.json' do
|
||||
expect(described_class.support?('package.json')).to be_truthy
|
||||
end
|
||||
|
||||
it 'does not support other files' do
|
||||
expect(described_class.support?('package.json.example')).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe '#link' do
|
||||
let(:file_name) { "package.json" }
|
||||
|
||||
let(:file_content) do
|
||||
<<-CONTENT.strip_heredoc
|
||||
{
|
||||
"name": "module-name",
|
||||
"version": "10.3.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vuejs/vue.git"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/vue#readme",
|
||||
"dependencies": {
|
||||
"primus": "*",
|
||||
"async": "~0.8.0",
|
||||
"express": "4.2.x",
|
||||
"bigpipe": "bigpipe/pagelet",
|
||||
"plates": "https://github.com/flatiron/plates/tarball/master"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vows": "^0.7.0",
|
||||
"assume": "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
|
||||
"pre-commit": "*"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
CONTENT
|
||||
end
|
||||
|
||||
subject { Gitlab::Highlight.highlight(file_name, file_content) }
|
||||
|
||||
def link(name, url)
|
||||
%{<a href="#{url}" rel="nofollow noreferrer noopener" target="_blank">#{name}</a>}
|
||||
end
|
||||
|
||||
it 'links the module name' do
|
||||
expect(subject).to include(link('module-name', 'https://npmjs.com/package/module-name'))
|
||||
end
|
||||
|
||||
it 'links the homepage' do
|
||||
expect(subject).to include(link('https://github.com/vuejs/vue#readme', 'https://github.com/vuejs/vue#readme'))
|
||||
end
|
||||
|
||||
it 'links the repository URL' do
|
||||
expect(subject).to include(link('https://github.com/vuejs/vue.git', 'https://github.com/vuejs/vue.git'))
|
||||
end
|
||||
|
||||
it 'links the license' do
|
||||
expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/'))
|
||||
end
|
||||
|
||||
it 'links dependencies' do
|
||||
expect(subject).to include(link('primus', 'https://npmjs.com/package/primus'))
|
||||
expect(subject).to include(link('async', 'https://npmjs.com/package/async'))
|
||||
expect(subject).to include(link('express', 'https://npmjs.com/package/express'))
|
||||
expect(subject).to include(link('bigpipe', 'https://npmjs.com/package/bigpipe'))
|
||||
expect(subject).to include(link('plates', 'https://npmjs.com/package/plates'))
|
||||
expect(subject).to include(link('vows', 'https://npmjs.com/package/vows'))
|
||||
expect(subject).to include(link('assume', 'https://npmjs.com/package/assume'))
|
||||
expect(subject).to include(link('pre-commit', 'https://npmjs.com/package/pre-commit'))
|
||||
end
|
||||
|
||||
it 'links GitHub repos' do
|
||||
expect(subject).to include(link('bigpipe/pagelet', 'https://github.com/bigpipe/pagelet'))
|
||||
end
|
||||
|
||||
it 'links Git repos' do
|
||||
expect(subject).to include(link('https://github.com/flatiron/plates/tarball/master', 'https://github.com/flatiron/plates/tarball/master'))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,5 +17,13 @@ describe Gitlab::DependencyLinker, lib: true do
|
|||
|
||||
described_class.link(blob_name, nil, nil)
|
||||
end
|
||||
|
||||
it 'links using PackageJsonLinker' do
|
||||
blob_name = 'package.json'
|
||||
|
||||
expect(described_class::PackageJsonLinker).to receive(:link)
|
||||
|
||||
described_class.link(blob_name, nil, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue