├── .npmignore ├── .travis.yml ├── img ├── link-attack.jpg └── link-zelda.png ├── package.json ├── LICENSE ├── bin └── cmd.js ├── README.md └── index.js /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | img/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | -------------------------------------------------------------------------------- /img/link-attack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feross/zelda/HEAD/img/link-attack.jpg -------------------------------------------------------------------------------- /img/link-zelda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feross/zelda/HEAD/img/link-zelda.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zelda", 3 | "description": "Automatically `npm link` all your packages together!", 4 | "version": "3.4.2", 5 | "author": { 6 | "name": "Feross Aboukhadijeh", 7 | "email": "feross@feross.org", 8 | "url": "http://feross.org/" 9 | }, 10 | "bin": "./bin/cmd.js", 11 | "bugs": { 12 | "url": "https://github.com/feross/zelda/issues" 13 | }, 14 | "dependencies": { 15 | "find-root": "~1.1.0", 16 | "minimist": "^1.1.0", 17 | "rimraf": "^3.0.0", 18 | "run-parallel": "^1.1.4", 19 | "run-series": "^1.1.4", 20 | "uniq": "^1.0.1" 21 | }, 22 | "devDependencies": { 23 | "standard": "*" 24 | }, 25 | "homepage": "https://github.com/feross/zelda/", 26 | "keywords": [ 27 | "all", 28 | "automatic", 29 | "mad science", 30 | "npm link", 31 | "npm link everything", 32 | "recursive", 33 | "zelda" 34 | ], 35 | "license": "MIT", 36 | "main": "index.js", 37 | "repository": { 38 | "type": "git", 39 | "url": "git://github.com/feross/zelda.git" 40 | }, 41 | "scripts": { 42 | "test": "standard" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Feross Aboukhadijeh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /bin/cmd.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var minimist = require('minimist') 4 | var zelda = require('../') 5 | 6 | var argv = minimist(process.argv.slice(2), { 7 | alias: { 8 | i: 'install', 9 | p: 'production', 10 | h: 'help', 11 | v: 'version' 12 | }, 13 | boolean: [ 14 | 'install', 15 | 'production', 16 | 'help', 17 | 'version' 18 | ], 19 | default: { 20 | install: true 21 | } 22 | }) 23 | 24 | if (argv.version) { 25 | console.log(require('../package.json').version) 26 | process.exit(0) 27 | } 28 | 29 | if (argv.help) { 30 | usage() 31 | process.exit(0) 32 | } 33 | 34 | zelda(process.cwd(), argv) 35 | 36 | function usage () { 37 | console.log('Usage: zelda CODE_DIR [OPTIONS]') 38 | console.log('') 39 | console.log('CODE-DIR - the folder where all your packages live') 40 | console.log('') 41 | console.log('OPTIONS:') 42 | console.log(' --dry-run see what would happen, without actually making changes') 43 | console.log(' --no-install skip `npm install` on each package') 44 | console.log(' --production only `npm install` production dependencies') 45 | console.log(' -h, --help show help message') 46 | console.log(' -v, --version show version') 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zelda [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] 2 | 3 | [travis-image]: https://img.shields.io/travis/feross/zelda/master.svg 4 | [travis-url]: https://travis-ci.org/feross/zelda 5 | [npm-image]: https://img.shields.io/npm/v/zelda.svg 6 | [npm-url]: https://npmjs.org/package/zelda 7 | [downloads-image]: https://img.shields.io/npm/dm/zelda.svg 8 | [downloads-url]: https://npmjs.org/package/zelda 9 | [standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg 10 | [standard-url]: https://standardjs.com 11 | 12 | #### Automatically `npm link` all your packages together! 13 | 14 | ![link spin attack](https://raw.githubusercontent.com/feross/zelda/master/img/link-attack.jpg) 15 | 16 | Sometimes Link needs a little help from Zelda. 17 | 18 | ### usage 19 | 20 | 1. Install it globally. 21 | 22 | ```bash 23 | npm install -g zelda 24 | ``` 25 | 26 | 2. Run `zelda` from your node project directory. For example: 27 | 28 | ```bash 29 | cd ~/code/my-project 30 | zelda 31 | ``` 32 | 33 | `zelda` finds all the node packages in your code folder (`~/code/` in the example). 34 | If any of these packages are listed as a dependency in the nearest `package.json` 35 | of your working directory, it automatically symlinks it for you. 36 | 37 | Zelda assumes that all your code lives in the directory one level up from the 38 | folder where you run `zelda`. So, keep all your packages in a single folder like 39 | `~/code` and run `zelda` inside one of the projects (ex: `~/code/my-project`). 40 | 41 | ### what you might do if you're clever 42 | 43 | 1. Clone a cool project. 44 | 45 | ```bash 46 | mkdir ~/code 47 | cd ~/code 48 | git clone git@github.com:feross/webtorrent.git 49 | ``` 50 | 51 | 2. Clone the project dependencies you plan to work on. 52 | 53 | ```bash 54 | git clone git@github.com:feross/bittorrent-protocol.git 55 | git clone git@github.com:feross/bittorrent-swarm.git 56 | git clone git@github.com:feross/bittorrent-dht.git 57 | ``` 58 | 59 | 3. Recursively `npm install` all project dependencies, but `npm link` the ones that are local. 60 | 61 | ```bash 62 | cd webtorrent 63 | zelda 64 | ``` 65 | 66 | Gone are the days of running tons of `npm link` commands by hand! 67 | 68 | ### features 69 | 70 | - Automatically `npm link` all your modules together 71 | - Supports `dependencies`, `devDependencies`, and `optionalDependencies` 72 | - Recursively runs `npm install` so your freshly cloned projects are ready to go! 73 | 74 | ### link is better with zelda! 75 | 76 | ![link](https://raw.githubusercontent.com/feross/zelda/master/img/link-zelda.png) 77 | 78 | ### license 79 | 80 | MIT. Copyright [Feross Aboukhadijeh](https://www.twitter.com/feross). 81 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = zelda 2 | 3 | var cp = require('child_process') 4 | var findRoot = require('find-root') 5 | var fs = require('fs') 6 | var path = require('path') 7 | var rimraf = require('rimraf') 8 | 9 | var NPM_EXEC = process.platform === 'win32' 10 | ? 'npm.cmd' 11 | : 'npm' 12 | 13 | function zelda (rootPath, opts) { 14 | if (!opts) opts = {} 15 | 16 | // Use folder with nearest package.json as root 17 | rootPath = findRoot(rootPath) 18 | 19 | var rootName = require(path.join(rootPath, 'package.json')).name 20 | var codePath = path.resolve(rootPath, '..') 21 | 22 | if (!rootName) throw new Error('root package must have a name ') 23 | 24 | // add node_modules symlink in code folder - magic! 25 | try { 26 | console.log('[zelda] cd ' + codePath + ' && ln -s . node_modules') 27 | if (!opts['dry-run']) fs.symlinkSync('.', path.join(codePath, 'node_modules'), 'dir') 28 | } catch (err) { 29 | // ignore err (symlink already exists) 30 | } 31 | 32 | // get packages in code folder 33 | var codePkgs = getCodePkgs(codePath) 34 | 35 | if (opts.install) npmInstall(rootPath) 36 | 37 | var pkgsToPurge = {} 38 | pkgsToPurge[rootName] = true 39 | 40 | traverseNodeModules(rootPath, function (pkgName, pkgPath) { 41 | if (codePkgs[pkgName]) { 42 | pkgsToPurge[pkgName] = true 43 | if (opts.install) npmInstall(path.join(codePath, pkgName)) 44 | } 45 | }) 46 | 47 | traverseNodeModules(rootPath, function (pkgName, pkgPath) { 48 | if (pkgsToPurge[pkgName]) { 49 | rmDir(pkgPath) 50 | } 51 | }) 52 | 53 | Object.keys(pkgsToPurge).forEach(function (pkgToPurge) { 54 | if (pkgToPurge === rootName) return 55 | 56 | var pkgPath = path.join(codePath, pkgToPurge) 57 | traverseNodeModules(pkgPath, function (pkgName, pkgPath) { 58 | if (pkgsToPurge[pkgName]) rmDir(pkgPath) 59 | }) 60 | }) 61 | 62 | function rmDir (dirPath) { 63 | console.log('[zelda] rm -rf ' + dirPath) 64 | if (!opts['dry-run']) rimraf.sync(dirPath) 65 | } 66 | 67 | function npmInstall (pkgPath) { 68 | console.log('[zelda] cd ' + pkgPath + ' && rm node_modules/ && npm install') 69 | 70 | var args = ['install'] 71 | if (opts.production) args.push('--production') 72 | 73 | if (!opts['dry-run']) { 74 | rimraf.sync(path.join(pkgPath, 'node_modules')) 75 | cp.spawnSync(NPM_EXEC, args, { 76 | cwd: pkgPath, 77 | stdio: 'inherit' 78 | }) 79 | } 80 | } 81 | } 82 | 83 | function getCodePkgs (codePath) { 84 | var entries 85 | try { 86 | entries = fs.readdirSync(codePath) 87 | } catch (err) { 88 | throw new Error('Could not find ' + codePath + '. ' + err.message) 89 | } 90 | 91 | var pkgs = {} 92 | 93 | entries.forEach(function (entry) { 94 | var pkgPath = path.join(codePath, entry) 95 | var pkg 96 | try { 97 | pkg = require(path.join(pkgPath, 'package.json')) 98 | } catch (err) { 99 | return // ignore folders without package.json 100 | } 101 | pkgs[pkg.name] = pkgPath 102 | }) 103 | 104 | return pkgs 105 | } 106 | 107 | function traverseNodeModules (pkgPath, fn) { 108 | var modulesPath = path.join(pkgPath, 'node_modules') 109 | var entries 110 | try { 111 | entries = fs.readdirSync(modulesPath) 112 | } catch (err) { 113 | return // nothing to traverse (no node_modules) 114 | } 115 | 116 | entries = entries.filter(function (entry) { 117 | var stat = fs.lstatSync(path.join(modulesPath, entry)) 118 | return !stat.isSymbolicLink() 119 | }) 120 | 121 | entries.forEach(function (entry) { 122 | var entryPath = path.join(modulesPath, entry) 123 | traverseNodeModules(entryPath, fn) 124 | fn(entry, entryPath) 125 | }) 126 | } 127 | --------------------------------------------------------------------------------