jkrems / proposal-pkg-exports

Proposal for Bare Module Specifier Resolution in node.js
MIT License
129 stars 14 forks source link

Method for finding published binaries from module with `exports` #45

Closed coreyfarrell closed 4 years ago

coreyfarrell commented 4 years ago

Does anyone have a suggestion for how to find a binary from a package which specifies exports? For example:

{
  "name": "cli-util",
  "bin": "./bin.js",
  "exports": {}
}

Without exports I could do something like:

const bin = path.join(
  path.dirname(require.resolve('cli-util/package.json')),
  require('cli-util/package.json').bin
);

This would ensure I find the binary associated with the copy of cli-util installed as my dependency (./node_modules/cli-util/bin.js or ../cli-util/bin.js). I understand that using libraries is be preferred over running binaries but this pattern is needed more than never.

Ref https://github.com/nodejs/modules/issues/445 I understand the point is to encapsulate the internals of a module but files linked via bin are not exactly internal.

guybedford commented 4 years ago

bin files are created by npm into node_modules/.bin/name, and will work absolutely fine from there.

I think it's reasonable to treat bins as private from a module perspective, but public from a command perspective unless packages explicitly want to expose the bin as a modular API as well.

This is because bin files aren't typically module.exports = api style modules anyway.

If the problem is just running the bin, yes, other approaches than going through the resolver does sound the best route.

coreyfarrell commented 4 years ago

I just had a crazy thought and it actually works (EDIT: testing in Linux only):

const cliUtil = require.resolve('.bin/cli-util');
// ... use child_process to spawn cliUtil

Is this a reasonable alternative to resolving bin's through package.json? It was somewhat surprising to me this works from node_modules/pkg1/index.js in the following structure:

node_modules/.bin/cli-util
node_modules/pkg1/index.js
node_modules/pkg1/node_modules/.bin/something-else

I would actually expect this to see node_modules/pkg1/node_modules/.bin and find the lack of cli-util in that folder and fail. It actually still resolves cli-util installed to the root node_modules. I'm not sure if this is a bug or a feature? It works this way at least back to node.js 0.10 so probably cannot be changed regardless?

jkrems commented 4 years ago

Yeah, require will continue walking for files. Resolving .bin/foo using the require resolver is what I would consider the right thing to do. Parsing that info from package.json is a bit more fragile because there's different ways to express that field.

coreyfarrell commented 4 years ago

This seems reasonable to me, thanks.