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
requireorrequire-devsection 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.jsonexists - determine whether a
composer.lockexists, and if so, whether it is up to date - use the
ComposerJsonNormalizerto normalize the content ofcomposer.json - format the normalized content (either as sniffed, or as specified using the
--indent-sizeand--indent-styleoptions) - write the normalized and formatted content of composer.json back to the file
- update the hash in
composer.lockif it exists and if an update is necessary
Currently, the following normalizations will be applied
- restructure
composer.jsonaccording to the underlying JSON schema - sort entries in the
binsection (by value) - sort entries in the
config,extra, andscripts-descriptionssections (by key) - sort entries in the
conflict,provide,replace,require,require-dev, andsuggestsections (as you are used to from thesort-packagesconfiguration) - normalize version constraints in
conflict,provide,replace,require, andrequire-devsections
The command accepts the following argument
file: Path tocomposer.jsonfile (optional, defaults tocomposer.jsonin 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-styleoption--indent-style: Indent style (one of "space", "tab"); should be used with the--indent-sizeoption--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.