Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the package.json file from within an npm script that runs on preinstall

Tags:

node.js

npm

So I need to read the package.json before installing a new package via npm.

Why reading package.json in the first place?

I am using npm for CSS components that are individually versioned and can have inter dependencies. (No javascript is delivered)

Looking for version conflicts for a bunch of dependencies I need to detect when package A requires package [email protected] and package B requires package [email protected] and deal with it.

Npm (as of version 3) deals with these issues by nesting a conflicting module deeper inside the tree. You now end up with both versions of the same module. CSS has a global namespace and a mixin (in Sasss case) would then overwrite each other and break your CSS.

This flat dependency issue is perfectly outlined in the npm blog: http://blog.npmjs.org/post/101775448305/npm-and-front-end-packaging

Even not considering our specific use case it strikes me as odd that you don't have access to the package.json in preinstall and postinstall scripts. They seem to be just for that use case.

What I tried

My package.json of the package I'm installing looks like this:

{
    "name": "testmodule",
    "version": "0.3.6",
    "description": "TODO",
    "scripts": {
        "preinstall": "npm i some-script && some-script",
    },
    "author": "TODO",
    "license": "MIT"
}

Inside that some-script package I run:

console.log( process.cwd() );
console.log( __dirname );

I then run:

~/path/to/folder $ npm i testmodule

This will result in:

$ npm i testmodule

> [email protected] preinstall /path/to/folder/node_modules/.staging/testmodule-5cc9d333
> some-script

/path/to/folder/node_modules/.staging/test-module-5cc9d333
/path/to/folder/node_modules/.staging/test-module-5cc9d333/node_modules/some-script

Now I totally get that I can't really access the root of where npm i was ran because my script was run by a subprocess of npm and has an entirely different root.

I then thought npm root should keep track where the actual root was for me and passed that as a parameter to my script from inside the testmodule package.json:

{
        "name": "testmodule",
        "version": "0.3.6",
        "description": "TODO",
        "scripts": {
                "preinstall": "npm i some-script && some-script \"$(npm root)\"",
        },
        "author": "TODO",
        "license": "MIT"
}

Unfortunately that also defaults back to a staging path:

/path/to/folder/node_modules/.staging/testmodule-5cc9d333/node_modules

I filed an issue with the registry but not holding my hopes up for them to get to that in time. Also my script needs to work on older npm installations.

In the meantime I came up with something like that inside my some-script:

let pgkPath = process.cwd().split('/node_modules/')[0];

That will return /path/to/folder/ which is correct but it makes the assumption no-one runs an npm i inside a folder incidentally named node_modules... Seems hacky.

Question

How can I access the path to the package.json from inside an npm script that is run via preinstall? To me that seems like something not too outrageous to ask for?

like image 458
Dominik Avatar asked Dec 21 '16 23:12

Dominik


1 Answers

I don't understand your use-case entirely, but to answer your specific question of finding a parent package.json from a preinstall script:

Pass $(cd .. && npm prefix) as an argument to your script, then load ./package.json.

npm prefix will return the closest parent directory to contain a package.json file, which when invoked from the .. directory, should return the parent npm package's path.

{
        "name": "testmodule",
        "version": "0.3.6",
        "description": "TODO",
        "scripts": {
            "preinstall": "npm i some-script && some-script \"$(cd .. && npm prefix)\"",
        },
        "author": "TODO",
        "license": "MIT"
}
like image 57
wjordan Avatar answered Sep 18 '22 17:09

wjordan