mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 20:15:42 +00:00
114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package skills
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type npmPackageBinManifest struct {
|
|
Name string `json:"name"`
|
|
Bin json.RawMessage `json:"bin"`
|
|
}
|
|
|
|
func findNpmPackageExecutableAlias(name string) (string, bool) {
|
|
packageDirs, err := npmGlobalPackageDirs()
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
for _, packageDir := range packageDirs {
|
|
raw, err := os.ReadFile(filepath.Join(packageDir, "package.json"))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
var manifest npmPackageBinManifest
|
|
if err := json.Unmarshal(raw, &manifest); err != nil {
|
|
continue
|
|
}
|
|
if !npmPackageNameMatchesExecutableAlias(manifest.Name, name) {
|
|
continue
|
|
}
|
|
binName, ok := singleNpmBinName(manifest)
|
|
if !ok {
|
|
continue
|
|
}
|
|
path := filepath.Join(npmGlobalBinDir(), binName)
|
|
if IsExecutableFile(path) {
|
|
return path, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func npmGlobalPackageDirs() ([]string, error) {
|
|
root := filepath.Join(npmGlobalPrefix(), "lib", "node_modules")
|
|
entries, err := os.ReadDir(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dirs := make([]string, 0, len(entries))
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
continue
|
|
}
|
|
path := filepath.Join(root, entry.Name())
|
|
if strings.HasPrefix(entry.Name(), "@") {
|
|
scoped, err := os.ReadDir(path)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, scopedEntry := range scoped {
|
|
if scopedEntry.IsDir() {
|
|
dirs = append(dirs, filepath.Join(path, scopedEntry.Name()))
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
dirs = append(dirs, path)
|
|
}
|
|
return dirs, nil
|
|
}
|
|
|
|
func npmPackageNameMatchesExecutableAlias(packageName, executableName string) bool {
|
|
base := npmPackageBaseName(packageName)
|
|
if base == "" {
|
|
return false
|
|
}
|
|
if base == executableName {
|
|
return true
|
|
}
|
|
return strings.TrimSuffix(base, "-cli") == executableName
|
|
}
|
|
|
|
func npmPackageBaseName(packageName string) string {
|
|
packageName = strings.TrimSpace(packageName)
|
|
if packageName == "" {
|
|
return ""
|
|
}
|
|
if slash := strings.LastIndexByte(packageName, '/'); slash >= 0 {
|
|
return packageName[slash+1:]
|
|
}
|
|
return packageName
|
|
}
|
|
|
|
func singleNpmBinName(manifest npmPackageBinManifest) (string, bool) {
|
|
if len(manifest.Bin) == 0 || string(manifest.Bin) == "null" {
|
|
return "", false
|
|
}
|
|
var binPath string
|
|
if err := json.Unmarshal(manifest.Bin, &binPath); err == nil {
|
|
return npmPackageBaseName(manifest.Name), strings.TrimSpace(binPath) != ""
|
|
}
|
|
var bins map[string]string
|
|
if err := json.Unmarshal(manifest.Bin, &bins); err != nil || len(bins) != 1 {
|
|
return "", false
|
|
}
|
|
for name, path := range bins {
|
|
if strings.TrimSpace(name) != "" && strings.TrimSpace(path) != "" {
|
|
return name, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|