Skip to content

feat: add dev-dependency-in-production rule#1740

Merged
BartWaardenburg merged 2 commits into
fallow-rs:mainfrom
CallumHoward:claude/fallow-dev-dep-prod-rule-ikfhww
Jul 5, 2026
Merged

feat: add dev-dependency-in-production rule#1740
BartWaardenburg merged 2 commits into
fallow-rs:mainfrom
CallumHoward:claude/fallow-dev-dep-prod-rule-ikfhww

Conversation

@CallumHoward

@CallumHoward CallumHoward commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What

Adds a new dev-dependency-in-production rule: a package in devDependencies that is imported by production (non-test, non-config) source code via a runtime/value import is flagged so it can be promoted to dependencies. It is the promote-side mirror of the existing test-only-dependency and type-only-dependency rules.

Detection (crates/core/src/analyze/unused_deps.rs::find_dev_dependencies_in_production) reuses the existing prod-vs-test file partition and the per-import value/type accounting the sibling detectors already rely on:

  • flags only when a production file has a runtime/value import of the dev dep;
  • does not flag type-only production imports (erased at build time — mirrors type-only-dependency);
  • does not flag imports that come only from test/config files (that's test-only-dependency's job);
  • skips workspace packages, known tooling (@types/*, typescript, …), ignoreDependencies entries, and packages also declared in dependencies / peerDependencies / optionalDependencies.

Default severity warn; rule key dev-dependencies-in-production; suppress a package via ignoreDependencies. Wired through the full analyzer contract per the analyzer-authoring guide: IssueKind + issue_meta rows, RulesConfig, the DevDependencyInProduction struct + Finding wrapper + move-to-prod action, human / JSON / SARIF / Code Climate / compact / markdown output, fallow explain, the LSP diagnostic, regression baselines, the GitHub Action + GitLab CI jq summaries, the VS Code extension, and all generated contracts (schema.json, output schema, npm registry/capabilities, TS types, SKILL.md).

Why

Closes #1738.

Fallow catches dependency demotion drift but not promotion drift. A devDependency value-imported from production code passes silently today — unlisted-dependency doesn't fire because the package is listed, just under the wrong section — yet pnpm install --prod omits it and the import breaks at runtime.

Repro before this change:

// package.json
{ "devDependencies": { "yaml": "^2.0.0" } }
// src/index.ts (reachable production code)
import { parse } from "yaml";
export const load = (s: string): unknown => parse(s);
$ fallow dead-code --fail-on-issues
✓ No issues found            # exit 0  ← the gap

After: the same project reports dev-dependency-in-production for yaml and exits 1 under --fail-on-issues, while a type-only import or a test-only import stays silent.

Test plan

  • cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings, cargo fmt --all -- --check — green.
  • New integration test (tests/fixtures/dev-dep-in-prod + crates/core/tests/integration_test/dev_dep_in_prod.rs) proving the value-import flag and the type-only / test-only / prod-dependency abstains end to end via fallow_core::analyze.
  • New unit tests (crates/core/src/analyze/unused_deps_tests/dev_dep_in_prod.rs) covering the flag plus each abstain path (type-only, test-only, ignored, workspace, known-tooling, peer-dependency).
  • npm run generate:contracts:check, action/tests/run.sh (415 passed), ci/tests/run.sh (317 passed), and the VS Code vitest suite (615 passed) all pass.
  • Updated JSON/SARIF snapshots and the config/output schema drift gates.

Note: this rule checks the root package.json against the project-wide import graph, matching the existing test-only/type-only detectors; true per-workspace attribution is a shared follow-up across all three dependency-family rules.


Just a first pass, I didn't see anything against AI-aided contributions in CONTRIBUTING.md – happy to close this or for more knowledgeable contributors to update if a more expert human contribution is desired.

claude and others added 2 commits July 5, 2026 12:02
Flag a package in devDependencies that is imported by production
(non-test, non-config) source code via a runtime/value import, so it can
be promoted to dependencies. A production-only install
(pnpm install --prod) omits devDependencies, so such an import breaks at
runtime. This is the promote-side mirror of the existing
test-only-dependency and type-only-dependency rules.

Detection reuses the prod-vs-test file partition and the per-import
value/type accounting already used by the sibling rules:

- flag only when a production file has a runtime (value) import
- do not flag type-only production imports (erased at build time)
- do not flag imports that come only from test/config files
- skip workspace packages, known tooling (@types/*, typescript, ...),
  ignoreDependencies entries, and packages also declared in
  dependencies / peerDependencies / optionalDependencies

Default severity warn; gated by the dev-dependencies-in-production rule
key; suppress a package with ignoreDependencies. Wired through human,
JSON, SARIF, Code Climate, compact, and markdown output, fallow explain,
the LSP diagnostic surface, the GitHub Action / GitLab CI jq summaries,
the VS Code extension, regression baselines, and the generated contracts.
Adds a fixture, integration test, and unit tests covering the flag and
each abstain path.
Filter parity: clear dev_dependencies_in_production in the --unused-deps
clear arm and the --file scoping path like every sibling dependency kind,
and render devDependenciesInProduction in the TOML regression baseline so
a saved fallow.toml baseline does not report a phantom regression.

Detection: only count imports from files reachable from a RUNTIME entry
point (repo tooling like scripts/, benchmarks/, and rollup-config chains
is not production evidence), skip files owned by a workspace package
(root-manifest scope; a hoisted root devDep declared in the workspace's
own dependencies is not a misplacement), and keep the rule active in
production mode where its signal is cleanest.

Surfaces: align jq summary labels with the registry label (drift gate),
add the kind to the action drift-guard fallback table, the VS Code status
bar registry, and the @fallow/node typings; drop the never-released
prod-usage-of-dev-dependency alias; strip the CHANGELOG em-dash and
document the runtime-reachability semantics and known limitations.
@BartWaardenburg BartWaardenburg force-pushed the claude/fallow-dev-dep-prod-rule-ikfhww branch from 4d21402 to fc979d7 Compare July 5, 2026 10:03
@BartWaardenburg BartWaardenburg enabled auto-merge (squash) July 5, 2026 10:05
@BartWaardenburg BartWaardenburg merged commit 7f0cef7 into fallow-rs:main Jul 5, 2026
1 check passed
@BartWaardenburg

Copy link
Copy Markdown
Collaborator

Released in v3.1.0. Thanks for the implementation, @CallumHoward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Detect dev dependencies used in production (promote-side mirror of test-only/type-only-dependency)

3 participants