summaryrefslogtreecommitdiff
path: root/lib/gitlab/dependency_linker
diff options
context:
space:
mode:
authorDouwe Maan <douwe@selenight.nl>2017-05-16 15:27:50 -0500
committerDouwe Maan <douwe@selenight.nl>2017-05-23 15:35:17 -0500
commit83747783e25b2d8b60efa2cb1aa9d6bd823fcdfe (patch)
treef9a41ffe3708b3ffda738b964b8932b3a213261e /lib/gitlab/dependency_linker
parent02ad8c0c68b575ca4957e0a478714ef2144a9b8a (diff)
downloadgitlab-ce-83747783e25b2d8b60efa2cb1aa9d6bd823fcdfe.tar.gz
Autolink package names in package.json
Diffstat (limited to 'lib/gitlab/dependency_linker')
-rw-r--r--lib/gitlab/dependency_linker/base_linker.rb4
-rw-r--r--lib/gitlab/dependency_linker/json_linker.rb63
-rw-r--r--lib/gitlab/dependency_linker/package_json_linker.rb44
3 files changed, 111 insertions, 0 deletions
diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb
index 019e670081e..15fa9f40116 100644
--- a/lib/gitlab/dependency_linker/base_linker.rb
+++ b/lib/gitlab/dependency_linker/base_linker.rb
@@ -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>}
diff --git a/lib/gitlab/dependency_linker/json_linker.rb b/lib/gitlab/dependency_linker/json_linker.rb
new file mode 100644
index 00000000000..021347b6429
--- /dev/null
+++ b/lib/gitlab/dependency_linker/json_linker.rb
@@ -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
diff --git a/lib/gitlab/dependency_linker/package_json_linker.rb b/lib/gitlab/dependency_linker/package_json_linker.rb
new file mode 100644
index 00000000000..330c95f0880
--- /dev/null
+++ b/lib/gitlab/dependency_linker/package_json_linker.rb
@@ -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