9

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?

13
  • 1
    This sounds very much like an XY problem - what are you trying to do that requires you to read that package.json to being with? And not "I'm trying to get value ... out ", why are you doing that? Because there's a good bet there's a better way to do what you need that you simply didn't know about. Commented Dec 21, 2016 at 23:17
  • Good point. I'll added that to the question. Thanks for pointing out. Commented Dec 21, 2016 at 23:22
  • 2
    Dude, what? This is literally what NPM already does for you, it's one of most important things it does, in fact, because if you had to do that by hand it'd be an intractable problem. What arcane real world example have you run into where NPM is not managing concurrent versioning correctly in the dependency tree? In the example you give, NPM would not overwrite one version with another, it will see the versioning conflict and have both versions reside deeper in the tree. Commented Dec 21, 2016 at 23:24
  • Front end use case. Read: blog.npmjs.org/post/101775448305/npm-and-front-end-packaging Commented Dec 21, 2016 at 23:25
  • I've read that post a few times in the past, what real world example do you have? Now is the time to come up with a minimal reproducible example and put that in your post because even these days we do front end bundling with things like browserify or webpack or rollup, and we can tell those exactly what they need to use, as well as npm scripts for running "shx mkdirp assets && cp node_modules/jquery/..../moo/thing.js assets" as a bootstrapping command that doesn't require guess work. It's your codebase, you peg versions and then always know what's in it. Commented Dec 21, 2016 at 23:27

1 Answer 1

3
+50

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"
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.