mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add separate package manager and resolver
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package packagemanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
)
|
||||
|
||||
type NpmPackageManagerConfig struct{}
|
||||
|
||||
func DefaultNpmPackageManagerConfig() NpmPackageManagerConfig {
|
||||
return NpmPackageManagerConfig{}
|
||||
}
|
||||
|
||||
type npmPackageManager struct {
|
||||
Config NpmPackageManagerConfig
|
||||
}
|
||||
|
||||
func NewNpmPackageManager(config NpmPackageManagerConfig) (*npmPackageManager, error) {
|
||||
return &npmPackageManager{
|
||||
Config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (npm *npmPackageManager) Name() string {
|
||||
return "npm"
|
||||
}
|
||||
|
||||
func (npm *npmPackageManager) ParseCommand(args []string) (*ParsedCommand, error) {
|
||||
// No command specified
|
||||
if len(args) < 2 {
|
||||
return &ParsedCommand{
|
||||
Command: Command{Args: args},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check if this is an install command
|
||||
if !npm.isInstallCommand(args[1]) {
|
||||
return &ParsedCommand{
|
||||
Command: Command{Args: args},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Extract packages from args
|
||||
var packages []string
|
||||
for i := 2; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
packages = append(packages, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// No packages specified
|
||||
if len(packages) == 0 {
|
||||
return &ParsedCommand{
|
||||
Command: Command{Args: args},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Process all package arguments
|
||||
var installTargets []*PackageInstallTarget
|
||||
for _, pkg := range packages {
|
||||
packageName, version, err := npmParsePackageInfo(pkg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse package info: %w", err)
|
||||
}
|
||||
|
||||
// Clean version if specified
|
||||
if version != "" {
|
||||
version = npmCleanVersion(version)
|
||||
}
|
||||
|
||||
installTargets = append(installTargets, &PackageInstallTarget{
|
||||
PackageVersion: &packagev1.PackageVersion{
|
||||
Package: &packagev1.Package{
|
||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||
Name: packageName,
|
||||
},
|
||||
Version: version,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return &ParsedCommand{
|
||||
Command: Command{Args: args},
|
||||
InstallTargets: installTargets,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (npm *npmPackageManager) isInstallCommand(cmd string) bool {
|
||||
return cmd == "install" || cmd == "i" || cmd == "add"
|
||||
}
|
||||
|
||||
func npmParsePackageInfo(input string) (packageName, version string, err error) {
|
||||
if input == "" {
|
||||
return "", "", fmt.Errorf("package info cannot be empty")
|
||||
}
|
||||
|
||||
input = strings.TrimSpace(input)
|
||||
if strings.HasPrefix(input, "@") {
|
||||
// Scoped package (e.g. @types/node or @types/node@1.0.0)
|
||||
lastAtIndex := strings.LastIndex(input, "@")
|
||||
if lastAtIndex > 0 {
|
||||
packageName = strings.TrimSpace(input[:lastAtIndex])
|
||||
version = strings.TrimSpace(input[lastAtIndex+1:])
|
||||
return packageName, version, nil
|
||||
}
|
||||
|
||||
// If no version specifier, return the whole input as package name
|
||||
return strings.TrimSpace(input), "", nil
|
||||
}
|
||||
|
||||
// Normal package (e.g. lodash or lodash@4.17.21)
|
||||
parts := strings.Split(input, "@")
|
||||
if len(parts) == 2 {
|
||||
packageName = strings.TrimSpace(parts[0])
|
||||
version = strings.TrimSpace(parts[1])
|
||||
return packageName, version, nil
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
packageName = strings.TrimSpace(parts[0])
|
||||
return packageName, "", nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("invalid format: expected 'package' OR 'package@version', got '%s'", input)
|
||||
}
|
||||
|
||||
func npmCleanVersion(version string) string {
|
||||
version = strings.TrimPrefix(version, "^")
|
||||
version = strings.TrimPrefix(version, "~")
|
||||
if version == "*" {
|
||||
return "latest"
|
||||
}
|
||||
|
||||
return version
|
||||
}
|
||||
Reference in New Issue
Block a user