Normalizing composer.json
If you are using composer, you have probably modified composer.json
at least once to keep things nice and tidy.
In fact, when I started using composer in late summer 2012, I of course edited composer.json
manually, even when I wanted to add or update dependencies. I soon learned that one should run
composer require foo/bar:~x.y.z
instead, as this prevents pulling in undesired updates. I did that, but in order to keep the list of dependencies sorted, I actually had to do it twice:
- require package
- move package in
require
orrequire-dev
section so packages are sorted - run command again (to keep the lock file updated, as the order of dependencies matters for the calculation of the hash)
That's a bit crazy, right? And just to keep things nice and tidy!
Then I came across a tweet by Henrik Joreteg:
Favorite little OCD module that I use *all the time* fixpack: https://t.co/amXS7th05U
— Henrik Joreteg (@HenrikJoreteg) March 25, 2014
Alphabetizes dependencies, and cleans up package.json
Nice, I wasn't alone with liking things nice and tidy!
At the time I was contracting for a company here in Berlin. One of the best parts working there was that a lot of respected members of the PHP community stopped by the office oftentimes, for example, Benjamin Eberlei, David Zuelke, and Nils Adermann. Nils (whom you probably know as one of the maintainers of composer, together with Jordi Boggiano) even regularly worked out of the office. Hesitant at first, I approached him about this issue, and asked him whether they would consider a pull request. I think he liked it, so I was quite happy and figured it should be ok to do something about it.
Nonetheless, it took me until December 2014 until I eventually opened a pull request to add the
--sort-packages
option to composer.
Since then it has been possible to run, for example,
composer require --dev --sort-packages phpunit/phpunit
to require a package and keep packages sorted (if you already have that package required, you can run the command to trigger sorting).
With a bit of tweaking not only packages and platform requirements would be sorted, but platform requirements listed before packages. Thanks to Hanov Ruslan, this behaviour can be configured, making it unnecessary to use the option on the command line.
Not only have a lot of people blogged about the feature, but a quick search on GitHub reveals that the configuration option is used at least 327,598 times! Excellent!
Now, how about taking it a step further? Today I present to you ergebnis/composer-normalize
, a composer plugin for tidying up all of composer.json
!
Run
composer global require ergebnis/composer-normalize
to install the composer plugin globally.
Run
composer normalize
to normalize composer.json
in the working directory or run
composer normalize ~/Sites/foo/bar/composer.json
to normalize composer.json
in the ~/Sites/foo/bar
directory.
The NormalizeCommand
provided by the NormalizePlugin within this package will
- determine whether a
composer.json
exists - determine whether a
composer.lock
exists, and if so, whether it is up to date - use the
ComposerJsonNormalizer
to normalize the content ofcomposer.json
- format the normalized content (either as sniffed, or as specified using the
--indent-size
and--indent-style
options) - write the normalized and formatted content of composer.json back to the file
- update the hash in
composer.lock
if it exists and if an update is necessary
Currently, the following normalizations will be applied
- restructure
composer.json
according to the underlying JSON schema - sort entries in the
bin
section (by value) - sort entries in the
config
,extra
, andscripts-descriptions
sections (by key) - sort entries in the
conflict
,provide
,replace
,require
,require-dev
, andsuggest
sections (as you are used to from thesort-packages
configuration) - normalize version constraints in
conflict
,provide
,replace
,require
, andrequire-dev
sections
The command accepts the following argument
file
: Path tocomposer.json
file (optional, defaults tocomposer.json
in working directory)
The command comes with the following options
--dry-run
: Show the results of normalizing, but do not modify any files--indent-size
: Indent size (an integer greater than 0); should be used with the--indent-style
option--indent-style
: Indent style (one of "space", "tab"); should be used with the--indent-size
option--no-update-lock
: Do not update lock file if it exists
Here is an example of running
composer normalize
on composer/composer
itself:
diff --git a/composer.json b/composer.json
index dd672c3b..330f73d0 100644
--- a/composer.json
+++ b/composer.json
@@ -1,9 +1,13 @@
{
"name": "composer/composer",
+ "type": "library",
"description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
- "keywords": ["package", "dependency", "autoload"],
+ "keywords": [
+ "package",
+ "dependency",
+ "autoload"
+ ],
"homepage": "https://getcomposer.org/",
- "type": "library",
"license": "MIT",
"authors": [
{
@@ -17,52 +21,58 @@
"homepage": "https://seld.be"
}
],
- "support": {
- "irc": "irc://irc.freenode.org/composer",
- "issues": "https://github.com/composer/composer/issues"
- },
"require": {
"php": "^5.3.2 || ^7.0",
- "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
"composer/ca-bundle": "^1.0",
"composer/semver": "^1.0",
"composer/spdx-licenses": "^1.2",
+ "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
+ "psr/log": "^1.0",
+ "seld/cli-prompt": "^1.0",
"seld/jsonlint": "^1.4",
+ "seld/phar-utils": "^1.0",
"symfony/console": "^2.7 || ^3.0 || ^4.0",
- "symfony/finder": "^2.7 || ^3.0 || ^4.0",
- "symfony/process": "^2.7 || ^3.0 || ^4.0",
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
- "seld/phar-utils": "^1.0",
- "seld/cli-prompt": "^1.0",
- "psr/log": "^1.0"
+ "symfony/finder": "^2.7 || ^3.0 || ^4.0",
+ "symfony/process": "^2.7 || ^3.0 || ^4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7",
"phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
},
+ "suggest": {
+ "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
+ "ext-zip": "Enabling the zip extension allows you to unzip archives",
+ "ext-zlib": "Allow gzip compression of HTTP requests"
+ },
"config": {
"platform": {
"php": "5.3.9"
}
},
- "suggest": {
- "ext-zip": "Enabling the zip extension allows you to unzip archives",
- "ext-zlib": "Allow gzip compression of HTTP requests",
- "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
},
"autoload": {
- "psr-4": { "Composer\\": "src/Composer" }
+ "psr-4": {
+ "Composer\\": "src/Composer"
+ }
},
"autoload-dev": {
- "psr-4": { "Composer\\Test\\": "tests/Composer/Test" }
- },
- "bin": ["bin/composer"],
- "extra": {
- "branch-alias": {
- "dev-master": "1.7-dev"
+ "psr-4": {
+ "Composer\\Test\\": "tests/Composer/Test"
}
},
+ "bin": [
+ "bin/composer"
+ ],
"scripts": {
"test": "phpunit"
+ },
+ "support": {
+ "issues": "https://github.com/composer/composer/issues",
+ "irc": "irc://irc.freenode.org/composer"
}
}
If you need to change the indentation, you can use the --indent-size
and --indent-style
options. Here's an example of running
composer normalize --indent-size=2 --indent-style=space
on composer/composer
itself:
diff --git a/composer.json b/composer.json
index dd672c3b..071c99c6 100644
--- a/composer.json
+++ b/composer.json
@@ -1,68 +1,78 @@
{
- "name": "composer/composer",
- "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
- "keywords": ["package", "dependency", "autoload"],
- "homepage": "https://getcomposer.org/",
- "type": "library",
- "license": "MIT",
- "authors": [
- {
- "name": "Nils Adermann",
- "email": "naderman@naderman.de",
- "homepage": "https://www.naderman.de"
- },
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "https://seld.be"
- }
- ],
- "support": {
- "irc": "irc://irc.freenode.org/composer",
- "issues": "https://github.com/composer/composer/issues"
+ "name": "composer/composer",
+ "type": "library",
+ "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.",
+ "keywords": [
+ "package",
+ "dependency",
+ "autoload"
+ ],
+ "homepage": "https://getcomposer.org/",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "https://www.naderman.de"
},
- "require": {
- "php": "^5.3.2 || ^7.0",
- "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
- "composer/ca-bundle": "^1.0",
- "composer/semver": "^1.0",
- "composer/spdx-licenses": "^1.2",
- "seld/jsonlint": "^1.4",
- "symfony/console": "^2.7 || ^3.0 || ^4.0",
- "symfony/finder": "^2.7 || ^3.0 || ^4.0",
- "symfony/process": "^2.7 || ^3.0 || ^4.0",
- "symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
- "seld/phar-utils": "^1.0",
- "seld/cli-prompt": "^1.0",
- "psr/log": "^1.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7",
- "phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
- },
- "config": {
- "platform": {
- "php": "5.3.9"
- }
- },
- "suggest": {
- "ext-zip": "Enabling the zip extension allows you to unzip archives",
- "ext-zlib": "Allow gzip compression of HTTP requests",
- "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages"
- },
- "autoload": {
- "psr-4": { "Composer\\": "src/Composer" }
- },
- "autoload-dev": {
- "psr-4": { "Composer\\Test\\": "tests/Composer/Test" }
- },
- "bin": ["bin/composer"],
- "extra": {
- "branch-alias": {
- "dev-master": "1.7-dev"
- }
- },
- "scripts": {
- "test": "phpunit"
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "require": {
+ "php": "^5.3.2 || ^7.0",
+ "composer/ca-bundle": "^1.0",
+ "composer/semver": "^1.0",
+ "composer/spdx-licenses": "^1.2",
+ "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
+ "psr/log": "^1.0",
+ "seld/cli-prompt": "^1.0",
+ "seld/jsonlint": "^1.4",
+ "seld/phar-utils": "^1.0",
+ "symfony/console": "^2.7 || ^3.0 || ^4.0",
+ "symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
+ "symfony/finder": "^2.7 || ^3.0 || ^4.0",
+ "symfony/process": "^2.7 || ^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7",
+ "phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
+ },
+ "suggest": {
+ "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
+ "ext-zip": "Enabling the zip extension allows you to unzip archives",
+ "ext-zlib": "Allow gzip compression of HTTP requests"
+ },
+ "config": {
+ "platform": {
+ "php": "5.3.9"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\": "src/Composer"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Composer\\Test\\": "tests/Composer/Test"
}
+ },
+ "bin": [
+ "bin/composer"
+ ],
+ "scripts": {
+ "test": "phpunit"
+ },
+ "support": {
+ "issues": "https://github.com/composer/composer/issues",
+ "irc": "irc://irc.freenode.org/composer"
+ }
}
Hope you like it!
Project on GitHub
ergebnis/composer-normalize
🎵 Provides a a composer
plugin for normalizing composer.json
.
Find out more at ergebnis/composer-normalize
.