Documentation
¶
Overview ¶
Package lockfile builds a resolve.Graph from a lockfile, without running any package manager or other process.
Lockfiles are untrusted input. Parsing is bounded by Limits: a lockfile larger than MaxBytes is not parsed, and the graph stops growing at MaxNodes nodes or MaxEdges edges. A malformed or unsupported lockfile yields an unknown root with a reason; a lockfile that parses but cannot be fully resolved yields a partial root with a reason. Parse never fails on content.
Each format names its roots after the manifest beside the lockfile, joined with the workspace directory for formats that record workspaces (package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml, uv.lock). Cargo.lock does not record where its workspace members live, so their roots come from the member manifests in Input.Members. A workspace, path or link package that something depends on is a local node, with its own dependencies as edges. Lockfile entries that no root reaches become non-direct members of the root beside the lockfile.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnsupported = errors.New("unsupported lockfile")
ErrUnsupported is returned for a lockfile whose base name is not a supported format.
Functions ¶
func ManifestFor ¶
ManifestFor returns the base name of the sibling manifest the format of path reads, if it reads one.
func Parse ¶
Parse builds the graph of one lockfile. The only error is ErrUnsupported; problems with the content are reported on the roots.
func ParseFS ¶
ParseFS reads the lockfile at path in fsys, and the sibling manifest when its format reads one, then parses them. For Cargo.lock it also reads the manifest of each workspace member the sibling Cargo.toml lists; more than MaxNodes of them is too_large, like oversized bytes. For packages.lock.json it looks for the project file beside it. The lockfile is read up to MaxBytes+1 bytes, and the manifests up to MaxBytes+1 bytes in total, so oversized input costs no more than that. A missing manifest is not an error; formats that need it report it on the root.
Example ¶
package main
import (
"fmt"
"testing/fstest"
"github.com/scanoss/scanoss.go/pkg/dependencies/resolve/lockfile"
)
func main() {
fsys := fstest.MapFS{"web/package-lock.json": {Data: []byte(`{
"lockfileVersion": 3,
"packages": {
"": {"dependencies": {"express": "^4.19.0"}},
"node_modules/express": {"version": "4.19.2", "dependencies": {"debug": "2.6.9"}},
"node_modules/debug": {"version": "2.6.9"}
}
}`)}}
g, err := lockfile.ParseFS(fsys, "web/package-lock.json", lockfile.DefaultLimits())
if err != nil {
panic(err)
}
for _, r := range g.Roots {
fmt.Println(r.Manifest, r.Status)
for _, m := range r.Members {
fmt.Println(" ", m.ID, "direct:", m.Direct)
}
for _, e := range r.Edges {
fmt.Println(" ", e.From, "->", e.To)
}
}
}
Output: web/package.json complete pkg:npm/debug@2.6.9 direct: false pkg:npm/express@4.19.2 direct: true pkg:npm/express@4.19.2 -> pkg:npm/debug@2.6.9
Types ¶
type Input ¶
type Input struct {
Path string // slash path of the lockfile; names the roots, never opened
Lockfile []byte
Manifest []byte // the sibling manifest named by ManifestFor; nil when absent
// Members holds workspace member manifests keyed by slash path relative
// to the lockfile's directory, such as "crates/app/Cargo.toml", for
// Cargo.lock. For packages.lock.json it holds the project file beside
// it, such as "App.csproj", which names the root; its content is not
// read.
Members map[string][]byte
}
Input is one lockfile and, when its format reads one, its sibling manifest.
type Limits ¶
type Limits struct {
MaxBytes int64 // largest lockfile, and largest total of its manifests, read and parsed
MaxNodes int // nodes kept; reaching it makes the roots partial (node_limit)
MaxEdges int // edges kept; reaching it makes the roots partial (edge_limit)
}
Limits bounds the work Parse does on one lockfile.
func DefaultLimits ¶
func DefaultLimits() Limits
DefaultLimits returns limits that fit any real lockfile: 32 MiB, 50,000 nodes and 500,000 edges.