mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 18:46:41 +01:00
feat: add yarn lock dev dep detection; fixed #4548
--------- Signed-off-by: Rez Moss <hi@rezmoss.com> Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
This commit is contained in:
parent
48ee12be0c
commit
c185657d71
@ -35,7 +35,8 @@ func TestYarnPackageLockDirectory(t *testing.T) {
|
|||||||
sbom, _ := catalogDirectory(t, "test-fixtures/yarn-lock")
|
sbom, _ := catalogDirectory(t, "test-fixtures/yarn-lock")
|
||||||
|
|
||||||
foundPackages := strset.New()
|
foundPackages := strset.New()
|
||||||
expectedPackages := strset.New("async@0.9.2", "async@3.2.3", "merge-objects@1.0.5", "should-type@1.3.0", "@4lolo/resize-observer-polyfill@1.5.2")
|
// merge-objects and should-type are devDependencies in package.json and are excluded by default
|
||||||
|
expectedPackages := strset.New("async@0.9.2", "async@3.2.3", "@4lolo/resize-observer-polyfill@1.5.2")
|
||||||
|
|
||||||
for actualPkg := range sbom.Artifacts.Packages.Enumerate(pkg.NpmPkg) {
|
for actualPkg := range sbom.Artifacts.Packages.Enumerate(pkg.NpmPkg) {
|
||||||
for _, actualLocation := range actualPkg.Locations.ToSlice() {
|
for _, actualLocation := range actualPkg.Locations.ToSlice() {
|
||||||
|
|||||||
@ -23,20 +23,21 @@ var _ generic.Parser = parsePackageJSON
|
|||||||
|
|
||||||
// packageJSON represents a JavaScript package.json file
|
// packageJSON represents a JavaScript package.json file
|
||||||
type packageJSON struct {
|
type packageJSON struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Latest []string `json:"latest"`
|
Latest []string `json:"latest"`
|
||||||
Author person `json:"author"`
|
Author person `json:"author"`
|
||||||
Authors people `json:"authors"`
|
Authors people `json:"authors"`
|
||||||
Contributors people `json:"contributors"`
|
Contributors people `json:"contributors"`
|
||||||
Maintainers people `json:"maintainers"`
|
Maintainers people `json:"maintainers"`
|
||||||
License json.RawMessage `json:"license"`
|
License json.RawMessage `json:"license"`
|
||||||
Licenses json.RawMessage `json:"licenses"`
|
Licenses json.RawMessage `json:"licenses"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Homepage string `json:"homepage"`
|
Homepage string `json:"homepage"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Dependencies map[string]string `json:"dependencies"`
|
Dependencies map[string]string `json:"dependencies"`
|
||||||
Repository repository `json:"repository"`
|
DevDependencies map[string]string `json:"devDependencies"`
|
||||||
Private bool `json:"private"`
|
Repository repository `json:"repository"`
|
||||||
|
Private bool `json:"private"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type person struct {
|
type person struct {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package javascript
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/goccy/go-yaml"
|
"github.com/goccy/go-yaml"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/internal/unknown"
|
"github.com/anchore/syft/internal/unknown"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
@ -51,7 +53,7 @@ type yarnPackage struct {
|
|||||||
Version string
|
Version string
|
||||||
Resolved string
|
Resolved string
|
||||||
Integrity string
|
Integrity string
|
||||||
Dependencies map[string]string // We don't currently support dependencies for yarn v1 lock files
|
Dependencies map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type yarnV2PackageEntry struct {
|
type yarnV2PackageEntry struct {
|
||||||
@ -71,6 +73,98 @@ func newGenericYarnLockAdapter(cfg CatalogerConfig) genericYarnLockAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readPackageJSONDeps reads the package.json adjacent to the given lockfile location.
|
||||||
|
// NOTE: in yarn workspaces, only the root package.json is consulted. Packages declared
|
||||||
|
// as devDependencies only in a workspace package.json (not the root) will not be detected
|
||||||
|
// as dev-only. This is a safe degradation — those packages will be included in the SBOM
|
||||||
|
// rather than incorrectly filtered out.
|
||||||
|
func readPackageJSONDeps(resolver file.Resolver, lockfileLocation file.Location) (prod, dev map[string]string) {
|
||||||
|
prod = make(map[string]string)
|
||||||
|
dev = make(map[string]string)
|
||||||
|
|
||||||
|
if resolver == nil {
|
||||||
|
return prod, dev
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgJSONLocation := resolver.RelativeFileByPath(lockfileLocation, "package.json")
|
||||||
|
if pkgJSONLocation == nil {
|
||||||
|
log.WithFields("lockfile", lockfileLocation.RealPath).Debug("could not find package.json for dev dependency detection")
|
||||||
|
return prod, dev
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := resolver.FileContentsByLocation(*pkgJSONLocation)
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields("location", pkgJSONLocation.RealPath, "error", err).Debug("could not read package.json for dev dependency detection")
|
||||||
|
return prod, dev
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogError(reader, pkgJSONLocation.RealPath)
|
||||||
|
|
||||||
|
var pkgJSON packageJSON
|
||||||
|
if err := json.NewDecoder(reader).Decode(&pkgJSON); err != nil {
|
||||||
|
log.WithFields("location", pkgJSONLocation.RealPath, "error", err).Debug("could not parse package.json for dev dependency detection")
|
||||||
|
return prod, dev
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgJSON.Dependencies != nil {
|
||||||
|
prod = pkgJSON.Dependencies
|
||||||
|
}
|
||||||
|
if pkgJSON.DevDependencies != nil {
|
||||||
|
dev = pkgJSON.DevDependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
return prod, dev
|
||||||
|
}
|
||||||
|
|
||||||
|
// findReachable returns all package names reachable from the given roots via BFS
|
||||||
|
// through the dependency graph.
|
||||||
|
func findReachable(roots map[string]string, pkgByName map[string]yarnPackage) map[string]bool {
|
||||||
|
visited := make(map[string]bool)
|
||||||
|
queue := make([]string, 0, len(roots))
|
||||||
|
|
||||||
|
for name := range roots {
|
||||||
|
queue = append(queue, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
name := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
|
||||||
|
if visited[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visited[name] = true
|
||||||
|
|
||||||
|
if pkg, exists := pkgByName[name]; exists {
|
||||||
|
for depName := range pkg.Dependencies {
|
||||||
|
if !visited[depName] {
|
||||||
|
queue = append(queue, depName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return visited
|
||||||
|
}
|
||||||
|
|
||||||
|
func findDevOnlyPkgs(yarnPkgs []yarnPackage, prodDeps, devDeps map[string]string) map[string]bool {
|
||||||
|
pkgByName := make(map[string]yarnPackage)
|
||||||
|
for _, p := range yarnPkgs {
|
||||||
|
pkgByName[p.Name] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
prodTransitive := findReachable(prodDeps, pkgByName)
|
||||||
|
devTransitive := findReachable(devDeps, pkgByName)
|
||||||
|
|
||||||
|
devOnly := make(map[string]bool)
|
||||||
|
for name := range devTransitive {
|
||||||
|
if !prodTransitive[name] {
|
||||||
|
devOnly[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return devOnly
|
||||||
|
}
|
||||||
|
|
||||||
func parseYarnV1LockFile(reader io.ReadCloser) ([]yarnPackage, error) {
|
func parseYarnV1LockFile(reader io.ReadCloser) ([]yarnPackage, error) {
|
||||||
content, err := io.ReadAll(reader)
|
content, err := io.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,9 +284,17 @@ func (a genericYarnLockAdapter) parseYarnLock(ctx context.Context, resolver file
|
|||||||
return nil, nil, fmt.Errorf("failed to parse yarn.lock file: %w", err)
|
return nil, nil, fmt.Errorf("failed to parse yarn.lock file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
packages := make([]pkg.Package, len(yarnPkgs))
|
// get dependencies from sibling package.json for dev dependency detection
|
||||||
for i, p := range yarnPkgs {
|
prodDeps, devDeps := readPackageJSONDeps(resolver, reader.Location)
|
||||||
packages[i] = newYarnLockPackage(ctx, a.cfg, resolver, reader.Location, p.Name, p.Version, p.Resolved, p.Integrity, p.Dependencies)
|
|
||||||
|
devOnlyPkgs := findDevOnlyPkgs(yarnPkgs, prodDeps, devDeps)
|
||||||
|
|
||||||
|
packages := make([]pkg.Package, 0, len(yarnPkgs))
|
||||||
|
for _, p := range yarnPkgs {
|
||||||
|
if devOnlyPkgs[p.Name] && !a.cfg.IncludeDevDependencies {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packages = append(packages, newYarnLockPackage(ctx, a.cfg, resolver, reader.Location, p.Name, p.Version, p.Resolved, p.Integrity, p.Dependencies))
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg.Sort(packages)
|
pkg.Sort(packages)
|
||||||
|
|||||||
@ -739,6 +739,274 @@ func TestParseYarnFindPackageNames(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseYarnLock_DevDependencies(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixtureDir string
|
||||||
|
includeDev bool
|
||||||
|
expected func(file.LocationSet) ([]pkg.Package, []artifact.Relationship)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v1 include dev dependencies",
|
||||||
|
fixtureDir: "test-fixtures/yarn-dev-deps",
|
||||||
|
includeDev: true,
|
||||||
|
expected: func(locations file.LocationSet) ([]pkg.Package, []artifact.Relationship) {
|
||||||
|
pkgs := []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "dev-only-transitive",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/dev-only-transitive@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/dev-only-transitive/-/dev-only-transitive-1.0.0.tgz#abc123",
|
||||||
|
Integrity: "sha512-devonlytransitive==",
|
||||||
|
Dependencies: map[string]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dev-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/dev-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/dev-pkg/-/dev-pkg-1.0.0.tgz#def456",
|
||||||
|
Integrity: "sha512-devpkg==",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"dev-only-transitive": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "prod-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/prod-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/prod-pkg/-/prod-pkg-1.0.0.tgz#ghi789",
|
||||||
|
Integrity: "sha512-prodpkg==",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"shared-pkg": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "shared-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/shared-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/shared-pkg/-/shared-pkg-1.0.0.tgz#jkl012",
|
||||||
|
Integrity: "sha512-sharedpkg==",
|
||||||
|
Dependencies: map[string]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rels := []artifact.Relationship{
|
||||||
|
{
|
||||||
|
From: pkgs[0], // dev-only-transitive
|
||||||
|
To: pkgs[1], // dev-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
From: pkgs[3], // shared-pkg
|
||||||
|
To: pkgs[2], // prod-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pkgs, rels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 exclude dev dependencies",
|
||||||
|
fixtureDir: "test-fixtures/yarn-dev-deps",
|
||||||
|
includeDev: false,
|
||||||
|
expected: func(locations file.LocationSet) ([]pkg.Package, []artifact.Relationship) {
|
||||||
|
pkgs := []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "prod-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/prod-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/prod-pkg/-/prod-pkg-1.0.0.tgz#ghi789",
|
||||||
|
Integrity: "sha512-prodpkg==",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"shared-pkg": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "shared-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/shared-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "https://registry.yarnpkg.com/shared-pkg/-/shared-pkg-1.0.0.tgz#jkl012",
|
||||||
|
Integrity: "sha512-sharedpkg==",
|
||||||
|
Dependencies: map[string]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rels := []artifact.Relationship{
|
||||||
|
{
|
||||||
|
From: pkgs[1], // shared-pkg
|
||||||
|
To: pkgs[0], // prod-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pkgs, rels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v2 (berry) include dev dependencies",
|
||||||
|
fixtureDir: "test-fixtures/yarn-berry-dev-deps",
|
||||||
|
includeDev: true,
|
||||||
|
expected: func(locations file.LocationSet) ([]pkg.Package, []artifact.Relationship) {
|
||||||
|
pkgs := []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "dev-only-transitive",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/dev-only-transitive@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "dev-only-transitive@npm:1.0.0",
|
||||||
|
Integrity: "abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dev-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/dev-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "dev-pkg@npm:1.0.0",
|
||||||
|
Integrity: "def456",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"dev-only-transitive": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "prod-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/prod-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "prod-pkg@npm:1.0.0",
|
||||||
|
Integrity: "ghi789",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"shared-pkg": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "shared-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/shared-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "shared-pkg@npm:1.0.0",
|
||||||
|
Integrity: "jkl012",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rels := []artifact.Relationship{
|
||||||
|
{
|
||||||
|
From: pkgs[0], // dev-only-transitive
|
||||||
|
To: pkgs[1], // dev-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
From: pkgs[3], // shared-pkg
|
||||||
|
To: pkgs[2], // prod-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pkgs, rels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v2 (berry) exclude dev dependencies",
|
||||||
|
fixtureDir: "test-fixtures/yarn-berry-dev-deps",
|
||||||
|
includeDev: false,
|
||||||
|
expected: func(locations file.LocationSet) ([]pkg.Package, []artifact.Relationship) {
|
||||||
|
pkgs := []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "prod-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/prod-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "prod-pkg@npm:1.0.0",
|
||||||
|
Integrity: "ghi789",
|
||||||
|
Dependencies: map[string]string{
|
||||||
|
"shared-pkg": "^1.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "shared-pkg",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "pkg:npm/shared-pkg@1.0.0",
|
||||||
|
Language: pkg.JavaScript,
|
||||||
|
Type: pkg.NpmPkg,
|
||||||
|
Metadata: pkg.YarnLockEntry{
|
||||||
|
Resolved: "shared-pkg@npm:1.0.0",
|
||||||
|
Integrity: "jkl012",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rels := []artifact.Relationship{
|
||||||
|
{
|
||||||
|
From: pkgs[1], // shared-pkg
|
||||||
|
To: pkgs[0], // prod-pkg
|
||||||
|
Type: artifact.DependencyOfRelationship,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pkgs, rels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fixture := tt.fixtureDir + "/yarn.lock"
|
||||||
|
locations := file.NewLocationSet(file.NewLocation(fixture))
|
||||||
|
expectedPkgs, expectedRels := tt.expected(locations)
|
||||||
|
|
||||||
|
adapter := newGenericYarnLockAdapter(CatalogerConfig{IncludeDevDependencies: tt.includeDev})
|
||||||
|
pkgtest.NewCatalogTester().
|
||||||
|
FromDirectory(t, tt.fixtureDir).
|
||||||
|
FromFile(t, fixture).
|
||||||
|
Expects(expectedPkgs, expectedRels).
|
||||||
|
TestParser(t, adapter.parseYarnLock)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func generateMockYarnRegistryHandler(responseFixture string) func(w http.ResponseWriter, r *http.Request) {
|
func generateMockYarnRegistryHandler(responseFixture string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "test-project",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"prod-pkg": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"dev-pkg": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
# This file is generated by running "yarn install" inside your project.
|
||||||
|
# Manual changes might be lost - proceed with caution!
|
||||||
|
|
||||||
|
__metadata:
|
||||||
|
version: 6
|
||||||
|
cacheKey: 8
|
||||||
|
|
||||||
|
"dev-only-transitive@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "dev-only-transitive@npm:1.0.0"
|
||||||
|
checksum: abc123
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"dev-pkg@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "dev-pkg@npm:1.0.0"
|
||||||
|
dependencies:
|
||||||
|
dev-only-transitive: ^1.0.0
|
||||||
|
checksum: def456
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"prod-pkg@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "prod-pkg@npm:1.0.0"
|
||||||
|
dependencies:
|
||||||
|
shared-pkg: ^1.0.0
|
||||||
|
checksum: ghi789
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"shared-pkg@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "shared-pkg@npm:1.0.0"
|
||||||
|
checksum: jkl012
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "test-project",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"prod-pkg": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"dev-pkg": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
dev-only-transitive@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dev-only-transitive/-/dev-only-transitive-1.0.0.tgz#abc123"
|
||||||
|
integrity sha512-devonlytransitive==
|
||||||
|
|
||||||
|
dev-pkg@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dev-pkg/-/dev-pkg-1.0.0.tgz#def456"
|
||||||
|
integrity sha512-devpkg==
|
||||||
|
dependencies:
|
||||||
|
dev-only-transitive "^1.0.0"
|
||||||
|
|
||||||
|
prod-pkg@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prod-pkg/-/prod-pkg-1.0.0.tgz#ghi789"
|
||||||
|
integrity sha512-prodpkg==
|
||||||
|
dependencies:
|
||||||
|
shared-pkg "^1.0.0"
|
||||||
|
|
||||||
|
shared-pkg@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/shared-pkg/-/shared-pkg-1.0.0.tgz#jkl012"
|
||||||
|
integrity sha512-sharedpkg==
|
||||||
Loading…
x
Reference in New Issue
Block a user