Skip to content

fix(pre-commit): resolve prettier plugins from nearest package.json dir - #370

Merged
twistedmelonman merged 2 commits into
mainfrom
claude/fix-prettier-nested-node-modules-8a854048
Sep 25, 2026
Merged

twistedmelonman merged 2 commits into
mainfrom
claude/fix-prettier-nested-node-modules-8a854048

Conversation

@twistedmelonman

Copy link
Copy Markdown
Member

Summary

Fixes #321. The prettier pre-commit hook ran npx prettier --write --ignore-unknown "$@" from the repo root. Prettier resolves a .prettierrc's plugins: entries relative to the process cwd, not the config file that declares them, so any repo with per-package node_modules (no root install) failed to resolve a plugin declared by a nested package's .prettierrc, blocking every commit that touched it.

Changes

  • git/hooks/lint-prettier.sh (new): walks each staged file up to its nearest package.json ancestor and runs npx prettier from there, so resolution finds that package's own node_modules. Falls back to the previous behavior (run from the given path) when no package.json ancestor exists. Rejects a resolved relative path that climbs out of the package dir.
  • pre-commit/config.yaml: prettier hook's entry now calls $HOME/.config/git/hooks/lint-prettier.sh, matching the existing lint-shell, lint-yamllint, lint-markdown, and lint-zizmor entries (lines 82/101/160/218). The entry falls back to the pre-fix npx prettier call when that script isn't present yet, since lint-prettier.sh is deployed to ~/.config/git/hooks by symlink repair (install.sh), which doesn't run itself — a machine that has pulled this change but not yet re-run install/repair would otherwise hit "No such file" on every prettier commit.
  • bash/tests/test-lint-prettier-nested-node-modules.sh (new): known-bad case reproduces the issue's exact repro shape (nested package, no root package.json), then verifies the fixed script resolves the plugin, the no-package.json fallback still formats, no-files exits clean, and the config.yaml entry itself falls back to the old npx prettier codepath (proven via a stub npx) when the script isn't installed.

Note on local review

The commit-time reviewer initially flagged the $HOME/... entry form as a BLOCKING false positive ("$HOME not expanded"), on two separate commit attempts. This is disproven by:

On the third commit attempt the adversarial-reviewer returned PASS and correctly identified the code-reviewer's claim as false; the arbiter sided with it and the commit went through on its own merits, not via a bypass.

Test plan

  • Known-bad case reproduces (plugin fails to resolve from repo root)
  • bash bash/tests/run-tests.sh — 34/34 passed
  • shellcheck -S info clean, no disable directives
  • Pre-push full-diff review: PASS
  • Pre-push full test suite: 34/34 passed

The prettier hook ran `npx prettier --write --ignore-unknown "$@"` from the
repo root. Prettier resolves a .prettierrc's `plugins:` entries relative to
the process cwd, not the config file, so any repo with per-package
node_modules (no root install) failed to resolve a plugin declared by a
nested package's .prettierrc, blocking every commit that touched it.

Extract the entry into git/hooks/lint-prettier.sh, which walks up from each
file to the nearest package.json and runs prettier from there, falling back
to the previous behavior when no package.json ancestor exists. Rejects a
resolved relative path that climbs out of the package dir, and makes each
prettier invocation's exit code explicit as defensive clarity (set -e
already propagates a failing subshell; this is not a fix for that).

The `bash -c '$HOME/...' --` entry form matches the existing lint-shell,
lint-yamllint, lint-markdown, and lint-zizmor entries at
pre-commit/config.yaml lines 82/101/160/218.

lint-prettier.sh is deployed to ~/.config/git/hooks by symlink repair
(install.sh), which does not run itself on every machine (ASIAGO, TILSIT,
MIMOLETTE). A machine that has pulled this change but not yet re-run
install/repair would hit "No such file" on every prettier commit, so the
entry now falls back to the pre-fix behavior (bare `npx prettier`) when the
script is not yet present at that path.

Closes #321
@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

claude Bot commented Sep 25, 2026

Copy link
Copy Markdown

New lint-prettier.sh hook resolves Prettier plugins from the nearest package.json ancestor directory, fixing the plugin resolution failure from #321. The >- YAML folding in config.yaml correctly collapses the multiline entry to a single shell command. The .. traversal guard is sound. The fallback when no package.json ancestor exists preserves the prior behavior. No BLOCK-level issues found.

VERDICT: PASS

@twistedmelonman
twistedmelonman merged commit 5fc84f9 into main Sep 25, 2026
5 checks passed
@twistedmelonman
twistedmelonman deleted the claude/fix-prettier-nested-node-modules-8a854048 branch September 25, 2026 20:53
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.

prettier pre-commit hook fails in repos with nested package node_modules (cwd-relative plugin resolution)

1 participant