diff --git a/doc/misc/npm-config.md b/doc/misc/npm-config.md index e54e2bb7..a70de812 100644 --- a/doc/misc/npm-config.md +++ b/doc/misc/npm-config.md @@ -648,6 +648,19 @@ devDependencies hash. Only works if there is already a package.json file present. +### save-prefix + +* Default: '^' +* Type: String + +Configure how versions of packages installed to a package.json file via +`--save` or `--save-dev` get prefixed. + +For example if a package has version `1.2.3`, by default it's version is +set to `^1.2.3` which allows minor upgrades for that package, but after +`npm config set save-prefix='~'` it would be set to `~1.2.3` which only allows +patch upgrades. + ### searchopts * Default: "" diff --git a/lib/install.js b/lib/install.js index 39c98ecf..8bc00934 100644 --- a/lib/install.js +++ b/lib/install.js @@ -337,6 +337,7 @@ function save (where, installed, tree, pretty, hasArguments, cb) { } var saveBundle = npm.config.get('save-bundle') + var savePrefix = npm.config.get('save-prefix') || "^"; // each item in the tree is a top-level thing that should be saved // to the package.json file. @@ -353,7 +354,7 @@ function save (where, installed, tree, pretty, hasArguments, cb) { var rangeDescriptor = semver.valid(k[1], true) && semver.gte(k[1], "0.1.0", true) && !npm.config.get("save-exact") - ? "^" : "" + ? savePrefix : "" set[k[0]] = rangeDescriptor + k[1] return set }, {}) diff --git a/test/tap/install-save-prefix.js b/test/tap/install-save-prefix.js new file mode 100644 index 00000000..0ce6e02f --- /dev/null +++ b/test/tap/install-save-prefix.js @@ -0,0 +1,140 @@ +var common = require('../common-tap.js') +var test = require('tap').test +var npm = require('../../') +var osenv = require('osenv') +var path = require('path') +var fs = require('fs') +var rimraf = require('rimraf') +var mkdirp = require('mkdirp') +var pkg = path.join(__dirname, 'install-save-prefix') +var mr = require("npm-registry-mock") + +test("setup", function (t) { + mkdirp.sync(pkg) + mkdirp.sync(path.resolve(pkg, 'node_modules')) + process.chdir(pkg) + t.end() +}) + +test('"npm install --save with default save-prefix should install local pkg versioned to allow minor updates', function(t) { + resetPackageJSON(pkg) + mr(common.port, function (s) { + npm.load({ + cache: pkg + "/cache", + loglevel: 'silent', + registry: common.registry }, function(err) { + t.ifError(err) + npm.config.set('save', true) + npm.commands.install(['underscore@1.3.1'], function(err) { + t.ifError(err) + var p = path.resolve(pkg, 'node_modules/underscore/package.json') + t.ok(JSON.parse(fs.readFileSync(p))) + var pkgJson = JSON.parse(fs.readFileSync(pkg + '/package.json', 'utf8')) + t.deepEqual(pkgJson.dependencies, { + 'underscore': '^1.3.1' + }, 'Underscore dependency should specify ^1.3.1') + npm.config.set('save', undefined) + s.close() + t.end() + }) + }) + }) +}) + +test('"npm install --save-dev with default save-prefix should install local pkg to dev dependencies versioned to allow minor updates', function(t) { + resetPackageJSON(pkg) + mr(common.port, function (s) { + npm.load({ + cache: pkg + "/cache", + loglevel: 'silent', + registry: common.registry }, function(err) { + t.ifError(err) + npm.config.set('save-dev', true) + npm.commands.install(['underscore@1.3.1'], function(err) { + t.ifError(err) + var p = path.resolve(pkg, 'node_modules/underscore/package.json') + t.ok(JSON.parse(fs.readFileSync(p))) + var pkgJson = JSON.parse(fs.readFileSync(pkg + '/package.json', 'utf8')) + t.deepEqual(pkgJson.devDependencies, { + 'underscore': '^1.3.1' + }, 'Underscore devDependency should specify ^1.3.1') + npm.config.set('save-dev', undefined) + s.close() + t.end() + }) + }) + }) +}) + +test('"npm install --save with "~" save-prefix should install local pkg versioned to allow patch updates', function(t) { + resetPackageJSON(pkg) + mr(common.port, function (s) { + npm.load({ + cache: pkg + "/cache", + loglevel: 'silent', + registry: common.registry }, function(err) { + t.ifError(err) + npm.config.set('save', true) + npm.config.set('save-prefix', '~') + npm.commands.install(['underscore@1.3.1'], function(err) { + t.ifError(err) + var p = path.resolve(pkg, 'node_modules/underscore/package.json') + t.ok(JSON.parse(fs.readFileSync(p))) + var pkgJson = JSON.parse(fs.readFileSync(pkg + '/package.json', 'utf8')) + t.deepEqual(pkgJson.dependencies, { + 'underscore': '~1.3.1' + }, 'Underscore dependency should specify ~1.3.1') + npm.config.set('save', undefined) + npm.config.set('save-prefix', undefined) + s.close() + t.end() + }) + }) + }) +}) + +test('"npm install --save-dev with "~" save-prefix should install local pkg to dev dependencies versioned to allow patch updates', function(t) { + resetPackageJSON(pkg) + mr(common.port, function (s) { + npm.load({ + cache: pkg + "/cache", + loglevel: 'silent', + registry: common.registry }, function(err) { + t.ifError(err) + npm.config.set('save-dev', true) + npm.config.set('save-prefix', '~') + npm.commands.install(['underscore@1.3.1'], function(err) { + t.ifError(err) + var p = path.resolve(pkg, 'node_modules/underscore/package.json') + t.ok(JSON.parse(fs.readFileSync(p))) + var pkgJson = JSON.parse(fs.readFileSync(pkg + '/package.json', 'utf8')) + t.deepEqual(pkgJson.devDependencies, { + 'underscore': '~1.3.1' + }, 'Underscore devDependency should specify ~1.3.1') + npm.config.set('save-dev', undefined) + npm.config.set('save-prefix', undefined) + s.close() + t.end() + }) + }) + }) +}) + +test('cleanup', function(t) { + process.chdir(__dirname) + rimraf.sync(path.resolve(pkg, 'node_modules')) + rimraf.sync(path.resolve(pkg, 'cache')) + resetPackageJSON(pkg) + t.end() +}) + +function resetPackageJSON(pkg) { + var pkgJson = JSON.parse(fs.readFileSync(pkg + '/package.json', 'utf8')) + delete pkgJson.dependencies + delete pkgJson.devDependencies + delete pkgJson.optionalDependencies + var json = JSON.stringify(pkgJson, null, 2) + "\n" + fs.writeFileSync(pkg + '/package.json', json, "ascii") +} + + diff --git a/test/tap/install-save-prefix/README.md b/test/tap/install-save-prefix/README.md new file mode 100644 index 00000000..aca67ff1 --- /dev/null +++ b/test/tap/install-save-prefix/README.md @@ -0,0 +1 @@ +# just a test diff --git a/test/tap/install-save-prefix/index.js b/test/tap/install-save-prefix/index.js new file mode 100644 index 00000000..33c1891f --- /dev/null +++ b/test/tap/install-save-prefix/index.js @@ -0,0 +1 @@ +module.exports = true diff --git a/test/tap/install-save-prefix/package.json b/test/tap/install-save-prefix/package.json new file mode 100644 index 00000000..84789fc2 --- /dev/null +++ b/test/tap/install-save-prefix/package.json @@ -0,0 +1,7 @@ +{ + "name": "bla", + "description": "fixture", + "version": "0.0.1", + "main": "index.js", + "repository": "git://github.com/robertkowalski/bogusfixture" +}