Today I found that an npm package version, Babel 6.0.15, that my application relies on had been removed from npm.
This caused compilation failure on a new pc, and I had to go manually find the closest available version for it, and all the cascading version changes it affected on related packages.
What is the best of way of dealing with npm packages, now that I know they can go missing at any time?
Do you check your node_modules folder into source control?
Is there a rule on npm about what versions (major, minor, etc) may be removed by the creator, and which are more 'long term support' and must be retained?
How do you get npm locally to inform you when 'npm update' fails on a new pc, rather than silently failing?
After thinking about this for a while I wrote a blog post summarising what I think is best practice. Reproduced below:
Summary
Specify exact versions of all npm modules, e.g. “alt”: “0.17.8”
Commit your node_modules folder into source control
Don’t use DefinitelyTyped or any other external library Typescript definition tools
Why?
Some of these principles might be controversial, so here’s my reasoning:
Specify exact versions of all npm modules
Semver (semantic versioning) says that breaking changes should occur only if the major version changes. So you should be able to say just “alt”: “0.17”
But I’ve found in practice that even patch changes (bugfixes) can break your application – because libraries that rely on these library often expect some tiny behaviour in a particular version not to change. So in order for all your particular versions of particular libraries to work, they need to rely on exact versions of other libraries.
Commit your node_modules folder into source control
I first assumed that all versions of famous npm libraries would remain there indefinitely. But I then discovered that creators often remove old versions of their software from npm – which then breaks the cascading chain of exact version number dependencies you’ve configured for your app.
Yes, committing all your npm libraries will take up space in your repository, but they’re text files after all, not .DLLs, so they’ll get compressed really small. And the alternative is one day not being able to compile your app at all on a new computer because a library has been completely removed from npm.
Don’t use DefinitelyTyped or any other external library Typescript definition tools
It’s wonderful to be given compile errors for external tools you use. But I’ve found it’s not worth the effort because:
there’s no way to match the definition file version number and npm library version number, so you get definitions that are out of sync with the library you are using
they often have bugs
the type bugs you catch at compile time are probably going to occur in your own app, not in how you call external libraries
Instead of using .d.ts files for external libraries, just say:
declare module 'lodash'
{
let x: any;
export = x;
}
Or use the –allowJS flag in Typescript 1.8 onwards.
Related
I'm responsible for maintaining a bunch of npm packages. Specifically, I am a maintainer of Apollo Server. Our latest major version combines over a dozen apollo-server-* packages into a single #apollo/server package.
We are going to use npm deprecate to mark the old packages as deprecated to help people find the new one. But I'm running into a bit of a pickle.
If my testing is correct, npm deprecate PACKAGENAME is actually equivalent to "individually mark all current versions of PACKAGENAME as deprecated". But if we then go ahead and publish another version of the package later, the new version appears to not be deprecated.
While we want people to upgrade to the new package, we still may publish some versions of the old package, for security fixes and the like. And unfortunately that will often mean publishing over a dozen separate packages.
So if I'm not confused, this will mean we have to re-run npm deprecate after any publish, or else the package will effectively end up non-deprecated?
So my next thought was to do the deprecation in a shell script, to either be run in CI post-publish or manually by a developer. But unless my testing was incorrect, it does not look like you can use NPM_TOKEN=xxx npm deprecate at all (whether it's an automation token or a publish token): I get an error that the package does not exist. So the script will have to be run manually... and will require me to enter dozens of OTPs.
So my question is: if I have a project that consists of dozens of packages that I want to keep deprecated even if I publish patches in the future, do I really need to maintain a shell script that runs npm deprecate dozens of times and requires me to manually enter dozens of OTPs? Or is there an easier way?
I would like to hard-pin my NPM dependencies. "Hard-pinning" would mean that an automated process would check my dependency list for certain packages with certain versions and if a package has been locally upgraded, an custom error message should be shown (ideally, this should be integrated in a pre-push Git hook).
The reasons for wanting this behavior could be:
external dependencies (e.g. other teams integrating with your project, requiring certain versions)
broken or unwanted behavior because of certain issues (e.g. "wait until #124 is fixed")
known non-obvious migration effort for major upgrades
upgrade incompatibilities (e.g. newest version requires, but does not enforce, a newer peer dependency).
Normal pinning does not cut it in this case: it's trivial to update pinned packages anyway, comments do not work with package.json without extra effort and sometimes the reasons are too important not to be displayed explicitly.
How can I achieve such "hard-pinning"?
I've recently installed some npm package (recommended Kubernetes client) for my react app.
After writing code that uses the package and deploying the code for testing I got some weird errors about missing functions or packages. Then I've read the documentation and realized that the package was Node-only.
Is there any way to check that the npm package works in browser before writing code that uses the package?
Python packages specify compatible python versions. Do npm packages have something like this whether they indicate support for particular Node versions and the browsers?
Some packages/libraries contain .browserlistrc file which I've found to be a starting point to find out the browsers and platforms the devs intend to support or have their code compile for. While it may not always be true and the package might just be able to support a browser that isn't mentioned, it's a good starting point. It surely helps to find out if IE (the bane of front-end dev) is supported or not.
Then again many packages don't necessarily include a .browserlistrc. You can then check the package.json for a "browserslist" field.
If neither are found, you can always clone the repo and add your own .browserlistrc in the root with queries that will let you know if the package supports your intended browser or platform - little more work but yeah it can help. Not full proof but a decent enough way to find out.
Though the best answer is really to just ask the maintainers.
I consider to use Hexo (the static blog generator) based on npm. I wonder one thing, what if although one npm package (dependency) will not be longer available? Each package has its own author and it can cancel support or completely remove it from npm's repository at any time.
So what do I do if missing one of the npm package affect on running Hexo and consequently I'll not be able to generate my blog in the future?
Although this can happen (and happened at least once), it is not a serious problem ussually. While you will be waiting until somebody will fix the missing dependency (it take place quickly on popular packages like Hexo) you can use older working version. And if you want to be 100% sure, you can commit node_modules together with your web sources (see discussion here).
I see this package called gulp-copy and I can't see anywhere if it's adopted for the latest version of gulp. Is that never an issue? I'm worried that I happen to pick wrong package constellation or perhaps an obsolete configuration all together.
Questions are:
In this particular case, does the linked gulp-copy work well with gulp 4?
Is there a general way to determine which packages work well with gulp?
There is no generalized way to determine whether a certain package only works with gulp 3 or gulp 4 (besides reading the documentation for that package). Package creators cannot programmatically specify what version of gulp their package supports and there's no warning when using a package that's designed for a different version of gulp.
That being said, there are some heuristics you can use depending on what kind of packages you are dealing with:
General node packages: those are packages that were not specifically designed for gulp at all. You can use them with gulp, because you can use any node package with gulp, but they make sense outside of gulp as well.
These packages should work with any version of gulp since they don't contain gulp-specific code and are therefore independent of any changes made to gulp. Examples that are often used with gulp are merge-stream and del.
Gulp-specific packages on the other hand can be affected by changes to gulp.
Among those there's gulp plugins which are packages that are supposed to be used in gulp streams with .pipe(). Their names almost always start with gulp-, they are tagged with gulpplugin on npm and listed on the GulpJS website.
These should also generally be safe to use with any version of gulp. Gulp streams are just regular nodejs streams, so those plugins should work with either version of gulp (although nodejs streams have their own history of compatibility problems, but that's not really relevant anymore). Barring major changes to the vinyl file format there's not much that can happen that might affect gulp plugins.
The gulp-copy plugin that you mention falls into this category and should be safe to use with both gulp 3 and gulp 4.
All that being said there are a few gulp plugins that only make sense for a specific version. gulp-plumber for example fixes an annoying issue with error handling in streams that is only necessary for gulp 3, but not gulp 4. gulp-src-ordered-globs circumvents a problem with ignore patterns in gulp 3 that's fixed in gulp 4.
Finally there's what I like to call gulp extensions. They're not supposed to be used with .pipe(). Instead they extend the capabilities of gulp in other ways.
These are the ones you need to watch out for. A lot of them deal with gulp's task running capabilities which have undergone major changes between gulp 3 and gulp 4. There's probably many packages in this category that only work with a certain version of gulp.
I wouldn't worry too much about it though. Most of those packages will prominently display their limitations in their documentation. run-sequence for example has a big fat note at the top informing the user that this is a temporary solution for gulp 3. I published a package named gulp-parameterized the other day that only works with gulp 4 and it screams so in all-caps at the top of the docs.
Basically scan the documentation of any package you want to use for these kinds of notes and you should be relatively safe.