js-dependency-mining
Read-only mining codemod: scans package.json manifests (single-package repos and monorepos) together with package-lock.json, pnpm-lock.yaml, or yarn.lock, classifies each dependency, and emits js-dependency-mining metrics. It does not modify source files.
No fs capability required — no Node fs or path imports. The workflow only targets **/package.json. Lockfiles are loaded with jssgTransform and small JSON/YAML transforms that capture root.root().text() (same mechanism as cross-file edits in the JSSG docs).
Classifications
Each finding is keyed by owner package (workspace-relative path), package name, and edge type (direct | transitive). Metric dimensions include:
| Field | Values |
|---|---|
| packageName | npm package name |
| declaredVersion | Range or spec from the manifest (empty for transitive-only rows) |
| ownerPackage | Workspace-relative owning package path |
| manifestPath | Path to package.json (relative to workspace root) |
| edgeType | direct, transitive |
| purpose | runtime, dev, test, build, optional, unknown |
| origin | registry, vcs, local, workspace, vendored, internal, unknown |
| versionPolicy | pinned, ranged, prerelease, unknown |
Workspace root
While processing a manifest, the codemod walks up the directory path and uses jssgTransform to probe for package-lock.json (language json), pnpm-lock.yaml / yarn.lock / pnpm-workspace.yaml (language yaml), and reads sibling package.json files to detect npm workspaces. The first directory that qualifies as a workspace root wins; the manifest is then resolved relative to that root.
Pipeline
- Workspace root — Walk upward from the manifest; at each level, try lockfiles and workspace markers via jssgTransform until a root is found.
- Lockfile — From that root, load the preferred lockfile in order npm → pnpm → yarn using jssgTransform + capture transforms (json for package-lock.json, yaml for pnpm/yarn). Parse with JSON.parse / js-yaml / the built-in yarn-classic parser.
- Metrics — For each dependency block in the manifest, emit direct rows; subtract directs from the lockfile transitive closure and emit transitive rows.
Workspace package names (for origin=workspace heuristics) are accumulated in a module-level registry as each package.json in the run is seen — no codemod:workflow state.
Configuration
Parameters are declared in workflow.yaml (params.schema) and merge with built-in defaults (extra test/build lists, default vendored fragments, etc.).
Where to edit them
- Codemod Insight UI — When you run this workflow in Insight, each parameter appears as a labeled field. You can change values there without editing YAML; that is the easiest way to tune classification for a run or saved job.
- CLI — Pass --param key=value to codemod workflow run (repeat for multiple keys), e.g. --param internal_scopes=@acme --param test_packages=playwright.
Parameters and examples (values are comma-separated unless noted):
| Parameter | Example | What it does |
|---|---|---|
| internal_scopes | @acme,@calcom | Scope prefixes for origin=internal. Packages whose names start with these (e.g. @acme/foo) are treated as internal when not better classified as workspace/VCS/local. |
| test_packages | storybook,@storybook/react | Extra names merged into the test-tool list. If listed in devDependencies, purpose can be test (instead of generic dev). |
| build_packages | turbo,tsc-alias | Extra names merged into the build-tool list. If listed in devDependencies, purpose can be build. |
| vendored_path_fragments | patches/,/vendor/ | Extra path substrings. For file: / link: dependency specs, if the path contains one of these (in addition to defaults like vendor/), origin can be vendored. |
Implementation notes
- No fs / path imports. Path operations use pure-string helpers. jssgTransform reads lockfiles under the workflow target; paths must stay within that target (per JSSG rules).
- jssgTransform in jssg test is a no-op — lockfiles are not loaded in unit tests, so snapshots typically show direct dependencies only. Real runs (codemod workflow run / Insight) load lockfiles and emit transitive rows.
- Yarn classic lockfiles are read with language yaml for parsing; the custom line parser runs on the captured text. If ast-grep cannot parse a particular yarn.lock, that lock may be skipped (catch and continue).
- Transitive dependencies are derived from the lockfile closure minus direct declarations (npm and pnpm v9 snapshots supported; yarn classic is best-effort).
- Conservative: ambiguous cases use unknown where classification would be a guess (e.g. transitive purpose).
- pnpm pnpm-lock.yaml is deserialized from the tree-sitter YAML AST (inline helpers in scripts/codemod.ts) inside a jssgTransform callback — no third-party YAML library, so Insight bundling stays simple.