Can I make npm install follow a certain package.json format? - node.js

When you install an npm package and use the --save, --save-dev or --save-optional options to write the package into your package.json file in the appropriate dependencies property, the entire file appears to be rewritten with 2-space indentation:
$ cat package.json
{
"name": "my-package"
}
$ npm install --save another-package && cat package.json
{
"name": "my-package",
"dependencies": {
"another-package": "~0.1.5"
}
}
Is there any way to make npm follow the existing format, or to specifiy a custom format (e.g. 4-space indentation) for the package.json file?
I can't find anything in the npm options documentation.

After digging through the npm source, it unfortunately appears the answer to my question is definitely "no". When npm install is executed with one of the "save" options, the following happens:
fs.readFile(saveTarget, function (er, data) {
try {
data = JSON.parse(data.toString("utf8"))
} catch (ex) {
er = ex
}
// ...
data = JSON.stringify(data, null, 2) + "\n"
fs.writeFile(saveTarget, data, function (er) {
cb(er, installed, tree, pretty)
})
})
The important line is the call to JSON.stringify. When invoking stringify with the third argument, the returned string indentation is formatted with the specified number of spaces.
Since there is no way to customise the value used by npm internally, this behaviour is currently non-configurable.

Fixed in 64b67f0 and npm 5...
https://github.com/npm/npm/issues/4718#issuecomment-307142397

Related

Self-published NPM package not importing anything when added

I published a very simple NPM package with the following:
module.exports = { foo: "baz" };
When webpacked, it looks like the line below, and this file is referenced as the main property in package.json.
(()=>{var r={579:r=>{r.exports={foo:"baz"}}},o={};!function t(e){if(o[e])return o[e].exports;var p=o[e]={exports:{}};return r[e](p,p.exports,t),p.exports}(579)})();
Now, in a separate project when I install the package and try to import it, I get nothing:
const obj = require('mypackage')
console.log(obj)
// => {}
import obj from 'mypackage'
console.log(obj)
// => {}
What is missing here? How do I get this exported object to come through to the installed NPM package?
Rather than messing with webpack config, my solution was indeed to use Microbundle as suggested by Derek in the comments.
It worked immediately, so the "answer" is that there was something wrong with the webpack config, though I don't know what it was.

npm install from the parent directory

I have a directory structure like this: /a/b/c
Directory c contains package.json and should contain node_modules.
How can I execute npm install from inside directory a?
I tried this way: npm --prefix b/c install b/c but this way, all the symlinks are created directly inside c instead of the default node_modules/.bin.
Is there any way to achieve that?
node: 6.2.2
npm: 3.10.2
Using an npm pre install hook in a package.json within your a directory is likely the best choice in this situation.
scripts: {
preinstall: `cd b/c && npm install`
}
This way running npm install in directory a will also do the c directory install and provide a seamless dev experience.
A bit overkill but may be useful...
With the help of recursion you can find node_modules.
you could run this file in a parent directory to find node_modules in child directory and pass npm arguments.
Note: tested on Windows
var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var safe = 0;
let args = process.argv.splice(2).toString().replace(/,/g ,' ');
function recurse(_path){
safe ++;
if(safe > 5000){
console.log('directory may be too large')
return
}
if(/node_modules$/.test(_path)){
let cwd = path.resolve(__dirname ,_path)
console.log('found node_modules at '+cwd)
child_process.exec(`start cmd.exe /k npm ${args}`,{cwd})
return
}
let directoryList = fs.readdirSync(_path);
directoryList.forEach(function(nextDir){
if(fs.statSync(_path+'/'+nextDir).isFile()){
return
}
if(/^\./.test(nextDir)){ //.folder beginging with .
return
}
recurse(_path+'/'+nextDir);
})
}
recurse('./' )

What is the behavior of -f (force?) argument in npm cache clean?

I've searched the web, but I couldn't get answer to my question. How is:
npm cache clean -f
Different from:
npm cache clean
I suspect -f is shorthand for --force, but even in official docs I wasn't able to find why one would use -f, what are upsides of it and what are downsides. I'm sure there's a warning when using -f, but I don't know why.
Take a look at npm cache clean source code:
// npm cache clean [<path>]
function clean (args, cb) {
assert(typeof cb === 'function', 'must include callback')
if (!args) args = []
var f = path.join(npm.cache, normalize(args))
if (f === npm.cache) {
fs.readdir(npm.cache, function (er, files) {
if (er) return cb()
asyncMap(
files.filter(function (f) {
return npm.config.get('force') || f !== '-'
}).map(function (f) {
return path.join(npm.cache, f)
}),
rm,
cb
)
})
} else {
rm(f, cb)
}
}
Link to the source code: https://github.com/npm/npm/blob/master/lib/cache.js#L199
It looks like by default cache clean command browse through default npm cache directory and removes all files except file named -. This file is skipped by default, and is deleted only in force option.
The question is what is the use of dash as a filename? I have found the possible answer in this question. According to information provided in accepted answer, some programs use - file as standard stdout.
To sum up. Standard npm cache clean removes all cache files except - file which is used sometimes to store stdout content. However npm cache clean -f or npm cache clean --force removes all files.

How to use npm.commands.version to bump the package.json version programatically

I'd like the package.json version of my project to bump up every time I run my project, for that I installed the npm package into my project as a local node-module but the following does not work:
var npm = require('npm');
npm.load(function (e, n) {
n.commands.version("patch", function (error) {
// 'error' here equals an error message as shown below
console.log(error);
});
});
Error message
"npm version [ | major | minor | patch | prerelease | preminor | premajor ](run in package dir)'npm -v' or 'npm --version' to print npm version (2.4.1)'npm view version' to view a package's published version'npm ls' to inspect current package/dependency versions"
Any idea what's missing and how to make it work?
Thanks!
Btw, I don't use grunt and not planning to at this point, so grunt-bump is not an option.
Well when I read the docs, I see "Like all other commands, this function takes a string array as its first parameter", which means you need ["patch"] where you have "patch":
var npm = require('npm');
npm.load(function (e, n) {
n.commands.version(["patch"], function (error) {
// 'error' here equals an error message as shown below
console.log(error);
});
});
That works fine for me.

Prevent NodeJS npm from printing details when retrieving package

Using the node npm module (https://www.npmjs.org/api/npm.html), I want to retrieve a package but NOT have it print the results to console. Setting loglevel silent does not seem to help.
var npm = require('npm');
npm.load(function (er, npm) {
// use the npm object, now that it's loaded.
npm.config.set('loglevel', 'silent');
npm.commands.view(["<package>#<version>"],function(err, npmBody) {
//at this point it is always logging npmBody contents to console!
});
});
This can't be done without hijacking stdout. See: https://github.com/npm/npm/blob/master/lib/install.js#L100
This was fixed once, but then reverted. It appears that npm is not designed to be use programmatically.
By "hijacking stdout" I mean something like this:
console.log('Begin');
console._stdout = { write : function () {} };
externalFn();
console._stdout = process.stdout;
console.log('End');
function externalFn () {
console.log('Annoying stuff');
}

Resources