If you subscribe to the philosophy of Semantic Versioning (you should!) and use npm, there is a certain behaviour that might trip you up. If you don’t want to waste a full hour like I did, this might be helpful.
I use Eleventy, and it hasn’t hit
v1.0 yet. It is, therefore, implicitly signalled by the developer to not be ready for production.
The precursor to the
v1.0 release -
v0.11.0 - was published recently.
As I was on
v0.10.0, the latest update should have been simple to upgrade to, right? Just using the update option on
npm and specifying the package should have done, like so:
npm upgrade @11ty/eleventy
Well, turns out, this didn’t work! 😭
Time to explore.
How is a dependency speciﬁed in package.json by default?
First, what happens when you
npm install something?
npm install @11ty/eleventy
By default, it saves the package as a dependency in your project’s
package.json ﬁle using the caret syntax for the version scheme:
The caret syntax almost always means: if there is a [minor] version update or a [patch] update, but not a [major] update, download and save the new package version.
Ok, so what?
Well, let’s demystify the almost always above, and you’ll see some interesting phenomenon.
For a pre
v1.0 release, also called a pre-release, the rule I mentioned above stands void — and that’s why the
npm upgrade command, by design, is not meant to work in my case.
It follows a different version matching algorithm. What is that? Look at npm’s documentation on semver:
Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple. In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for versions 0.X >=0.1.0, and no updates for versions 0.0.X.
Many authors treat a 0.x version as if the x were the major “breaking-change” indicator.
Caret ranges are ideal when an author may make breaking changes between 0.2.4 and 0.3.0 releases, which is a common practice. However, it presumes that there will not be breaking changes between 0.2.4 and 0.2.5. It allows for changes that are presumed to be additive (but non-breaking), according to commonly observed practices.
This means if Eleventy had a
v0.10.1 release, I would have had no trouble upgrading. There is no breaking change expected in this release.
If Eleventy had versioned all its releases so far using just the patch ﬁeld, then no upgrade would have happened between, for example,
v0.0.11. Since only the patch ﬁeld is being used, every release is presumed to be a breaking change.
A similar behaviour applies for pre-release updates to production (
v1.0+) packages. If a developer has released
v1.1-alpha.1, it will be ignored by
npm upgrade. An alpha release, of course, is not ready for production.
How did I ﬁnally upgrade to v0.11.0?
Turns out you have to do it the old-fashioned way. Go ahead and edit the
package.json ﬁle, and specify the version you need. Then install the dependencies again.
This is usually frowned upon for good reason - this, however, is one exception.