Skip to content

next_lint detector silently reports zero issues on Next.js 16 (next lint was removed) #709

Description

@miguelalfredooo

Problem

NEXTJS_SPEC hardcodes the lint command:

# desloppify/languages/_framework/frameworks/specs/nextjs.py:547
cmd="npx --no-install next lint --format json",

Next.js 16 removed the lint subcommand. The invocation is now parsed as next <dir>, so it exits with:

$ npx next lint
Invalid project directory provided, no such directory: /path/to/project/lint

This does not fail loudly. The scan records reduced coverage:

Coverage reduced (next_lint): next lint tooling unavailable (parser_error)

…and reports zero lint issues. A Next 16 codebase reads as lint-clean when it is not. On the project I hit this on, eslint reports 6 real warnings across 5 files that next_lint contributed nothing about.

Repro

Any project with next@>=16 installed:

desloppify scan --path .
# → "Coverage reduced (next_lint): next lint tooling unavailable (parser_error)"

Environment: desloppify 1.0, next 16.3.0, Python 3.14.3, macOS.

Suggested fix

Resolve the command from the installed Next.js major (read node_modules/next/package.json, not the declared range — ^15.0.0 can resolve to 16) and run ESLint directly on >=16. Convenient detail: eslint --format json emits exactly the [{filePath, messages}] shape parse_next_lint already parses, so the parser needs no change.

To keep this general, I added an optional ToolIntegration.cmd_resolver: Callable[[Path], str | None] resolved per scan root in _framework_tool_phase. When it is absent or returns None, behaviour is unchanged — so projects on Next <=15, or with nothing installed to inspect, keep the historical command.

Patch

I have a working, tested branch. Diff summary:

 .../frameworks/phases.py                          | 34 ++++++---
 .../frameworks/specs/nextjs.py                    | 46 +++++++++++-
 .../frameworks/types.py                           |  6 ++
 .../typescript/tests/test_ts_nextjs_framework.py  | 75 ++++++++++++++++++++
 4 files changed, 149 insertions(+), 12 deletions(-)

8 new tests cover: the version→command mapping across majors 14/15/16/17, v-prefixed versions, unreadable/absent package.json falling back to None, the spec actually declaring the resolver, and a behavioral test asserting the resolved command reaches the tool runner (verified to fail against pre-fix code, not just pass against post-fix).

Full suite: 6757 passed. Four failures exist on main before this change and are unrelated (test_ts_nextjs_framework is not among them):

  • desloppify/languages/ruby/tests/test_init.py::test_has_required_phases[Coupling + cycles + orphaned]
  • 3 in desloppify/tests/lang/common/test_bash_unused_imports.py

I could not push a branch (no write access to this repo). Happy to open a PR from a fork if that is useful — just say the word and I will send it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions