dragon-code/codestyler
Codestyler is a Composer dev tool that automatically fixes coding style standards in Laravel and PHP projects. It copies preset pint.json and .editorconfig files via the vendor/bin/codestyle command, supporting PHP 8.2–8.5 presets plus Rector and npm/Biome configuration.
Codestyler is not a linter. It is a config-file distributor — a tiny Composer binary whose job is to drop opinionated pint.json, rector.php, .editorconfig and biome.json files into your project root so you never hand-write Laravel Pint rules again. The actual fixing is done by Pint, Rector and Biome, which come along as dependencies. Treat it as scaffolding, not as a runtime dependency.
Minimal path for an existing Laravel app:
composer require dragon-code/codestyler --dev
# Publish the configs (8.2 = your *minimum* supported PHP version)
vendor/bin/codestyle pint 8.2
vendor/bin/codestyle rector laravel
vendor/bin/codestyle editorconfig
vendor/bin/codestyle npm
# Wire the fixer script
composer config scripts.style "vendor/bin/rector && vendor/bin/pint --parallel"
# Run it
composer style
Where to look first, in this order:
composer.json → the scripts block is the whole integration surface (style for fixing, post-update-cmd for config refresh).pint.json → this is the real payload; skim it before your first run so you know what will happen to your codebase. As of 6.8.1 the versioned presets are style-only — the legacy PHP migration rules were stripped out of every 8.x preset (see gotchas), so nothing here will silently modernise your syntax..editorconfig → free IDE consistency (PhpStorm/VS Code must have EditorConfig support enabled, which it is by default).vendor/bin/codestyle with no arguments → lists the available subcommands and presets; it is the only "documentation" you get beyond the README.The classic first use case: a legacy Laravel project with three competing code styles across teams. Install, run composer style once on a clean working tree, commit the resulting diff as a single style: apply dragon-code preset commit, and add that SHA to .git-blame-ignore-revs immediately. Everything after that commit is diff noise-free.
If you are upgrading from ≤ 6.8.0, expect your first republish to shrink pint.json considerably. That is the intended 6.8.1 behaviour, not corruption.
Bootstrap-on-update (the pattern the package is designed for). Put the publishers in post-update-cmd so the presets are refreshed whenever the dependency bumps:
{
"scripts": {
"post-update-cmd": [
"vendor/bin/codestyle pint 8.2",
"vendor/bin/codestyle rector laravel",
"vendor/bin/codestyle editorconfig",
"vendor/bin/codestyle npm",
"composer normalize"
],
"style": [
"vendor/bin/rector",
"vendor/bin/pint --parallel"
]
}
}
Order matters: normalize composer.json last, after composer update has already rewritten the file. If you use the global install (composer global require dragon-code/codestyler), swap every vendor/bin/codestyle for bare codestyle — but see the deprecation gotcha below before building CI on that.
Check-only in CI, fix locally. The style script mutates files, which is wrong for a pull-request gate. Split it:
{
"scripts": {
"style": ["vendor/bin/rector", "vendor/bin/pint --parallel"],
"style:test": ["vendor/bin/rector --dry-run", "vendor/bin/pint --test --parallel"]
}
}
Then composer style:test in a PR job and composer style in a pre-commit hook or a scheduled bot job. This is the difference between a useful gate and a failing build every time someone forgets.
Freeze the config instead of republishing. The moment you edit pint.json to add a project rule, the post-update-cmd publisher will silently overwrite it on the next composer update. Two clean escapes:
post-update-cmd and commit the configs as first-class project files. This is what I'd do for anything long-lived — and 6.8.1 is a good reminder of why: upstream preset edits arrive as an unreviewed diff unless you own the file.pint.project.json and run vendor/bin/pint --config pint.project.json. You lose automatic upstream rule updates, which for a 29-star package is arguably a feature.Adopt the 6.8.1 presets deliberately. Whether you republish or hand-merge, diff the old and new pint.json and decide per-rule. The removed entries were migration rules — dropping them stops the auto-rewrite of legacy constructs, it does not revert code already converted. So the slimmed preset is safe to adopt, but any expectation that "Pint will modernise this file eventually" is now gone; those upgrades are your job (and a one-off rector.php rule or a manual sweep).
Match the preset to your real minimum PHP. The pint 8.2 argument is a ruleset generation target, not your CI PHP version. Running the 8.5 preset on a package that still supports PHP 8.2 can emit rules that produce code your floor cannot parse. Always pass the lowest PHP your composer.json require.php allows. Same logic for rector: laravel preset for Laravel/Symfony-shaped code, default for framework-agnostic packages and libraries.
JS/CSS side. codestyle npm writes a Biome 2.x biome.json. Pair it with npm scripts rather than trying to drive Biome from Composer:
{
"scripts": {
"lint": "npx @biomejs/biome lint --write",
"format": "npx @biomejs/biome format --write",
"style": "npm run lint && npm run format"
}
}
Then "composer style": ["@style", "npm run style"]-style composition if you want one entry point for full-stack repos.
GitHub Actions. The maintainer's shared workflow is the lowest-effort route:
jobs:
Checks:
uses: TheDragonCode/.github/.github/workflows/code-style.yml@main
with:
php: true
node: true
Use it for a dedicated styling repo or a bot branch; for a normal team repo prefer your own style:test job (read-only token) so contributors aren't fighting an auto-committing bot.
Everything is destructive. codestyle editorconfig explicitly replaces an existing .editorconfig; pint and npm behave the same way. There is no --force guard, no merge, no backup. Anything you customise is one composer update away from being deleted. Commit the files and review the diff every release.
6.8.1 rewrites all four versioned presets. The patch removed the old PHP migration rules from 8.2.json, 8.3.json, 8.4.json and 8.5.json. Three consequences:
post-update-cmd, your next composer update silently deletes a chunk of pint.json. It's a legitimate upstream change, not a corrupted download — but it lands in the same commit as the dependency bump, so review the config diff separately before you commit.8.2 vs 8.5 still gates syntax-level rules, but you're no longer opting into four different tiers of modernisation.You are trusting transitive binaries. The finalized require-dev in the README contains only dragon-code/codestyler, yet the scripts call vendor/bin/rector and vendor/bin/pint. Those arrive transitively. If a future release drops or reshuffles a dependency you get a vendor/bin/rector: not found on a completely unrelated composer update. Declare laravel/pint, rector/rector and ergebnis/composer-normalize explicitly in require-dev so the versions you lint with are the versions you chose.
Patch bumps change your diffs. 6.8.1 is a patch release that edits every shipped preset — earlier ones replaced deprecated rule names, changed phpdoc_line_span settings, added 8.5 in 6.5.0. Combined with the auto-republish pattern, a composer update can produce a config diff unrelated to any of your changes. Pin the version ("dragon-code/codestyler": "6.8.1") or accept the churn knowingly. Newer presets also require newer versions — codestyle pint 8.5 on a pre-6.5 release fails with an unknown-preset error, not a helpful message.
post-update-cmd never fires on composer install. That's usually what you want (production --no-dev installs won't try to run a missing binary), but it also means a fresh clone + composer install leaves a contributor without configs if they were never committed. Always commit the generated files.
composer config scripts.style ... overwrites silently. If you already had a style script, the README's one-liner clobbers it with no warning. Check composer.json before running it.
Global install is a dead end. The README's own note states v5 is the last version installed globally via Composer, and v6 moves to command-based publishing. Don't build a shared CI image or a ~/.composer workflow around the global codestyle binary.
Forks and PRs break the shared workflow. It uses permissions: write-all because it commits fixes back. On pull_request events from forks the token is read-only, so the job fails rather than reporting style violations. Split into a read-only pint --test job for PRs and a write job on push to the default branch.
Debugging checklist. vendor/bin/codestyle with no args to confirm the subcommand exists. composer run-script style -v to see which binary fails. For Pint, vendor/bin/pint --test -v shows the offending files without touching them; Rector's --dry-run --debug shows which rules would apply. If composer style passes locally but the bot commit keeps reappearing, you almost certainly have a customised pint.json being overwritten by the publisher — diff the config between the bot branch and your working copy. If a style fix you expected stops being applied after 6.8.1, the rule was almost certainly one of the removed migration rules rather than a config or cache problem.
Know the exclusions before you're surprised. The shipped Biome config excludes tests/Fixtures, analytics.* and metrics.*, and composer.json/package.json are deliberately excluded from Biome formatting because Composer Normalize owns the former. If your JS tooling lives somewhere Biome skips, you're not being formatted, you're being ignored — verify by introducing an obvious formatting error and confirming it gets fixed.
Bus factor. One maintainer, no tracked dependents, 29 stars. The value here is entirely the curated rule set — so consume it as a template. Publish the configs, read them, keep them, and stop depending on the distributor once you're happy. That gives you 100% of the benefit and none of the composer update roulette. 6.8.1 shrinking four preset files is exactly the kind of change that costs you nothing if you own the files and a confusing diff if you don't.
How can I help you explore Laravel packages today?