chore(dev): add opt-in Prettier pre-commit hook and editor format-on-save#355
Open
pjdoland wants to merge 2 commits into
Open
chore(dev): add opt-in Prettier pre-commit hook and editor format-on-save#355pjdoland wants to merge 2 commits into
pjdoland wants to merge 2 commits into
Conversation
Contributors who edit docs or styles sometimes forget to run jlpm lint before committing, so Prettier formatting drift only surfaces in CI. This adds a zero-risk editor layer that fixes the formatting at the source. .editorconfig sets the baseline whitespace rules (and mirrors the package.json 4-space override) so non-VS-Code editors stay consistent. The .vscode settings enable format-on-save with Prettier, scoped per language so Python and any formatter a contributor already configures are left untouched. extensions.json recommends the matching extensions. The .vscode share is opt-in: settings.json and extensions.json are un-ignored in .gitignore while all other (per-user) .vscode files stay ignored.
The recurring red CI on contributor PRs is almost always a single unformatted Markdown, TypeScript, or CSS file: jlpm lint:check gates Prettier across the tree, and it is easy to forget jlpm lint before pushing. This adds an opt-in pre-commit hook that formats staged files so the fix happens before the commit instead of in CI. On jlpm install a postinstall script runs husky, which registers a pre-commit hook that invokes lint-staged. lint-staged runs `prettier --write --ignore-unknown` on staged files only and re-stages the result, so the committed version is already formatted. The hook is Prettier-only: ESLint and Stylelint rule violations are still left for the contributor and CI to surface. postinstall (not prepare) is used because Yarn Berry does not run the root workspace's prepare script on install, but does run postinstall. The `|| exit 0` guard keeps it a no-op where husky is absent (consumers of the published npm package, whose install never receives the husky devDep) and where there is no .git checkout (sdist to wheel builds), so it never breaks packaging. Contributors can skip a single run with `git commit --no-verify` or opt out with `HUSKY=0 jlpm install`. CONTRIBUTING documents both this hook and the editor format-on-save layer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Contributor PRs land red on CI fairly often, and the failure is almost always the same shape: a single Markdown, TypeScript, or CSS file that was edited without running
jlpm lintfirst.jlpm lint:checkgates Prettier across the whole tree (**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}), so one unformatted file is enough to turn the run red even when the actual change is fine.This adds two small, independent, opt-in layers that move that formatting fix from CI back to the moment of editing or committing. Neither changes what CI enforces; they just catch the drift earlier.
jlpm lintremains the manual catch-all.The two layers are split into two commits so either can be adopted or dropped on its own:
9efdcfeEditor format-on-save (zero-risk, no dependencies)..editorconfigsets baseline whitespace rules (and mirrors the existing package.json 4-space override)..vscode/settings.jsonenables format-on-save with Prettier, scoped per language so Python and any formatter a contributor already uses are left untouched..vscode/extensions.jsonrecommends the matching extensions. Only those two.vscodefiles are un-ignored in.gitignore; all other per-user.vscodefiles stay ignored.17d5256Pre-commit hook. Apostinstallscript runs Husky, which registers a pre-commit hook that invokes lint-staged. lint-staged runsprettier --write --ignore-unknownon staged files only and re-stages the result, so the committed version is already formatted.Solution
The load-bearing decisions:
postinstall, notprepare. Husky's documented setup usesprepare, but I confirmed empirically that Yarn Berry (this repo'sjlpm) does not run the root workspace'sprepareon install, while it does runpostinstall. Sopreparewould silently never install the hook here.husky || exit 0guard. Husky is a devDependency, so anyone installing the published@plmbr/notebook-intelligencenpm package does not receive it; a barepostinstall: huskywould fail their install with "command not found." The|| exit 0keeps it a no-op in that case, and also where there is no.git(the sdist to wheel build), so it never affects packaging.exit 0is portable across the POSIX shell, Yarn Berry's portable shell, and cmd.exe.--fixfor those can make non-trivial changes. Because eslint-plugin-prettier and stylelint-prettier both enforce the same Prettier config, formatting fixed by the hook satisfies the Prettier dimension of all three linters."*": "prettier --write --ignore-unknown". Keyed on*rather than a duplicated extension glob, so it cannot drift fromprettier:base.--ignore-unknownskips file types Prettier has no parser for, and Prettier still honors.prettierignore, so staged build output or binaries are left untouched.Testing
husky || exit 0exits 0 when the husky binary is absent (consumer case) and when there is no.git(sdist build case)..gitignorenegation tracks exactly.vscode/settings.jsonand.vscode/extensions.jsonwhile keeping other.vscodefiles ignored (git check-ignore).jlpm lint:check,jlpm tsc --noEmit, andjlpm jest(328 tests) all green. No Python changed, so pytest is not applicable.Risks / follow-ups
git commit --no-verifyor disabled entirely withHUSKY=0 jlpm install..yamlor.scss, for example, would also be formatted.[tool.hatch.build.targets.sdist]exclude list, but the sdist already carries other dev files (CONTRIBUTING.md, lint configs), so pruning only these would be inconsistent. Happy to add it if you would prefer a leaner archive.yarn.lockgrows by the husky and lint-staged dependency trees; the change is purely additive (generated withjlpm).