Is it possible to specify a runtime dependency of a code generator? - npm

I am working on an npm package that is used as a CLI to generate code. It makes sense that this project should generally be used as a devDependency.
But the generated code will require a third-party library at runtime. Is there a way to manage this dependency via package.json? I have seen other libraries include a note in the README that defines this dependency, but there is no guarantee that the end user will read this README, so I would like to be able to enforce this.
One thought I had was to use a preinstall script to validate that this third-party library was already installed, otherwise throw an error and don't allow the user to install. But that feels like more of a hack than a solution.
I anticipate someone saying "but won't it be obvious when none of the generated code works?" which is absolutely correct, but ideally this process should fail well before runtime.
I am not sure the specifics really matter here, but my package generates boilerplate TypeScript code for an API project, from an OpenAPI spec, and depends on Express.js at runtime.

This sounds like a peer dependency. You can specify a peer dependency in package.json with the peerDependencies key.
In npm 6, missing peer dependencies will result in a warning.
In npm 7, missing peer dependencies are installed.

Related

Given an npm package, how do I know whether it will work in browser?

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.

How to solve missing peer deps in npm?

There are a number of questions here on SO, which deal with the problem of conflicting/missing peer dependency, but they all seem to be directed at a very specific library (or got no answer at all).
I would like to know how to solve this problem in general. When a dependency requires a specific version of another lib and I have a newer version installed (example: React 17.0.1 as project dependency, react#^15.3.0 || ^16.0.0-alpha, required by react-virtualized-auto-sizer#1.0.4), how can I fix this situation?
So far I ignored the warning and the application seems to work fine. But I don't want to rely only on the current appearance.
Somewhere I read that such "local" versions are resolved (by NPM) by placing the 4th party dep in a sub node_modules folder of the lib, which requires it, but I haven't seen this happening.

Understanding how to manage dependencies in a custom npm library

I am trying to write my first set of npm libraries for a large, distributed application and am running into problems around how to manage dependencies. Npm has the concept of "dependencies" and "peerDependencies".
From what I read, peerDependencies are needed when you want to write a "plugin" where the consuming application is already expected to have the dependency installed on their end. For example, this might be building a plugin for webpack.
Then there are regular dependencies needed at runtime by a library. In my scenario, I am trying to write a library to abstract Authentication and Authorization away from the consuming application. This new library (lets call it #myapp/auth) has a dependency on the #auth0/angular-jwt library as it uses it to deal with JWTs. I want this library to be completely abstracted away from the consuming application and therefore not force that consuming application to install it manually.
With that said, it sounds like I need to add #auth0/angular-jwt to dependencies in my library and then add it to the whitelistedNonPeerDependencies which allows it to build.
This confuses me because the ng-packagr docs say that we should always use peerDependencies when possible. If this is the case, then why when you install a larger library like express, you simply run npm install express --save and you get ALL of the dependencies of express without manually installing each one.
What is the proper way to include package dependencies in a custom library that you do not want the consuming application to install themselves?

npm package.json dependencies - for a library component

Lets say I am working on a library that will be consumed by other developers. MyPackage has a dependency on moment. The developer that consumes my package also has a dependency on moment. So moment will exist as a "dependency" in both library package.json and application package.json (and thus get packaged twice). Is there a way to package it just once? If the consumer has it, use theirs, else use mine?
It's already happening by default on fresh installs if dependency ranges match.
npm v>=3 does gang the dependencies, depending on the installation order and depth, see here.
Also, if you kept working on the same folder for a while, there might be some cruft, which could be wiped using npm dedupe, see here.
In theory, moment should not be duplicated if both your library and developer's library are consuming the same version ranges of it. At least if npm dedupe is called or node_modules are wiped and npm i-nstalled.

How do you deal with people removing npm package versions?

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.