zkat / pacote

programmatic npm package and metadata downloader (moved!)
https://github.com/npm/pacote
MIT License
280 stars 62 forks source link

How to make pacote respect .npmrc? #156

Open DanielSWolf opened 6 years ago

DanielSWolf commented 6 years ago

I need to download NPM packages and get the exact same results a local NPM installation would. However, there seems to be one major difference: NPM looks for an .npmrc file. If it exists, settings such as the NPM registry and authentication information are taken from this file.

Pacote, on the other hand, seems to ignore .npmrc files. Unless explicitly given different options, it always uses https://registry.npmjs.org as registry and doesn't perform any authentication.

Is there a way to make pacote behave identical to NPM? I could of course locate .npmrc myself, parse it, and pass the results to pacote. But I feel that this code must already exist somewhere.

DanielSWolf commented 6 years ago

I figured it out myself. 😃

NPM already contains code that locates its config files, parses them, and creates an options object specifically for use with pacote. The only downside to using this code is that it's deep within the NPM package and isn't part of the "official" NPM API. So any new NPM release may contain a breaking change.

Here's a working example:

const npm = require('npm');
const pacoteOpts = require('npm/lib/config/pacote');
const pacote = require('pacote');

async function main() {
  await new Promise((resolve, reject) => {
    npm.load(error => { if (error) reject(error); else resolve(); });
  });
  const opts = pacoteOpts();
  const package = await pacote.manifest('lodash@1.0.0', opts);
  console.log(package);
}

main();
zkat commented 6 years ago

The project to extract npm's config library is very very big, very complicated, and full of landmines. We're in the middle of chipping away at that monstrosity, and I definitely hope we have a standalone npm config reader/parser/manager at some point. In the meantime, your hack will work with the current version of pacote.

Word of warning: as part of that refactor, I'm going to be changing the API for pacote's opts MASSIVELY, and the code you're using for this will break with pacote@9 once that's released. But it should make it much, much easier to integrate pacote with npm's config. Stay tuned, and be warned :)

zkat commented 6 years ago

see https://github.com/zkat/pacote/pull/146

DanielSWolf commented 6 years ago

Thanks for your explanations! So I'll stick with my hack for the time being.

If possible, it would be great if you could update this issue once there is a clean solution for reading the NPM configuration.

zkat commented 6 years ago

I probably won't be able to do that. Tracking all the issues I get pinged on is a bit tricky as-is, so I'll almost certainly fail to do this.

DanielSWolf commented 5 years ago

I notice that npm@6.6.0 replaced /lib/config/pacote with /lib/config/figgy-config.js, so my original hack doesn't work any more. What's the recommended way of reading the NPM configuration for pacote now?

chbota commented 5 years ago

You should be able to use libnpmconfig

DanielSWolf commented 5 years ago

Are the two directly compatible? That is, can I do this:

const config = require('libnpmconfig').read();
const package = await pacote.manifest('lodash@1.0.0', config);

Or do I have to transform the configuration first?

chbota commented 5 years ago

require('libnpmconfig').read() will return an object with the fields defined here: https://docs.npmjs.com/misc/config#config-settings

pacote.manifest expects the options defined here (+ other pacote-specific opts): https://www.npmjs.com/package/npm-registry-fetch#fetch-options

For most of these options, they're directly compatible (e.g., registry, cert, ca), while others (at first glance) don't appear to be named the same (e.g., scope vs project-scope), so you may need to do the mapping yourself if you care about those options