Class: Bibliothecary::Parsers::NPM
- Inherits:
-
Object
- Object
- Bibliothecary::Parsers::NPM
- Includes:
- Analyser
- Defined in:
- lib/bibliothecary/parsers/npm.rb
Constant Summary collapse
- PACKAGE_LOCK_JSON_MAX_DEPTH =
Max depth to recurse into the “dependencies” property of package-lock.json
10
Class Method Summary collapse
- .lockfile_preference_order(file_infos) ⇒ Object
- .mapping ⇒ Object
-
.parse_ls(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument.
-
.parse_manifest(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument.
-
.parse_package_lock(file_contents, options: {}) ⇒ Object
(also: parse_shrinkwrap)
rubocop:disable Lint/UnusedMethodArgument.
- .parse_package_lock_deps_recursively(dependencies, depth = 1) ⇒ Object
- .parse_package_lock_v1(manifest) ⇒ Object
- .parse_package_lock_v2(manifest) ⇒ Object
-
.parse_yarn_lock(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument.
Methods included from Analyser
create_analysis, create_error_analysis, included
Class Method Details
.lockfile_preference_order(file_infos) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/bibliothecary/parsers/npm.rb', line 128 def self.lockfile_preference_order(file_infos) files = file_infos.each_with_object({}) do |file_info, obj| obj[File.basename(file_info.full_path)] = file_info end if files["npm-shrinkwrap.json"] [files["npm-shrinkwrap.json"]] + files.values.reject { |fi| File.basename(fi.full_path) == "npm-shrinkwrap.json" } else files.values end end |
.mapping ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/bibliothecary/parsers/npm.rb', line 11 def self.mapping { match_filename("package.json") => { kind: "manifest", parser: :parse_manifest, }, match_filename("npm-shrinkwrap.json") => { kind: "lockfile", parser: :parse_shrinkwrap, }, match_filename("yarn.lock") => { kind: "lockfile", parser: :parse_yarn_lock, }, match_filename("package-lock.json") => { kind: "lockfile", parser: :parse_package_lock, }, match_filename("npm-ls.json") => { kind: "lockfile", parser: :parse_ls, }, } end |
.parse_ls(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
122 123 124 125 126 |
# File 'lib/bibliothecary/parsers/npm.rb', line 122 def self.parse_ls(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument manifest = JSON.parse(file_contents) transform_tree_to_array(manifest.fetch("dependencies", {})) end |
.parse_manifest(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/bibliothecary/parsers/npm.rb', line 95 def self.parse_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument manifest = JSON.parse(file_contents) raise "appears to be a lockfile rather than manifest format" if manifest.key?("lockfileVersion") ( map_dependencies(manifest, "dependencies", "runtime") + map_dependencies(manifest, "devDependencies", "development") ) .reject { |dep| dep[:name].start_with?("//") } # Omit comment keys. They are valid in package.json: https://groups.google.com/g/nodejs/c/NmL7jdeuw0M/m/yTqI05DRQrIJ end |
.parse_package_lock(file_contents, options: {}) ⇒ Object Also known as: parse_shrinkwrap
rubocop:disable Lint/UnusedMethodArgument
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/bibliothecary/parsers/npm.rb', line 40 def self.parse_package_lock(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument manifest = JSON.parse(file_contents) # https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#lockfileversion if manifest["lockfileVersion"].to_i <= 1 # lockfileVersion 1 uses the "dependencies" object parse_package_lock_v1(manifest) else # lockfileVersion 2 has backwards-compatability by including both "packages" and the legacy "dependencies" object # lockfileVersion 3 has no backwards-compatibility and only includes the "packages" object parse_package_lock_v2(manifest) end end |
.parse_package_lock_deps_recursively(dependencies, depth = 1) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/bibliothecary/parsers/npm.rb', line 76 def self.parse_package_lock_deps_recursively(dependencies, depth=1) dependencies.flat_map do |name, requirement| type = requirement.fetch("dev", false) ? "development" : "runtime" version = requirement.key?("from") ? requirement["from"][/#(?:semver:)?v?(.*)/, 1] : nil version ||= requirement["version"].split("#").last child_dependencies = if depth >= PACKAGE_LOCK_JSON_MAX_DEPTH [] else parse_package_lock_deps_recursively(requirement.fetch("dependencies", []), depth + 1) end [{ name: name, requirement: version, type: type, }] + child_dependencies end end |
.parse_package_lock_v1(manifest) ⇒ Object
58 59 60 |
# File 'lib/bibliothecary/parsers/npm.rb', line 58 def self.parse_package_lock_v1(manifest) parse_package_lock_deps_recursively(manifest.fetch("dependencies", [])) end |
.parse_package_lock_v2(manifest) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/bibliothecary/parsers/npm.rb', line 62 def self.parse_package_lock_v2(manifest) # "packages" is a flat object where each key is the installed location of the dep, e.g. node_modules/foo/node_modules/bar. manifest .fetch("packages") .reject { |name, _dep| name == "" } # this is the lockfile's package itself .map do |name, dep| { name: name.split("node_modules/").last, requirement: dep["version"], type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime", } end end |
.parse_yarn_lock(file_contents, options: {}) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/bibliothecary/parsers/npm.rb', line 106 def self.parse_yarn_lock(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument response = Typhoeus.post("#{Bibliothecary.configuration.yarn_parser_host}/parse", body: file_contents) raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.yarn_parser_host}/parse", response.response_code) unless response.success? json = JSON.parse(response.body, symbolize_names: true) json.uniq.map do |dep| { name: dep[:name], requirement: dep[:version], lockfile_requirement: dep[:requirement], type: dep[:type], } end end |