Skip to content

Commit

Permalink
fix(cli): npm lockfile workspace parsing (#3244)
Browse files Browse the repository at this point in the history
We were unable to parse an npm lockfile that used the alternate format
for workspaces (`"workspaces": { "packages": [ "packages/**" ]}`)

Error manifested as:
```
 WARNING  Issues occurred when constructing package graph. Turbo will function, but some features may not be available:  1 error occurred:
	* json: cannot unmarshal object into Go struct field NpmPackage.packages.workspaces of type []string
```

Fixes #3207 and
#3050
  • Loading branch information
tknickman committed Jan 10, 2023
1 parent 5eddc61 commit bdc74af
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 8 deletions.
25 changes: 24 additions & 1 deletion cli/internal/lockfile/npm_lockfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type NpmPackage struct {
OS []string `json:"os,omitempty"`

// Only used for root level package
Workspaces []string `json:"workspaces,omitempty"`
Workspaces Workspaces `json:"workspaces,omitempty"`
}

// NpmDependency Legacy representation of dependencies
Expand All @@ -76,6 +76,29 @@ type NpmDependency struct {
Dependencies map[string]NpmDependency `json:"dependencies,omitempty"`
}

// Workspaces represents the standard workspaces field in package.json
type Workspaces []string

// WorkspacesAlt represents the alternate workspaces field (nested) package.json
type WorkspacesAlt struct {
Packages []string `json:"packages,omitempty"`
}

// UnmarshalJSON determines the correct format of the workspaces field to unmarshal
func (r *Workspaces) UnmarshalJSON(data []byte) error {
var tmp = &WorkspacesAlt{}
if err := json.Unmarshal(data, tmp); err == nil {
*r = Workspaces(tmp.Packages)
return nil
}
var tempstr = []string{}
if err := json.Unmarshal(data, &tempstr); err != nil {
return err
}
*r = tempstr
return nil
}

var _ Lockfile = (*NpmLockfile)(nil)

// ResolvePackage Given a workspace, a package it imports and version returns the key, resolved version, and if it was found
Expand Down
19 changes: 12 additions & 7 deletions cli/internal/lockfile/npm_lockfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
"gotest.tools/v3/assert/cmp"
)

func getNpmLockfile(t *testing.T) *NpmLockfile {
content, err := getFixture(t, "npm-lock.json")
assert.NilError(t, err, "reading npm-lock.json")
func getNpmLockfile(t *testing.T, file string) *NpmLockfile {
content, err := getFixture(t, file)
assert.NilError(t, err, "reading {}", file)
lockfile, err := DecodeNpmLockfile(content)
assert.NilError(t, err, "parsing npm-lock.json")
assert.NilError(t, err, "parsing {}", file)
return lockfile
}

Expand Down Expand Up @@ -48,6 +48,11 @@ func Test_NpmPathParent(t *testing.T) {
}
}

func Test_NpmResolvesAlternateWorkspaceFormat(t *testing.T) {
lockfile := getNpmLockfile(t, "npm-lock-workspace-variation.json")
assert.Equal(t, lockfile.Name, "npm-prune-workspace-variation")
}

func Test_PossibleNpmDeps(t *testing.T) {
type TestCase struct {
name string
Expand Down Expand Up @@ -145,7 +150,7 @@ func Test_NpmResolvePackage(t *testing.T) {
},
}

lockfile := getNpmLockfile(t)
lockfile := getNpmLockfile(t, "npm-lock.json")
for _, tc := range testCases {
workspace := turbopath.AnchoredUnixPath(tc.workspace)
pkg, err := lockfile.ResolvePackage(workspace, tc.name, "")
Expand Down Expand Up @@ -190,7 +195,7 @@ func Test_NpmAllDependencies(t *testing.T) {
},
}

lockfile := getNpmLockfile(t)
lockfile := getNpmLockfile(t, "npm-lock.json")
for _, tc := range testCases {
deps, ok := lockfile.AllDependencies(tc.key)
assert.Assert(t, ok, tc.name)
Expand All @@ -209,7 +214,7 @@ func Test_NpmAllDependencies(t *testing.T) {
func Test_NpmPeerDependenciesMeta(t *testing.T) {
var buf bytes.Buffer

lockfile := getNpmLockfile(t)
lockfile := getNpmLockfile(t, "npm-lock.json")
if err := lockfile.Encode(&buf); err != nil {
t.Error(err)
}
Expand Down
186 changes: 186 additions & 0 deletions cli/internal/lockfile/testdata/npm-lock-workspace-variation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
{
"name": "npm-prune-workspace-variation",
"version": "0.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "npm-prune",
"version": "0.0.0",
"workspaces": { "packages": ["apps/*", "packages/*"] },
"devDependencies": {
"eslint-config-custom": "*",
"prettier": "latest",
"turbo": "latest"
},
"engines": {
"node": ">=14.0.0"
}
},
"apps/docs": {
"version": "0.0.0",
"dependencies": {
"lodash": "^3.0.0",
"next": "12.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"ui": "*"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@types/node": "^17.0.12",
"@types/react": "18.0.17",
"eslint": "7.32.0",
"eslint-config-custom": "*",
"next-transpile-modules": "9.0.0",
"tsconfig": "*",
"typescript": "^4.5.3"
}
},
"apps/web": {
"version": "0.0.0",
"dependencies": {
"lodash": "^4.17.21",
"next": "12.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"ui": "*"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@types/node": "^17.0.12",
"@types/react": "18.0.17",
"eslint": "7.32.0",
"eslint-config-custom": "*",
"next-transpile-modules": "9.0.0",
"tsconfig": "*",
"typescript": "^4.5.3"
}
},
"apps/web/node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"engines": ["node >= 0.8.0"]
},
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.18.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
"version": "7.19.3",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.3.tgz",
"integrity": "sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.19.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz",
"integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6",
"@babel/generator": "^7.19.3",
"@babel/helper-compilation-targets": "^7.19.3",
"@babel/helper-module-transforms": "^7.19.0",
"@babel/helpers": "^7.19.0",
"@babel/parser": "^7.19.3",
"@babel/template": "^7.18.10",
"@babel/traverse": "^7.19.3",
"@babel/types": "^7.19.3",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.1",
"semver": "^6.3.0"
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/generator": {
"version": "7.19.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz",
"integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==",
"dev": true,
"dependencies": {
"@babel/types": "^7.19.3",
"@jridgewell/gen-mapping": "^0.3.2",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.19.3",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz",
"integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==",
"dev": true,
"dependencies": {
"@babel/compat-data": "^7.19.3",
"@babel/helper-validator-option": "^7.18.6",
"browserslist": "^4.21.3",
"semver": "^6.3.0"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
}
}
}

0 comments on commit bdc74af

Please sign in to comment.