diff --git a/doc/user/project/repository/code_suggestions/repository_xray.md b/doc/user/project/repository/code_suggestions/repository_xray.md
index e593fe12e15ce8a170f11d8e2eb9f690a0d0f90b..5d5ce745cd286286177869a0ffe0098bf58c35b2 100644
--- a/doc/user/project/repository/code_suggestions/repository_xray.md
+++ b/doc/user/project/repository/code_suggestions/repository_xray.md
@@ -46,6 +46,7 @@ The Repository X-Ray searches a maximum of two directory levels from the reposit
 | Go         | Go Modules      | `go.mod`                         | 17.4 or later  |
 | Java       | Gradle          | `build.gradle`                   | 17.4 or later  |
 | Java       | Maven           | `pom.xml`                        | 17.4 or later  |
+| JavaScript | NPM             | `package-lock.json`              | 17.5 or later  |
 | Kotlin     | Gradle          | `build.gradle.kts`               | 17.5 or later  |
 | PHP        | Composer        | `composer.lock`, `composer.json` | 17.5 or later  |
 | Python     | Conda           | `environment.yml`                | 17.5 or later  |
diff --git a/ee/lib/ai/context/dependencies/config_files/constants.rb b/ee/lib/ai/context/dependencies/config_files/constants.rb
index 5b1b8e73561efd2e1a717db019dc986bb07cc72a..5cb1324c8abf10ac772913a01eb32b22bdf65087 100644
--- a/ee/lib/ai/context/dependencies/config_files/constants.rb
+++ b/ee/lib/ai/context/dependencies/config_files/constants.rb
@@ -26,6 +26,7 @@ module Constants
             ConfigFiles::GoModules,
             ConfigFiles::JavaGradle,
             ConfigFiles::JavaMaven,
+            ConfigFiles::JavascriptNpmLock,
             ConfigFiles::KotlinGradle,
             ConfigFiles::PhpComposerLock,
             ConfigFiles::PhpComposer,
diff --git a/ee/lib/ai/context/dependencies/config_files/javascript_npm_lock.rb b/ee/lib/ai/context/dependencies/config_files/javascript_npm_lock.rb
new file mode 100644
index 0000000000000000000000000000000000000000..091e246ebbedb5d2d17cf8cbc0f2f9881a7a404d
--- /dev/null
+++ b/ee/lib/ai/context/dependencies/config_files/javascript_npm_lock.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Ai
+  module Context
+    module Dependencies
+      module ConfigFiles
+        class JavascriptNpmLock < Base
+          NAME_PREFIX = 'node_modules/'
+          PUBLIC_REGISTRY_PREFIX = 'https://registry.npmjs.org/'
+
+          def self.file_name_glob
+            'package-lock.json'
+          end
+
+          def self.lang_name
+            'JavaScript'
+          end
+
+          private
+
+          ### The first package is always an empty string representing the project itself
+          ### Example format:
+          #
+          # "packages": {
+          #   "": {
+          #     "name": "countly-server",
+          #     "version": "24.5.0"
+          #   },
+          #   "api/utils/countly-root": {
+          #     "version": "0.1.0"
+          #   },
+          #   "node_modules/@babel/core/node_modules/convert-source-map": {
+          #     "version": "2.0.0",
+          #     "resolved": "https://registry.npmjs.org/...",
+          #     "integrity": "sha512-...",
+          #     "dev": true,
+          #     "license": "MIT"
+          #   }
+          # }
+          #
+          def extract_libs
+            parsed = ::Gitlab::Json.parse(content)
+
+            dig_in(parsed, 'packages').try(:filter_map) do |name, dep|
+              next if name.empty?
+              next unless dig_in(dep, 'resolved')&.start_with?(PUBLIC_REGISTRY_PREFIX)
+
+              Lib.new(name: name.delete_prefix(NAME_PREFIX), version: dig_in(dep, 'version'))
+            end
+          rescue JSON::ParserError
+            raise ParsingError, 'content is not valid JSON'
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/lib/ai/context/dependencies/config_files/javascript_npm_lock_spec.rb b/ee/spec/lib/ai/context/dependencies/config_files/javascript_npm_lock_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bcc763339a679095b331e074090798713a687c57
--- /dev/null
+++ b/ee/spec/lib/ai/context/dependencies/config_files/javascript_npm_lock_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ai::Context::Dependencies::ConfigFiles::JavascriptNpmLock, feature_category: :code_suggestions do
+  it 'returns the expected language value' do
+    expect(described_class.lang).to eq('javascript')
+  end
+
+  it_behaves_like 'parsing a valid dependency config file' do
+    let(:config_file_content) do
+      <<~JSON
+        {
+          "name": "countly-server",
+          "version": "24.5.0",
+          "lockfileVersion": 3,
+          "requires": true,
+          "packages": {
+            "": {
+              "name": "countly-server",
+              "version": "24.5.0"
+            },
+            "api/utils/countly-root": {
+              "version": "0.1.0"
+            },
+            "node_modules/@babel/core/node_modules/convert-source-map": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/...",
+              "integrity": "sha512-...",
+              "dev": true,
+              "license": "MIT"
+            },
+            "node_modules/@babel/test-package": {
+              "version": "1.2.3",
+              "resolved": "https://registry.npmjs.org/...",
+              "integrity": "sha512-...",
+              "dev": true,
+              "license": "MIT"
+            }
+          }
+        }
+      JSON
+    end
+
+    let(:expected_formatted_lib_names) do
+      ['@babel/core/node_modules/convert-source-map (2.0.0)', '@babel/test-package (1.2.3)']
+    end
+  end
+
+  it_behaves_like 'parsing an invalid dependency config file' do
+    let(:expected_parsing_error_message) { 'content is not valid JSON' }
+  end
+
+  describe '.matches?' do
+    using RSpec::Parameterized::TableSyntax
+
+    where(:path, :matches) do
+      'package-lock.json'             | true
+      'dir/package-lock.json'         | true
+      'dir/subdir/package-lock.json'  | true
+      'dir/package.json'              | false
+      'Package-lock.json'             | false
+      'package_lock.json'             | false
+    end
+
+    with_them do
+      it 'matches the file name glob pattern at various directory levels' do
+        expect(described_class.matches?(path)).to eq(matches)
+      end
+    end
+  end
+end