aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/packagemanager/pnpm.go
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /cli/internal/packagemanager/pnpm.go
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'cli/internal/packagemanager/pnpm.go')
-rw-r--r--cli/internal/packagemanager/pnpm.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/cli/internal/packagemanager/pnpm.go b/cli/internal/packagemanager/pnpm.go
new file mode 100644
index 0000000..e65a4dc
--- /dev/null
+++ b/cli/internal/packagemanager/pnpm.go
@@ -0,0 +1,168 @@
+package packagemanager
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/Masterminds/semver"
+ "github.com/vercel/turbo/cli/internal/fs"
+ "github.com/vercel/turbo/cli/internal/lockfile"
+ "github.com/vercel/turbo/cli/internal/turbopath"
+ "github.com/vercel/turbo/cli/internal/yaml"
+)
+
+// PnpmWorkspaces is a representation of workspace package globs found
+// in pnpm-workspace.yaml
+type PnpmWorkspaces struct {
+ Packages []string `yaml:"packages,omitempty"`
+}
+
+func readPnpmWorkspacePackages(workspaceFile turbopath.AbsoluteSystemPath) ([]string, error) {
+ bytes, err := workspaceFile.ReadFile()
+ if err != nil {
+ return nil, fmt.Errorf("%v: %w", workspaceFile, err)
+ }
+ var pnpmWorkspaces PnpmWorkspaces
+ if err := yaml.Unmarshal(bytes, &pnpmWorkspaces); err != nil {
+ return nil, fmt.Errorf("%v: %w", workspaceFile, err)
+ }
+ return pnpmWorkspaces.Packages, nil
+}
+
+func getPnpmWorkspaceGlobs(rootpath turbopath.AbsoluteSystemPath) ([]string, error) {
+ pkgGlobs, err := readPnpmWorkspacePackages(rootpath.UntypedJoin("pnpm-workspace.yaml"))
+ if err != nil {
+ return nil, err
+ }
+
+ if len(pkgGlobs) == 0 {
+ return nil, fmt.Errorf("pnpm-workspace.yaml: no packages found. Turborepo requires pnpm workspaces and thus packages to be defined in the root pnpm-workspace.yaml")
+ }
+
+ filteredPkgGlobs := []string{}
+ for _, pkgGlob := range pkgGlobs {
+ if !strings.HasPrefix(pkgGlob, "!") {
+ filteredPkgGlobs = append(filteredPkgGlobs, pkgGlob)
+ }
+ }
+ return filteredPkgGlobs, nil
+}
+
+func getPnpmWorkspaceIgnores(pm PackageManager, rootpath turbopath.AbsoluteSystemPath) ([]string, error) {
+ // Matches upstream values:
+ // function: https://github.com/pnpm/pnpm/blob/d99daa902442e0c8ab945143ebaf5cdc691a91eb/packages/find-packages/src/index.ts#L27
+ // key code: https://github.com/pnpm/pnpm/blob/d99daa902442e0c8ab945143ebaf5cdc691a91eb/packages/find-packages/src/index.ts#L30
+ // call site: https://github.com/pnpm/pnpm/blob/d99daa902442e0c8ab945143ebaf5cdc691a91eb/packages/find-workspace-packages/src/index.ts#L32-L39
+ ignores := []string{
+ "**/node_modules/**",
+ "**/bower_components/**",
+ }
+ pkgGlobs, err := readPnpmWorkspacePackages(rootpath.UntypedJoin("pnpm-workspace.yaml"))
+ if err != nil {
+ return nil, err
+ }
+ for _, pkgGlob := range pkgGlobs {
+ if strings.HasPrefix(pkgGlob, "!") {
+ ignores = append(ignores, pkgGlob[1:])
+ }
+ }
+ return ignores, nil
+}
+
+var nodejsPnpm = PackageManager{
+ Name: "nodejs-pnpm",
+ Slug: "pnpm",
+ Command: "pnpm",
+ Specfile: "package.json",
+ Lockfile: "pnpm-lock.yaml",
+ PackageDir: "node_modules",
+ // pnpm v7+ changed their handling of '--'. We no longer need to pass it to pass args to
+ // the script being run, and in fact doing so will cause the '--' to be passed through verbatim,
+ // potentially breaking scripts that aren't expecting it.
+ // We are allowed to use nil here because ArgSeparator already has a type, so it's a typed nil,
+ // This could just as easily be []string{}, but the style guide says to prefer
+ // nil for empty slices.
+ ArgSeparator: nil,
+ WorkspaceConfigurationPath: "pnpm-workspace.yaml",
+
+ getWorkspaceGlobs: getPnpmWorkspaceGlobs,
+
+ getWorkspaceIgnores: getPnpmWorkspaceIgnores,
+
+ Matches: func(manager string, version string) (bool, error) {
+ if manager != "pnpm" {
+ return false, nil
+ }
+
+ v, err := semver.NewVersion(version)
+ if err != nil {
+ return false, fmt.Errorf("could not parse pnpm version: %w", err)
+ }
+ c, err := semver.NewConstraint(">=7.0.0")
+ if err != nil {
+ return false, fmt.Errorf("could not create constraint: %w", err)
+ }
+
+ return c.Check(v), nil
+ },
+
+ detect: func(projectDirectory turbopath.AbsoluteSystemPath, packageManager *PackageManager) (bool, error) {
+ specfileExists := projectDirectory.UntypedJoin(packageManager.Specfile).FileExists()
+ lockfileExists := projectDirectory.UntypedJoin(packageManager.Lockfile).FileExists()
+
+ return (specfileExists && lockfileExists), nil
+ },
+
+ canPrune: func(cwd turbopath.AbsoluteSystemPath) (bool, error) {
+ return true, nil
+ },
+
+ UnmarshalLockfile: func(_rootPackageJSON *fs.PackageJSON, contents []byte) (lockfile.Lockfile, error) {
+ return lockfile.DecodePnpmLockfile(contents)
+ },
+
+ prunePatches: func(pkgJSON *fs.PackageJSON, patches []turbopath.AnchoredUnixPath) error {
+ return pnpmPrunePatches(pkgJSON, patches)
+ },
+}
+
+func pnpmPrunePatches(pkgJSON *fs.PackageJSON, patches []turbopath.AnchoredUnixPath) error {
+ pkgJSON.Mu.Lock()
+ defer pkgJSON.Mu.Unlock()
+
+ keysToDelete := []string{}
+ pnpmConfig, ok := pkgJSON.RawJSON["pnpm"].(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("Invalid structure for pnpm field in package.json")
+ }
+ patchedDependencies, ok := pnpmConfig["patchedDependencies"].(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("Invalid structure for patchedDependencies field in package.json")
+ }
+
+ for dependency, untypedPatch := range patchedDependencies {
+ patch, ok := untypedPatch.(string)
+ if !ok {
+ return fmt.Errorf("Expected only strings in patchedDependencies. Got %v", untypedPatch)
+ }
+
+ inPatches := false
+
+ for _, wantedPatch := range patches {
+ if wantedPatch.ToString() == patch {
+ inPatches = true
+ break
+ }
+ }
+
+ if !inPatches {
+ keysToDelete = append(keysToDelete, dependency)
+ }
+ }
+
+ for _, key := range keysToDelete {
+ delete(patchedDependencies, key)
+ }
+
+ return nil
+}