diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /cli/internal/packagemanager/pnpm.go | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'cli/internal/packagemanager/pnpm.go')
| -rw-r--r-- | cli/internal/packagemanager/pnpm.go | 168 |
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 +} |
