├── .npmignore ├── .gitignore ├── index.js ├── .editorconfig ├── README.md ├── package.json ├── LICENSE ├── install-npm.js ├── install-yarn.js └── install.js /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.log 3 | 4 | node_modules 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | 3 | .DS_Store 4 | 5 | node_modules 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'This is not the module you are looking for'; 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [**.{css,hbs,js,json,md,scss}] 12 | indent_style = space 13 | indent_size = 2 14 | insert_final_newline = true 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # install-peers 2 | 3 | Automatically installs project's peerDependencies (as devDependencies). Works with `npm`, `yarn` and `nvm`. 4 | 5 | ## Install 6 | 7 | ### npm 8 | 9 | ``` 10 | $ npm install --save-dev --ignore-scripts install-peers 11 | ``` 12 | 13 | ### yarn 14 | 15 | ``` 16 | $ yarn add --dev --ignore-scripts install-peers 17 | ``` 18 | 19 | ## Usage 20 | 21 | Run `npm install` (or `yarn install`) to install `prod` and `dev`, as well as `peer` dependencies. 22 | 23 | _You still may see "unmet peer dependency" warnings, due to installation flow of npm/yarn._ 24 | 25 | Also it won't update lock (shrinkwrap) files or modify package.json, keeping your setup pure and clean. 26 | 27 | ## License 28 | 29 | Install-Peers is released under the MIT license. 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "install-peers", 3 | "version": "1.0.4", 4 | "description": "Automatically installs project's peerDependencies (as devDependencies)", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "postinstall": "node install.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/alexindigo/install-peers.git" 13 | }, 14 | "keywords": [ 15 | "peerdependencies", 16 | "peers", 17 | "peerdeps", 18 | "dev", 19 | "devdeps", 20 | "devdependencies", 21 | "install", 22 | "automatically", 23 | "npm", 24 | "yarn", 25 | "nvm", 26 | "local" 27 | ], 28 | "author": "Alex Indigo ", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/alexindigo/install-peers/issues" 32 | }, 33 | "homepage": "https://github.com/alexindigo/install-peers#readme", 34 | "dependencies": { 35 | "executioner": "^2.0.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Alex Indigo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /install-npm.js: -------------------------------------------------------------------------------- 1 | // do it inline in sync way 2 | // to make it work in non-npm environment 3 | var npmBin 4 | , executioner 5 | , path = require('path') 6 | , node = process.argv[0] 7 | ; 8 | 9 | if (process.env['npm_execpath']) { 10 | var execPath = process.env['npm_execpath']; 11 | var expectedPath = path.join('bin', 'npm-cli.js'); 12 | 13 | if (execPath.slice(-1 * expectedPath.length) === expectedPath) { 14 | npmBin = path.resolve(execPath); 15 | } 16 | } 17 | 18 | // if no npm module found, don't expose any function 19 | // to allow upstream modules find alternatives 20 | module.exports = null; 21 | 22 | if (npmBin) { 23 | executioner = require('executioner'); 24 | 25 | module.exports = function(packages, config, done) { 26 | var options = { 27 | node : node, 28 | npm : npmBin, 29 | // escape package name@versions 30 | packages: packages.map((pkg) => '"' + pkg + '"').join(' ') 31 | }; 32 | 33 | executioner('"${node}" "${npm}" install --no-save --no-package-lock ${packages}', options, function (error, result) { 34 | if (error) { 35 | console.error('Unable to install peerDependencies', error); 36 | process.exit(1); 37 | return; 38 | } 39 | done(result); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /install-yarn.js: -------------------------------------------------------------------------------- 1 | // do it inline in sync way 2 | // to make it work in non-npm environment 3 | var yarnBin 4 | , executioner 5 | , path = require('path') 6 | , node = process.argv[0] 7 | ; 8 | 9 | if (process.env['npm_execpath'] && process.env['npm_execpath'].match(/[\/\\]yarn(-v\d+\.\d+\.\d+)?[\/\\]bin[\/\\]yarn\.js$/)) { 10 | var execPath = process.env['npm_execpath']; 11 | var expectedPath = path.join('bin', 'yarn.js'); 12 | if (execPath.slice(-1 * expectedPath.length) === expectedPath) { 13 | yarnBin = path.resolve(execPath, '..', '..', 'lib', 'cli'); 14 | } 15 | } 16 | 17 | // if no yarn module found, don't expose any function 18 | // to allow upstream modules find alternatives 19 | module.exports = null; 20 | 21 | if (yarnBin) { 22 | executioner = require('executioner'); 23 | 24 | module.exports = function(packages, extra, done) { 25 | var options = { 26 | node : node, 27 | yarn : yarnBin, 28 | // escape package names@versions 29 | packages: packages.map((pkg) => '"' + pkg + '"').join(' ') 30 | }; 31 | 32 | executioner('"${node}" "${yarn}" add --peer --no-lockfile ${packages}', options, function(error, result) { 33 | if (error) { 34 | console.error('Unable to install peerDependencies', error); 35 | process.exit(1); 36 | return; 37 | } 38 | done(result); 39 | }); 40 | 41 | // Looks like yarn shows last line from the output of sub-scripts 42 | console.log('Installing peerDependencies as devDependencies...'); 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /install.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | , path = require('path') 3 | , installNpm = require('./install-npm.js') 4 | , installYarn = require('./install-yarn.js') 5 | 6 | , rootPath = process.env.INIT_CWD || path.resolve(process.cwd(), '..', '..') 7 | 8 | , envLabel = 'skip_install_peers_as_dev' 9 | 10 | , defaultPeerInstallOptions = { 11 | 'save': false, 12 | 'save-bundle': false, 13 | 'save-dev': false, 14 | 'save-optional': false, 15 | 'save-prod': false 16 | } 17 | ; 18 | 19 | // in npm@3+ preinstall happens in `node_modules/.staging` folder 20 | // so if we ended up in `node_modules/` jump one level up 21 | if (path.basename(rootPath) === 'node_modules') { 22 | rootPath = path.resolve(rootPath, '..'); 23 | } 24 | 25 | installPeerDeps(); 26 | 27 | // --- Subroutines 28 | 29 | function installPeerDeps() { 30 | 31 | // check for the "kill switch" 32 | if (process.env[envLabel]) { 33 | console.log('Skipping installing peerDependencies as devDependencies.'); 34 | return; 35 | } 36 | 37 | // yo, do not install peers while installing peers 38 | process.env[envLabel] = '1'; 39 | 40 | getPackageConfig(rootPath, function(config) { 41 | var peerDeps = getPeerDeps(config) 42 | , peerInstallOptions = getPeerInstallOptions(config) 43 | ; 44 | 45 | if (!peerDeps) { 46 | console.error('Unable to find peerDependencies in ' + rootPath); 47 | return; 48 | } 49 | 50 | // ready to install, switch directories 51 | process.chdir(rootPath); 52 | 53 | // TODO: Add more alternatives 54 | // TODO: Handle `peerInstallOptions` for npm and yarn 55 | if (installYarn) { 56 | installYarn(peerDeps, peerInstallOptions, installDone.bind(null, 'yarn')); 57 | } else if (installNpm) { 58 | installNpm(peerDeps, peerInstallOptions, installDone.bind(null, 'npm')); 59 | } else { 60 | console.error('Did not find a viable package manager to install dependencies with.'); 61 | } 62 | }); 63 | } 64 | 65 | function installDone(tool, result) { 66 | // cleanup env 67 | process.env[envLabel] = ''; 68 | 69 | console.log('Installed peerDependencies as devDependencies via ' + tool + '.'); 70 | 71 | console.log(result); 72 | } 73 | 74 | function getPeerDeps(config) { 75 | var peerDeps; 76 | 77 | if (typeof config.peerDependencies === 'object' && !Array.isArray(config.peerDependencies)) { 78 | peerDeps = Object.keys(config.peerDependencies).map(function(name) { 79 | return name + '@' + config.peerDependencies[name]; 80 | }); 81 | } 82 | 83 | return peerDeps; 84 | } 85 | 86 | function getPeerInstallOptions(config) { 87 | var peerInstallOptions = defaultPeerInstallOptions; 88 | 89 | if (typeof config.peerInstallOptions === 'object' && !Array.isArray(config.peerInstallOptions)) { 90 | Object.keys(config.peerInstallOptions).forEach(function(key) { 91 | peerInstallOptions[key] = config.peerInstallOptions[key]; 92 | }); 93 | } 94 | 95 | return peerInstallOptions; 96 | } 97 | 98 | function getPackageConfig(packagePath, callback) { 99 | var packageFile = path.join(packagePath, 'package.json') 100 | , config 101 | ; 102 | 103 | fs.readFile(packageFile, 'utf-8', function(error, content) { 104 | if (error || !content) { 105 | console.error('Unable to read ' + packageFile + ':', error || 'no content'); 106 | return; 107 | } 108 | 109 | config = parseConfig(content); 110 | 111 | if (config.isParseConfigFailed) { 112 | console.error('Unable to parse ' + packageFile + ':', config.error); 113 | return; 114 | } 115 | 116 | callback(config); 117 | }); 118 | } 119 | 120 | function parseConfig(config) { 121 | try { 122 | config = JSON.parse(config); 123 | } catch (error) { 124 | config = {isParseConfigFailed: true, error: error}; 125 | } 126 | 127 | return config; 128 | } 129 | --------------------------------------------------------------------------------