From 59676b2da13f60451669f03e0432fe0a0082913e Mon Sep 17 00:00:00 2001 From: Adva Oren Date: Wed, 13 May 2026 14:24:15 +0300 Subject: [PATCH 1/2] feat: expose package manager name in componentAnalysis result Attach the matched provider's command name (e.g. pip, uv, poetry) as `packageManager` on the component analysis result so IDE consumers can display provider-specific instructions without re-implementing detection. Jira: TC-4336 Co-Authored-By: Claude Opus 4.6 --- src/index.js | 6 +++++- src/providers/python_pip.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index a3449b76..38b50ab1 100644 --- a/src/index.js +++ b/src/index.js @@ -210,7 +210,11 @@ async function componentAnalysis(manifest, opts = {}) { fs.accessSync(manifest, fs.constants.R_OK) opts["manifest-type"] = path.basename(manifest) let provider = match(manifest, availableProviders, opts) // throws error if no matching provider - return await analysis.requestComponent(provider, manifest, theUrl, opts) // throws error request sending failed + const result = await analysis.requestComponent(provider, manifest, theUrl, opts) // throws error request sending failed + if (typeof provider._cmdName === 'function') { + result.packageManager = provider._cmdName() + } + return result } /** diff --git a/src/providers/python_pip.js b/src/providers/python_pip.js index 7a740bd0..77d39906 100644 --- a/src/providers/python_pip.js +++ b/src/providers/python_pip.js @@ -14,7 +14,7 @@ import { import Python_controller from './python_controller.js' import { getParser, getIgnoreQuery, getPinnedVersionQuery } from './requirements_parser.js' -export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest } +export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, _cmdName() { return 'pip' } } /** @typedef {{name: string, version: string, dependencies: DependencyEntry[], hashes?: Array<{alg: string, content: string}>}} DependencyEntry */ From e2cc1c0f9a2e9612a8563bc7e574bbde41318837 Mon Sep 17 00:00:00 2001 From: Adva Oren Date: Mon, 18 May 2026 11:13:02 +0300 Subject: [PATCH 2/2] refactor: add packageManagerName as public API for all providers Keep _cmdName as the internal/protected method for binary resolution and add packageManagerName() as the proper public method on every provider. This ensures result.packageManager is always set for all providers, not just when _cmdName happens to exist. Jira: TC-4336 Co-Authored-By: Claude Opus 4.6 --- src/index.js | 4 +--- src/provider.js | 2 +- src/providers/base_java.js | 8 ++++++++ src/providers/base_javascript.js | 8 ++++++++ src/providers/base_pyproject.js | 8 ++++++++ src/providers/golang_gomodules.js | 2 +- src/providers/python_pip.js | 2 +- src/providers/rust_cargo.js | 2 +- 8 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 9fec9275..9c280a58 100644 --- a/src/index.js +++ b/src/index.js @@ -211,9 +211,7 @@ async function componentAnalysis(manifest, opts = {}) { opts["manifest-type"] = path.basename(manifest) let provider = match(manifest, availableProviders, opts) // throws error if no matching provider const result = await analysis.requestComponent(provider, manifest, theUrl, opts) // throws error request sending failed - if (typeof provider._cmdName === 'function') { - result.packageManager = provider._cmdName() - } + result.packageManager = provider.packageManagerName() return result } diff --git a/src/provider.js b/src/provider.js index 9c6df1c5..f426c091 100644 --- a/src/provider.js +++ b/src/provider.js @@ -15,7 +15,7 @@ import Python_uv from './providers/python_uv.js' import rustCargoProvider from './providers/rust_cargo.js' /** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */ -/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise, provideStack: function(string, {}): Provided | Promise, readLicenseFromManifest: function(string): string | null}} Provider */ +/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise, provideStack: function(string, {}): Provided | Promise, readLicenseFromManifest: function(string): string | null, packageManagerName: function(): string}} Provider */ /** * MUST include all providers here. diff --git a/src/providers/base_java.js b/src/providers/base_java.js index 9d328967..4e9b8916 100644 --- a/src/providers/base_java.js +++ b/src/providers/base_java.js @@ -35,6 +35,14 @@ export default class Base_Java { this.localWrapper = localWrapper } + /** + * Returns the package manager name (e.g. mvn, gradle) + * @returns {string} + */ + packageManagerName() { + return this.globalBinary + } + /** * Recursively populates the SBOM instance with the parsed graph * @param {string} src - Source dependency to start the calculations from diff --git a/src/providers/base_javascript.js b/src/providers/base_javascript.js index ddc4c11a..ae999fe7 100644 --- a/src/providers/base_javascript.js +++ b/src/providers/base_javascript.js @@ -82,6 +82,14 @@ export default class Base_javascript { throw new TypeError("_cmdName must be implemented"); } + /** + * Returns the package manager name (e.g. npm, yarn, pnpm, bun) + * @returns {string} The package manager name + */ + packageManagerName() { + return this._cmdName(); + } + /** * Returns the command arguments for listing dependencies * @returns {Array} The command arguments diff --git a/src/providers/base_pyproject.js b/src/providers/base_pyproject.js index 1d0f0034..ee0fe5a6 100644 --- a/src/providers/base_pyproject.js +++ b/src/providers/base_pyproject.js @@ -168,6 +168,14 @@ export default class Base_pyproject { throw new TypeError('_cmdName must be implemented') } + /** + * Returns the package manager name (e.g. pip, poetry, uv) + * @returns {string} + */ + packageManagerName() { + return this._cmdName() + } + /** * Resolve dependencies using the tool-specific command and parser. * diff --git a/src/providers/golang_gomodules.js b/src/providers/golang_gomodules.js index 559aa304..395168c7 100644 --- a/src/providers/golang_gomodules.js +++ b/src/providers/golang_gomodules.js @@ -10,7 +10,7 @@ import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } import { getParser, getRequireQuery } from './gomod_parser.js' -export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest } +export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'go' } } /** @typedef {import('../provider').Provider} */ diff --git a/src/providers/python_pip.js b/src/providers/python_pip.js index 77d39906..f25ed468 100644 --- a/src/providers/python_pip.js +++ b/src/providers/python_pip.js @@ -14,7 +14,7 @@ import { import Python_controller from './python_controller.js' import { getParser, getIgnoreQuery, getPinnedVersionQuery } from './requirements_parser.js' -export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, _cmdName() { return 'pip' } } +export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'pip' } } /** @typedef {{name: string, version: string, dependencies: DependencyEntry[], hashes?: Array<{alg: string, content: string}>}} DependencyEntry */ diff --git a/src/providers/rust_cargo.js b/src/providers/rust_cargo.js index bfe62eba..ef480c67 100644 --- a/src/providers/rust_cargo.js +++ b/src/providers/rust_cargo.js @@ -8,7 +8,7 @@ import { getLicense } from '../license/license_utils.js' import Sbom from '../sbom.js' import { getCustom, getCustomPath, invokeCommand } from '../tools.js' -export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest } +export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'cargo' } } /** @typedef {import('../provider').Provider} */