diff --git a/completers/common/pnpm_completer/cmd/catFile.go b/completers/common/pnpm_completer/cmd/catFile.go
new file mode 100644
index 0000000000..ee618e5369
--- /dev/null
+++ b/completers/common/pnpm_completer/cmd/catFile.go
@@ -0,0 +1,20 @@
+package cmd
+
+import (
+ "github.com/carapace-sh/carapace"
+ "github.com/spf13/cobra"
+)
+
+var catFileCmd = &cobra.Command{
+ Use: "cat-file",
+ Short: "Prints the contents of a file based on the hash value stored in the index file",
+ Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() {
+ carapace.Gen(catFileCmd).Standalone()
+
+ catFileCmd.Flags().BoolP("help", "h", false, "Output usage information")
+
+ rootCmd.AddCommand(catFileCmd)
+}
diff --git a/completers/common/pnpm_completer/cmd/catIndex.go b/completers/common/pnpm_completer/cmd/catIndex.go
new file mode 100644
index 0000000000..d933da37cf
--- /dev/null
+++ b/completers/common/pnpm_completer/cmd/catIndex.go
@@ -0,0 +1,20 @@
+package cmd
+
+import (
+ "github.com/carapace-sh/carapace"
+ "github.com/spf13/cobra"
+)
+
+var catIndexCmd = &cobra.Command{
+ Use: "cat-index",
+ Short: "Prints the index file of a specific package from the store",
+ Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() {
+ carapace.Gen(catIndexCmd).Standalone()
+
+ catIndexCmd.Flags().BoolP("help", "h", false, "Output usage information")
+
+ rootCmd.AddCommand(catIndexCmd)
+}
diff --git a/completers/common/pnpm_completer/cmd/exec.go b/completers/common/pnpm_completer/cmd/exec.go
index 523e08ab23..4ce92e77a9 100644
--- a/completers/common/pnpm_completer/cmd/exec.go
+++ b/completers/common/pnpm_completer/cmd/exec.go
@@ -2,24 +2,46 @@ package cmd
import (
"github.com/carapace-sh/carapace"
- "github.com/carapace-sh/carapace-bridge/pkg/actions/bridge"
+ "github.com/carapace-sh/carapace-bin/pkg/actions/tools/pnpm"
"github.com/spf13/cobra"
)
var execCmd = &cobra.Command{
- Use: "exec",
- Short: "Executes a shell command in scope of a project",
- GroupID: "run",
- Run: func(cmd *cobra.Command, args []string) {},
- DisableFlagParsing: true,
+ Use: "exec",
+ Short: "Executes a shell command in scope of a project",
+ GroupID: "run",
+ Run: func(cmd *cobra.Command, args []string) {},
}
func init() {
carapace.Gen(execCmd).Standalone()
+ execCmd.Flags().Bool("aggregate-output", false, "Aggregate output from child processes that are run in parallel")
+ execCmd.Flags().String("changed-files-ignore-pattern", "", "Defines files to ignore when filtering for changed projects")
+ execCmd.Flags().Bool("color", false, "Controls colors in the output")
+ execCmd.Flags().StringP("dir", "C", "", "Change to directory
")
+ execCmd.Flags().String("filter", "", "set filter")
+ execCmd.Flags().String("filter-prod", "", "Restricts the scope to package names matching the given pattern")
+ execCmd.Flags().BoolP("help", "h", false, "Output usage information")
+ execCmd.Flags().String("loglevel", "", "What level of logs to report")
+ execCmd.Flags().Bool("no-bail", false, "The command will exit with a 0 exit code even if the script fails")
+ execCmd.Flags().Bool("no-color", false, "Controls colors in the output")
+ execCmd.Flags().Bool("parallel", false, "Completely disregard concurrency and topological sorting")
+ execCmd.Flags().BoolP("recursive", "r", false, "Run the defined package script in every package found in subdirectories")
+ execCmd.Flags().Bool("report-summary", false, "Save the execution results of every package to \"pnpm-exec-summary.json\"")
+ execCmd.Flags().Bool("resume-from", false, "Command executed from given package")
+ execCmd.Flags().Bool("sequential", false, "Run the specified scripts one by one")
+ execCmd.Flags().Bool("stream", false, "Stream output from child processes immediately")
+ execCmd.Flags().String("test-pattern", "", "Defines files related to tests.")
+ execCmd.Flags().Bool("use-stderr", false, "Divert all output to stderr")
+ execCmd.Flags().Bool("workspace-root", false, "Run the command on the root workspace project")
+ addWorkspaceFlags(execCmd)
rootCmd.AddCommand(execCmd)
- carapace.Gen(execCmd).PositionalAnyCompletion(
- bridge.ActionCarapaceBin(),
- )
+ carapace.Gen(execCmd).FlagCompletion(carapace.ActionMap{
+ "dir": carapace.ActionDirectories(),
+ "filter": pnpm.ActionFilter(),
+ "filter-prod": pnpm.ActionFilter(),
+ "loglevel": pnpm.ActionLoglevel(),
+ })
}
diff --git a/completers/common/pnpm_completer/cmd/findHash.go b/completers/common/pnpm_completer/cmd/findHash.go
new file mode 100644
index 0000000000..6063325a4a
--- /dev/null
+++ b/completers/common/pnpm_completer/cmd/findHash.go
@@ -0,0 +1,20 @@
+package cmd
+
+import (
+ "github.com/carapace-sh/carapace"
+ "github.com/spf13/cobra"
+)
+
+var findHashCmd = &cobra.Command{
+ Use: "find-hash",
+ Short: "Experimental! Lists the packages that include the file with the specified hash",
+ Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() {
+ carapace.Gen(findHashCmd).Standalone()
+
+ findHashCmd.Flags().BoolP("help", "h", false, "Output usage information")
+
+ rootCmd.AddCommand(findHashCmd)
+}
diff --git a/completers/common/pnpm_completer/cmd/help.go b/completers/common/pnpm_completer/cmd/help.go
new file mode 100644
index 0000000000..8889f92a4d
--- /dev/null
+++ b/completers/common/pnpm_completer/cmd/help.go
@@ -0,0 +1,20 @@
+package cmd
+
+import (
+ "github.com/carapace-sh/carapace"
+ "github.com/spf13/cobra"
+)
+
+var helpCmd = &cobra.Command{
+ Use: "help",
+ Short: "Show help for pnpm",
+ Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() {
+ carapace.Gen(helpCmd).Standalone()
+
+ helpCmd.Flags().BoolP("help", "h", false, "Output usage information")
+
+ rootCmd.AddCommand(helpCmd)
+}
diff --git a/completers/common/pnpm_completer/cmd/init.go b/completers/common/pnpm_completer/cmd/init.go
new file mode 100644
index 0000000000..d180e82d3a
--- /dev/null
+++ b/completers/common/pnpm_completer/cmd/init.go
@@ -0,0 +1,28 @@
+package cmd
+
+import (
+ "github.com/carapace-sh/carapace"
+ "github.com/spf13/cobra"
+)
+
+var initCmd = &cobra.Command{
+ Use: "init",
+ Short: "Create a package.json file",
+ Run: func(cmd *cobra.Command, args []string) {},
+}
+
+func init() {
+ carapace.Gen(initCmd).Standalone()
+
+ initCmd.Flags().BoolP("help", "h", false, "Output usage information")
+ initCmd.Flags().String("name", "", "Set the name field in package.json")
+ initCmd.Flags().String("version", "", "Set the version field in package.json")
+ initCmd.Flags().String("description", "", "Set the description field in package.json")
+ initCmd.Flags().String("author", "", "Set the author field in package.json")
+ initCmd.Flags().String("license", "", "Set the license field in package.json")
+ initCmd.Flags().String("homepage", "", "Set the homepage field in package.json")
+ initCmd.Flags().String("repository", "", "Set the repository field in package.json")
+ initCmd.Flags().String("keywords", "", "Set the keywords field in package.json")
+
+ rootCmd.AddCommand(initCmd)
+}
diff --git a/completers/common/pnpm_completer/cmd/rebuild.go b/completers/common/pnpm_completer/cmd/rebuild.go
index 3241209c56..5d79a4b582 100644
--- a/completers/common/pnpm_completer/cmd/rebuild.go
+++ b/completers/common/pnpm_completer/cmd/rebuild.go
@@ -42,6 +42,9 @@ func init() {
})
carapace.Gen(rebuildCmd).PositionalAnyCompletion(
- pnpm.ActionDependencies(),
+ carapace.Batch(
+ pnpm.ActionDependencies(),
+ pnpm.ActionWorkspaceDependencies(),
+ ).ToA(),
)
}
diff --git a/completers/common/pnpm_completer/cmd/remove.go b/completers/common/pnpm_completer/cmd/remove.go
index 2686644165..24e37f2b62 100644
--- a/completers/common/pnpm_completer/cmd/remove.go
+++ b/completers/common/pnpm_completer/cmd/remove.go
@@ -45,6 +45,9 @@ func init() {
})
carapace.Gen(removeCmd).PositionalAnyCompletion(
- pnpm.ActionDependencies(),
+ carapace.Batch(
+ pnpm.ActionDependencies(),
+ pnpm.ActionWorkspaceDependencies(),
+ ).ToA(),
)
}
diff --git a/completers/common/pnpm_completer/cmd/root.go b/completers/common/pnpm_completer/cmd/root.go
index 060d856107..6488ae9474 100644
--- a/completers/common/pnpm_completer/cmd/root.go
+++ b/completers/common/pnpm_completer/cmd/root.go
@@ -2,6 +2,7 @@ package cmd
import (
"github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace-bin/pkg/actions/tools/pnpm"
"github.com/spf13/cobra"
)
@@ -15,6 +16,7 @@ var rootCmd = &cobra.Command{
func Execute() error {
return rootCmd.Execute()
}
+
func init() {
carapace.Gen(rootCmd).Standalone()
@@ -23,9 +25,33 @@ func init() {
&cobra.Group{ID: "review", Title: "Review Commands"},
&cobra.Group{ID: "run", Title: "Run Commands"},
&cobra.Group{ID: "store", Title: "Store Commands"},
+ &cobra.Group{ID: "other", Title: "Other Commands"},
)
rootCmd.Flags().BoolP("help", "h", false, "show help")
rootCmd.Flags().BoolP("recursive", "r", false, "Run the command for each project in the workspace")
rootCmd.Flags().BoolP("version", "v", false, "show version")
+ rootCmd.Flags().String("filter", "", "set filter")
+ rootCmd.Flags().String("filter-prod", "", "Restricts the scope to package names matching the given pattern")
+ rootCmd.Flags().String("loglevel", "", "What level of logs to report")
+ rootCmd.Flags().Bool("color", false, "Controls colors in the output")
+ rootCmd.Flags().Bool("no-color", false, "Controls colors in the output")
+
+ carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{
+ "filter": pnpm.ActionFilter(),
+ "filter-prod": pnpm.ActionFilter(),
+ "loglevel": pnpm.ActionLoglevel(),
+ })
+}
+
+func addWorkspaceFlags(cmd *cobra.Command) {
+ cmd.Flags().StringArrayP("workspace", "w", []string{""}, "Enable running a command in the context of the given workspace")
+ cmd.Flags().Bool("workspaces", false, "Enable running a command in the context of all workspaces")
+
+ carapace.Gen(cmd).FlagCompletion(carapace.ActionMap{
+ "workspace": carapace.Batch(
+ pnpm.ActionWorkspaces(),
+ pnpm.ActionWorkspaceDependencies(),
+ ).ToA(),
+ })
}
diff --git a/completers/common/pnpm_completer/cmd/root_root.go b/completers/common/pnpm_completer/cmd/root_root.go
index d0e645f626..9d977a1e80 100644
--- a/completers/common/pnpm_completer/cmd/root_root.go
+++ b/completers/common/pnpm_completer/cmd/root_root.go
@@ -5,15 +5,16 @@ import (
"github.com/spf13/cobra"
)
-var root_rootCmd = &cobra.Command{
+var rootRootCmd = &cobra.Command{
Use: "root",
- Short: "Print the effective `node_modules` directory",
+ Short: "Print the effective modules directory",
Run: func(cmd *cobra.Command, args []string) {},
}
func init() {
- carapace.Gen(root_rootCmd).Standalone()
+ carapace.Gen(rootRootCmd).Standalone()
- root_rootCmd.Flags().BoolP("global", "g", false, "Print the global `node_modules` directory")
- rootCmd.AddCommand(root_rootCmd)
+ rootRootCmd.Flags().BoolP("help", "h", false, "Output usage information")
+
+ rootCmd.AddCommand(rootRootCmd)
}
diff --git a/completers/common/pnpm_completer/cmd/run.go b/completers/common/pnpm_completer/cmd/run.go
index abf375386b..be92e1c6f0 100644
--- a/completers/common/pnpm_completer/cmd/run.go
+++ b/completers/common/pnpm_completer/cmd/run.go
@@ -46,5 +46,10 @@ func init() {
"loglevel": pnpm.ActionLoglevel(),
})
- // TODO complete scripts
+ carapace.Gen(runCmd).PositionalCompletion(
+ carapace.Batch(
+ pnpm.ActionScripts(),
+ pnpm.ActionWorkspaceScripts(),
+ ).ToA(),
+ )
}
diff --git a/completers/common/pnpm_completer/cmd/start.go b/completers/common/pnpm_completer/cmd/start.go
index 5c6106eec8..4f399c3376 100644
--- a/completers/common/pnpm_completer/cmd/start.go
+++ b/completers/common/pnpm_completer/cmd/start.go
@@ -2,20 +2,46 @@ package cmd
import (
"github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace-bin/pkg/actions/tools/pnpm"
"github.com/spf13/cobra"
)
var startCmd = &cobra.Command{
- Use: "start",
- Short: "Runs an arbitrary command specified in the package's \"start\" property",
- GroupID: "run",
- Run: func(cmd *cobra.Command, args []string) {},
+ Use: "start",
+ Short: "Runs an arbitrary command specified in the package's start property of its scripts object",
+ Run: func(cmd *cobra.Command, args []string) {},
}
func init() {
carapace.Gen(startCmd).Standalone()
+ startCmd.Flags().Bool("aggregate-output", false, "Aggregate output from child processes that are run in parallel")
+ startCmd.Flags().String("changed-files-ignore-pattern", "", "Defines files to ignore when filtering for changed projects")
+ startCmd.Flags().Bool("color", false, "Controls colors in the output")
+ startCmd.Flags().StringP("dir", "C", "", "Change to directory ")
+ startCmd.Flags().String("filter", "", "set filter")
+ startCmd.Flags().String("filter-prod", "", "Restricts the scope to package names matching the given pattern")
+ startCmd.Flags().BoolP("help", "h", false, "Output usage information")
+ startCmd.Flags().Bool("if-present", false, "Avoid exiting with a non-zero exit code when the script is undefined")
+ startCmd.Flags().String("loglevel", "", "What level of logs to report")
+ startCmd.Flags().Bool("no-bail", false, "The command will exit with a 0 exit code even if the script fails")
+ startCmd.Flags().Bool("no-color", false, "Controls colors in the output")
+ startCmd.Flags().Bool("parallel", false, "Completely disregard concurrency and topological sorting")
+ startCmd.Flags().BoolP("recursive", "r", false, "Run the defined package script in every package found in subdirectories")
+ startCmd.Flags().Bool("report-summary", false, "Save the execution results of every package to \"pnpm-exec-summary.json\"")
+ startCmd.Flags().Bool("resume-from", false, "Command executed from given package")
+ startCmd.Flags().Bool("sequential", false, "Run the specified scripts one by one")
+ startCmd.Flags().Bool("stream", false, "Stream output from child processes immediately")
+ startCmd.Flags().String("test-pattern", "", "Defines files related to tests.")
+ startCmd.Flags().Bool("use-stderr", false, "Divert all output to stderr")
+ startCmd.Flags().Bool("workspace-root", false, "Run the command on the root workspace project")
+ addWorkspaceFlags(startCmd)
rootCmd.AddCommand(startCmd)
- // TODO positional completion
+ carapace.Gen(startCmd).FlagCompletion(carapace.ActionMap{
+ "dir": carapace.ActionDirectories(),
+ "filter": pnpm.ActionFilter(),
+ "filter-prod": pnpm.ActionFilter(),
+ "loglevel": pnpm.ActionLoglevel(),
+ })
}
diff --git a/completers/common/pnpm_completer/cmd/update.go b/completers/common/pnpm_completer/cmd/update.go
index 353549c3c7..a260d796ab 100644
--- a/completers/common/pnpm_completer/cmd/update.go
+++ b/completers/common/pnpm_completer/cmd/update.go
@@ -50,6 +50,9 @@ func init() {
})
carapace.Gen(updateCmd).PositionalAnyCompletion(
- pnpm.ActionDependencyNames(),
+ carapace.Batch(
+ pnpm.ActionDependencyNames(),
+ pnpm.ActionWorkspaceDependencies(),
+ ).ToA(),
)
}
diff --git a/pkg/actions/tools/pnpm/config.go b/pkg/actions/tools/pnpm/config.go
new file mode 100644
index 0000000000..b876766b3b
--- /dev/null
+++ b/pkg/actions/tools/pnpm/config.go
@@ -0,0 +1,39 @@
+package pnpm
+
+import (
+ "encoding/json"
+
+ "github.com/carapace-sh/carapace"
+)
+
+// ActionLocalConfigKeys completes local config keys
+func ActionLocalConfigKeys() carapace.Action {
+ return actionConfigKeys(false)
+}
+
+// ActionGlobalConfigKeys completes global config keys
+func ActionGlobalConfigKeys() carapace.Action {
+ return actionConfigKeys(true)
+}
+
+func actionConfigKeys(global bool) carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ args := []string{"config", "list", "--json"}
+ if global {
+ args = append(args, "--global")
+ }
+
+ return carapace.ActionExecCommand("pnpm", args...)(func(output []byte) carapace.Action {
+ var config map[string]interface{}
+ if err := json.Unmarshal(output, &config); err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ vals := make([]string, 0, len(config))
+ for key := range config {
+ vals = append(vals, key)
+ }
+ return carapace.ActionValues(vals...)
+ })
+ }).Tag("config keys")
+}
diff --git a/pkg/actions/tools/pnpm/module.go b/pkg/actions/tools/pnpm/module.go
new file mode 100644
index 0000000000..1c022eb218
--- /dev/null
+++ b/pkg/actions/tools/pnpm/module.go
@@ -0,0 +1,46 @@
+package pnpm
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace/pkg/util"
+)
+
+func nodeModulesPath(c carapace.Context) (string, error) {
+ return util.FindReverse(c.Dir, "node_modules")
+}
+
+// ActionModules completes modules
+func ActionModules() carapace.Action {
+ return carapace.ActionMultiParts("/", func(c carapace.Context) carapace.Action {
+ path, err := nodeModulesPath(c)
+ if err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ fullSegments := make([]string, 0)
+ for _, part := range c.Parts {
+ if strings.HasPrefix(part, "@") {
+ fullSegments = append(fullSegments, part)
+ } else {
+ fullSegments = append(fullSegments, part, "node_modules")
+ }
+ }
+
+ contents, err := os.ReadDir(fmt.Sprintf("%v/%v", path, strings.Join(fullSegments, "/")))
+ if err != nil {
+ return carapace.ActionValues()
+ }
+
+ vals := make([]string, 0)
+ for _, content := range contents {
+ if content.IsDir() && !strings.HasPrefix(content.Name(), ".") {
+ vals = append(vals, content.Name())
+ }
+ }
+ return carapace.ActionValues(vals...)
+ })
+}
diff --git a/pkg/actions/tools/pnpm/owner.go b/pkg/actions/tools/pnpm/owner.go
new file mode 100644
index 0000000000..69cc005af1
--- /dev/null
+++ b/pkg/actions/tools/pnpm/owner.go
@@ -0,0 +1,20 @@
+package pnpm
+
+import (
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+)
+
+// ActionOwners completes owners
+func ActionOwners(pkg string) carapace.Action {
+ return carapace.ActionExecCommand("pnpm", "owner", "ls", pkg)(func(output []byte) carapace.Action {
+ lines := strings.Split(string(output), "\n")
+
+ vals := make([]string, 0)
+ for _, line := range lines[:len(lines)-1] {
+ vals = append(vals, strings.SplitN(line, " ", 2)...)
+ }
+ return carapace.ActionValuesDescribed(vals...)
+ })
+}
diff --git a/pkg/actions/tools/pnpm/package.go b/pkg/actions/tools/pnpm/package.go
new file mode 100644
index 0000000000..fb6bc7902b
--- /dev/null
+++ b/pkg/actions/tools/pnpm/package.go
@@ -0,0 +1,117 @@
+package pnpm
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace/pkg/util"
+)
+
+// ActionPackageSearch completes packages@version for given registry
+func ActionPackageSearch(registry string) carapace.Action {
+ return carapace.ActionMultiParts("@", func(c carapace.Context) carapace.Action {
+ switch len(c.Parts) {
+ case 0:
+ return ActionPackageNames(registry).NoSpace()
+ case 1:
+ return ActionPackageVersions(PackageOpts{Registry: registry, Package: c.Parts[0]})
+ default:
+ return carapace.ActionValues()
+ }
+ })
+}
+
+// ActionPackageNames completes package names for given registry
+func ActionPackageNames(registry string) carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ args := []string{"search", "--parseable", "--searchlimit", "250", fmt.Sprintf(`/^%v`, c.Value)}
+ if registry != "" {
+ args = append(args, "--registry", registry)
+ }
+
+ // pnpm uses npm search under the hood, but we can also try pnpm-specific search
+ return carapace.ActionExecCommand("pnpm", args...)(func(output []byte) carapace.Action {
+ lines := strings.Split(string(output), "\n")
+
+ vals := make([]string, 0)
+ for _, line := range lines[:len(lines)-1] {
+ fields := strings.Split(line, "\t")
+ if len(fields) >= 2 {
+ vals = append(vals, fields[0], fields[1])
+ }
+ }
+ return carapace.ActionValuesDescribed(vals...)
+ })
+ })
+}
+
+type PackageOpts struct {
+ Registry string
+ Package string
+}
+
+// ActionPackageVersions completes versions for given package
+func ActionPackageVersions(opts PackageOpts) carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ args := []string{"view", opts.Package, "versions", "--json"}
+ if opts.Registry != "" {
+ args = append(args, "--registry", opts.Registry)
+ }
+
+ // pnpm uses npm view under the hood
+ return carapace.ActionExecCommand("pnpm", args...)(func(output []byte) carapace.Action {
+ var versions []string
+ if err := json.Unmarshal(output, &versions); err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+ return carapace.ActionValues(versions...)
+ })
+ })
+}
+
+// ActionPackageTags completes tags for given package
+func ActionPackageTags(opts PackageOpts) carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ args := []string{"view", opts.Package, "dist-tags", "--json"}
+ if opts.Registry != "" {
+ args = append(args, "--registry", opts.Registry)
+ }
+
+ // pnpm uses npm view under the hood
+ return carapace.ActionExecCommand("pnpm", args...)(func(output []byte) carapace.Action {
+ var tags map[string]string
+ if err := json.Unmarshal(output, &tags); err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ vals := make([]string, 0, len(tags)*2)
+ for tag, version := range tags {
+ vals = append(vals, tag, version)
+ }
+ return carapace.ActionValuesDescribed(vals...)
+ })
+ })
+}
+
+type packageJson struct {
+ Scripts map[string]string
+ Workspaces []string
+ Dependencies map[string]string
+ DevDependencies map[string]string `json:"devDependencies"`
+ OptionalDependencies map[string]string `json:"optionalDependencies"`
+ PeerDependencies map[string]string `json:"peerDependencies"`
+}
+
+func loadPackageJson(c carapace.Context) (pj packageJson, err error) {
+ var packageFile string
+ if packageFile, err = util.FindReverse(c.Dir, "package.json"); err == nil {
+ var content []byte
+ if content, err = os.ReadFile(packageFile); err == nil {
+ err = json.Unmarshal(content, &pj)
+ }
+ }
+ return
+}
diff --git a/pkg/actions/tools/pnpm/pnpm.go b/pkg/actions/tools/pnpm/pnpm.go
index 12c2b30240..6936f31f72 100644
--- a/pkg/actions/tools/pnpm/pnpm.go
+++ b/pkg/actions/tools/pnpm/pnpm.go
@@ -1,13 +1,10 @@
package pnpm
import (
- "encoding/json"
- "fmt"
"strings"
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/pkg/actions/tools/git"
- "github.com/carapace-sh/carapace-bin/pkg/actions/tools/npm"
)
// ActionFilter completes filter
@@ -21,52 +18,93 @@ func ActionFilter() carapace.Action {
c.Value = c.Value[index+1:]
return carapace.ActionDirectories().Invoke(c).Prefix(prefix).ToA().NoSpace()
}
- return npm.ActionDependencies().NoSpace()
+ return carapace.Batch(
+ ActionWorkspaceFilter(),
+ ActionWorkspacePackages().NoSpace(),
+ ).ToA()
})
}
-type location struct {
- Dependencies map[string]struct {
- Version string
- }
-}
-
-// ActionDependencyNames completes dependency names
+// ActionDependencyNames completes dependency names from package.json
func ActionDependencyNames() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
- return carapace.ActionExecCommand("pnpm", "list", "--json")(func(output []byte) carapace.Action {
- var locations []location
- if err := json.Unmarshal(output, &locations); err != nil {
- return carapace.ActionMessage(err.Error())
- }
+ pj, err := loadPackageJson(c)
+ if err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
- vals := make([]string, 0)
- for _, l := range locations {
- for name := range l.Dependencies {
- vals = append(vals, name)
- }
+ vals := make([]string, 0)
+ seen := make(map[string]bool)
+
+ for name := range pj.Dependencies {
+ if !seen[name] {
+ seen[name] = true
+ vals = append(vals, name)
}
- return carapace.ActionValues(vals...)
- })
+ }
+ for name := range pj.DevDependencies {
+ if !seen[name] {
+ seen[name] = true
+ vals = append(vals, name)
+ }
+ }
+ for name := range pj.OptionalDependencies {
+ if !seen[name] {
+ seen[name] = true
+ vals = append(vals, name)
+ }
+ }
+ return carapace.ActionValues(vals...).Tag("dependencies")
})
}
-// ActionDependencies ocmpletes dependencies with their version
+// ActionDependencies completes dependencies with their version from package.json
func ActionDependencies() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
- return carapace.ActionExecCommand("pnpm", "list", "--json")(func(output []byte) carapace.Action {
- var locations []location
- if err := json.Unmarshal(output, &locations); err != nil {
- return carapace.ActionMessage(err.Error())
- }
+ pj, err := loadPackageJson(c)
+ if err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
- vals := make([]string, 0)
- for _, l := range locations {
- for name, details := range l.Dependencies {
- vals = append(vals, fmt.Sprintf("%v@%v", name, details.Version))
+ vals := make([]string, 0)
+ seen := make(map[string]bool)
+
+ // Helper to add dependencies with description (version)
+ addDeps := func(deps map[string]string) {
+ for name, version := range deps {
+ if !seen[name] {
+ seen[name] = true
+ vals = append(vals, name, version)
}
}
- return carapace.ActionValues(vals...)
- }).Invoke(c).ToMultiPartsA("@")
+ }
+
+ addDeps(pj.Dependencies)
+ addDeps(pj.DevDependencies)
+ addDeps(pj.OptionalDependencies)
+
+ return carapace.ActionValuesDescribed(vals...).Tag("dependencies")
+ })
+}
+
+// ActionStorePath completes store paths
+func ActionStorePath() carapace.Action {
+ return carapace.ActionExecCommand("pnpm", "store", "path")(func(output []byte) carapace.Action {
+ path := strings.TrimSpace(string(output))
+ return carapace.ActionValues(path)
+ })
+}
+
+// ActionStoreStatus completes store status information
+func ActionStoreStatus() carapace.Action {
+ return carapace.ActionExecCommand("pnpm", "store", "status")(func(output []byte) carapace.Action {
+ lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+ vals := make([]string, 0)
+ for _, line := range lines {
+ if line != "" {
+ vals = append(vals, line)
+ }
+ }
+ return carapace.ActionValues(vals...)
})
}
diff --git a/pkg/actions/tools/pnpm/script.go b/pkg/actions/tools/pnpm/script.go
new file mode 100644
index 0000000000..c83887d8aa
--- /dev/null
+++ b/pkg/actions/tools/pnpm/script.go
@@ -0,0 +1,18 @@
+package pnpm
+
+import "github.com/carapace-sh/carapace"
+
+// ActionScripts completes scripts
+func ActionScripts() carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ if pj, err := loadPackageJson(c); err != nil {
+ return carapace.ActionMessage(err.Error())
+ } else {
+ vals := make([]string, 0)
+ for name := range pj.Scripts {
+ vals = append(vals, name)
+ }
+ return carapace.ActionValues(vals...)
+ }
+ })
+}
diff --git a/pkg/actions/tools/pnpm/store.go b/pkg/actions/tools/pnpm/store.go
new file mode 100644
index 0000000000..614097d96c
--- /dev/null
+++ b/pkg/actions/tools/pnpm/store.go
@@ -0,0 +1,40 @@
+package pnpm
+
+import (
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+)
+
+// ActionStorePackages completes packages in the store
+func ActionStorePackages() carapace.Action {
+ return carapace.ActionExecCommand("pnpm", "store", "status")(func(output []byte) carapace.Action {
+ lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+ vals := make([]string, 0)
+ for _, line := range lines {
+ if line != "" && !strings.Contains(line, "Packages:") && !strings.Contains(line, "Size:") {
+ vals = append(vals, line)
+ }
+ }
+ return carapace.ActionValues(vals...)
+ })
+}
+
+// ActionStorePrune completes store prune options
+func ActionStorePrune() carapace.Action {
+ return carapace.ActionValues("--verify-store-integrity")
+}
+
+// ActionStoreAdd completes store add options
+func ActionStoreAdd() carapace.Action {
+ return carapace.ActionMultiParts("@", func(c carapace.Context) carapace.Action {
+ switch len(c.Parts) {
+ case 0:
+ return ActionPackageNames("").NoSpace()
+ case 1:
+ return ActionPackageVersions(PackageOpts{Package: c.Parts[0]})
+ default:
+ return carapace.ActionValues()
+ }
+ })
+}
diff --git a/pkg/actions/tools/pnpm/workspace.go b/pkg/actions/tools/pnpm/workspace.go
new file mode 100644
index 0000000000..275e0fe1fc
--- /dev/null
+++ b/pkg/actions/tools/pnpm/workspace.go
@@ -0,0 +1,89 @@
+package pnpm
+
+import (
+ "encoding/json"
+ "os"
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace/pkg/util"
+)
+
+type pnpmWorkspace struct {
+ Packages []string `json:"packages"`
+}
+
+// ActionWorkspaces completes workspaces
+func ActionWorkspaces() carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ // First try to find pnpm-workspace.yaml
+ if workspaceFile, err := util.FindReverse(c.Dir, "pnpm-workspace.yaml"); err == nil {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ content, err := os.ReadFile(workspaceFile)
+ if err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ // Parse YAML-like structure for packages
+ lines := strings.Split(string(content), "\n")
+ vals := make([]string, 0)
+ inPackages := false
+
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "packages:" {
+ inPackages = true
+ continue
+ }
+ if inPackages && strings.HasPrefix(line, "-") {
+ packagePath := strings.TrimSpace(strings.TrimPrefix(line, "-"))
+ vals = append(vals, packagePath)
+ }
+ }
+
+ return carapace.ActionValues(vals...)
+ })
+ }
+
+ // Fallback to package.json workspaces
+ if pj, err := loadPackageJson(c); err != nil {
+ return carapace.ActionMessage(err.Error())
+ } else {
+ return carapace.ActionValues(pj.Workspaces...)
+ }
+ })
+}
+
+// ActionWorkspacePackages completes packages within workspaces
+func ActionWorkspacePackages() carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ return carapace.ActionExecCommandE("pnpm", "ls", "-r", "--json", "--depth", "-1")(func(output []byte, err error) carapace.Action {
+ if err != nil {
+ return carapace.ActionValues()
+ }
+
+ var packages []struct {
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Path string `json:"path"`
+ Private bool `json:"private"`
+ }
+
+ if err := json.Unmarshal(output, &packages); err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ vals := make([]string, 0, len(packages)*2)
+ for _, pkg := range packages {
+ if pkg.Name != "" && !pkg.Private {
+ desc := pkg.Version
+ if desc == "" {
+ desc = pkg.Path
+ }
+ vals = append(vals, pkg.Name, desc)
+ }
+ }
+ return carapace.ActionValuesDescribed(vals...).Tag("workspace packages")
+ })
+ })
+}
diff --git a/pkg/actions/tools/pnpm/workspace_specific.go b/pkg/actions/tools/pnpm/workspace_specific.go
new file mode 100644
index 0000000000..ff808148bd
--- /dev/null
+++ b/pkg/actions/tools/pnpm/workspace_specific.go
@@ -0,0 +1,105 @@
+package pnpm
+
+import (
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/carapace-sh/carapace"
+ "github.com/carapace-sh/carapace/pkg/util"
+)
+
+// ActionWorkspaceScripts completes scripts available across all workspace packages
+func ActionWorkspaceScripts() carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ workspaceFile, err := util.FindReverse(c.Dir, "pnpm-workspace.yaml")
+ if err != nil {
+ return ActionScripts()
+ }
+
+ content, err := os.ReadFile(workspaceFile)
+ if err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ workspaceDir := filepath.Dir(workspaceFile)
+ lines := strings.Split(string(content), "\n")
+
+ vals := make([]string, 0)
+ seen := make(map[string]bool)
+ inPackages := false
+
+ for _, line := range lines {
+ trimmed := strings.TrimSpace(line)
+ if trimmed == "packages:" {
+ inPackages = true
+ continue
+ }
+ if inPackages && strings.HasPrefix(trimmed, "-") {
+ pattern := strings.Trim(strings.TrimPrefix(trimmed, "-"), "\"' \t")
+ matches, err := filepath.Glob(filepath.Join(workspaceDir, pattern))
+ if err != nil {
+ continue
+ }
+ for _, match := range matches {
+ data, err := os.ReadFile(filepath.Join(match, "package.json"))
+ if err != nil {
+ continue
+ }
+ var pj packageJson
+ if json.Unmarshal(data, &pj) == nil {
+ for name := range pj.Scripts {
+ if !seen[name] {
+ seen[name] = true
+ vals = append(vals, name)
+ }
+ }
+ }
+ }
+ }
+ }
+ return carapace.ActionValues(vals...).Tag("workspace scripts")
+ })
+}
+
+// ActionWorkspaceFilter completes common workspace filter patterns
+func ActionWorkspaceFilter() carapace.Action {
+ return carapace.ActionValuesDescribed(
+ "...", "Include all dependents (recursive)",
+ "...^", "Include direct dependents only",
+ "^...", "Include all dependencies (recursive)",
+ "^...^", "Include direct dependencies only",
+ "*", "All packages",
+ "packages/*", "All packages in packages/",
+ "apps/*", "All packages in apps/",
+ "libs/*", "All packages in libs/",
+ "tools/*", "All packages in tools/",
+ ).Tag("workspace filter patterns")
+}
+
+type location struct {
+ Dependencies map[string]struct {
+ Version string
+ }
+}
+
+// ActionWorkspaceDependencies completes workspace package names with their versions
+func ActionWorkspaceDependencies() carapace.Action {
+ return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
+ return carapace.ActionExecCommand("pnpm", "list", "--json", "--depth", "0")(func(output []byte) carapace.Action {
+ var locations []location
+ if err := json.Unmarshal(output, &locations); err != nil {
+ return carapace.ActionMessage(err.Error())
+ }
+
+ vals := make([]string, 0)
+ for _, l := range locations {
+ for name, details := range l.Dependencies {
+ vals = append(vals, name, details.Version)
+ }
+ }
+ return carapace.ActionValuesDescribed(vals...).Tag("workspace dependencies")
+ })
+ })
+}