├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin.js ├── index.js ├── lib ├── cli.js └── symlink.js ├── manpage.md ├── package.json ├── symlink.1 └── test ├── cli.test.js ├── cyclicals ├── module6 │ └── package.json ├── module7 │ └── package.json └── module8 │ └── package.json ├── extradir1 └── module4 │ └── package.json ├── extradir2 └── module5 │ └── package.json ├── module1 └── package.json ├── module2 └── package.json ├── module3 └── package.json ├── ok ├── dep │ └── package.json └── parent │ └── package.json ├── order.test.js └── throw.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib-cov 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | examples 3 | .gitignore 4 | .travis.yml 5 | .npmignore 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | 4 | node_js: 5 | - 0.10 6 | - 4 7 | - node 8 | 9 | notifications: 10 | email: false 11 | 12 | before_script: 13 | - npm link 14 | 15 | after_script: 16 | - npm install coveralls 17 | - npm run coverage | coveralls 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2.1.0 / 2015-11-16 2 | ================= 3 | * Add a man page 4 | * Add `.npmignore` 5 | 6 | 2.0.0 / 2015-03-28 7 | ================= 8 | * Dry run flag removed. Dry run is now the default. 9 | * Execute flag added `-e` or `--execute`. 10 | * Perform linking with either: 11 | 1. `symlink repoDirs | sh` 12 | 2. `symlink repoDirs --execute` (will log a little more) 13 | 14 | 1.1.0 / 2015-03-28 15 | ================= 16 | * Swap optimist for yargs and improve command line usage 17 | * Errors from child_process calls in CLI now result in non-zero return code 18 | 19 | 1.0.1 / 2015-03-28 20 | ================== 21 | * Errors from library use are now propagated correctly via callback 22 | 23 | 1.0.0 / 2015-03-26 24 | ================== 25 | * Allow multiple input directories to be listed before flags 26 | 27 | 0.3.0 / 2014-07-20 28 | ================== 29 | * `-r` flag is removed, now assumed the only unnamed argument 30 | * Rewrite so it can be used as a library: 31 | - package.json location is now fully asynchronous 32 | - library simply works out the commands like a -d dryrun 33 | * Unit test coverage 34 | 35 | 0.2.0 - 0.2.4 / 2013-10-07 36 | ================== 37 | * Add -g flag for global modules that can be linked in if present 38 | * Command output now perform multiple installs and links per line (shorter output) 39 | * Documentation improvements 40 | 41 | 0.1.1 / 2013-10-05 42 | ================== 43 | * Unit tests 44 | 45 | 0.1.0 / 2013-01-06 46 | ================== 47 | * Take a container directory rather than a bunch of sibling directories as input 48 | 49 | 0.0.3 / 2012-10-20 50 | ================== 51 | * documentation + code cleanup 52 | 53 | 0.0.1 / 2012-10-18 54 | ================== 55 | * initial release 56 | 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 Eirik Albrigtsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # symlink 2 | [![npm status](http://img.shields.io/npm/v/symlink.svg)](https://www.npmjs.org/package/symlink) 3 | [![build status](https://secure.travis-ci.org/clux/symlink.svg)](http://travis-ci.org/clux/symlink) 4 | [![dependency status](https://david-dm.org/clux/symlink.svg)](https://david-dm.org/clux/symlink) 5 | [![coverage status](http://img.shields.io/coveralls/clux/symlink.svg)](https://coveralls.io/r/clux/symlink) 6 | 7 | - Manage lots of node modules? 8 | - Do you `npm link` multiple modules together to ensure they all work with latest while developing? 9 | - Find the process of linking together multiple modules tedious? 10 | 11 | If you answered YES to all the above, then this module is for you. 12 | 13 | ## Usage 14 | Install, then give it a set of directories containing packages that you would like to link together (will parse all immediate subdirectories containing a package.json). 15 | 16 | ```sh 17 | npm install -g symlink 18 | symlink repoDir # prints a list of commands that CAN be executed 19 | ``` 20 | 21 | ## Execute 22 | To execute these commands in series run symlink by either piping to `sh` if pipes are convenient for your use case, or use the `--execute` flag which also gives you one log per command. 23 | 24 | ```sh 25 | symlink repoDir --execute 26 | ``` 27 | 28 | ## Example 29 | Linking together the related tournament modules from [clux](https://github.com/clux?tab=repositories)'s repositories, to ensure they all work together, and all get the same test frameworks (though that's mostly just convenience): 30 | 31 | NB: for readability the full paths have been shortened 32 | 33 | ```sh 34 | clux@kjttks ~/trn $ symlink . -g nodeunit -g jscoverage -g nodeunit 35 | cd tournament && npm link nodeunit jscoverage coveralls 36 | cd tournament && npm install interlude 37 | cd tournament && npm link 38 | cd duel && npm link nodeunit jscoverage coveralls tournament 39 | cd duel && npm install interlude 40 | cd duel && npm link 41 | cd ffa && npm link nodeunit jscoverage coveralls tournament 42 | cd ffa && npm install interlude group 43 | cd ffa && npm link 44 | cd groupstage && npm link nodeunit jscoverage coveralls tournament 45 | cd groupstage && npm install interlude roundrobin group 46 | cd groupstage && npm link 47 | cd masters && npm link nodeunit jscoverage coveralls tournament ffa 48 | cd masters && npm install interlude 49 | cd masters && npm link 50 | cd tiebreaker && npm link nodeunit jscoverage coveralls tournament groupstage ffa 51 | cd tiebreaker && npm install interlude 52 | cd tiebreaker && npm link 53 | cd tourney && npm link nodeunit jscoverage coveralls tournament 54 | cd tourney && npm install interlude 55 | cd tourney && npm link 56 | cd ffa-tb && npm link nodeunit jscoverage coveralls tourney tiebreaker ffa 57 | cd ffa-tb && npm install autonomy 58 | cd ffa-tb && npm link 59 | cd groupstage-tb && npm link nodeunit jscoverage coveralls tiebreaker groupstage tourney 60 | cd groupstage-tb && npm link 61 | cd groupstage-tb-duel && npm link nodeunit jscoverage duel groupstage-tb tourney groupstage 62 | cd groupstage-tb-duel && npm install autonomy 63 | cd groupstage-tb-duel && npm link 64 | 65 | # all looks sane - execute: 66 | $ kjttks@clux ~/repos $ !! | sh 67 | ``` 68 | 69 | The most independent modules (tournament) gets their missing dependencies installed first, then gets npm linked so the more requiring modules (specific implementations) can npm link in these. 70 | 71 | If you have a local/chowned install of node (such that creating links to globally installed modules can be done sans-sudo) then `symlink` can execute sudo free too. 72 | 73 | ## What it does 74 | 75 | - reads the `package.json` of each module founds in the given directory and collects their `dependencies` and `devDependencies` 76 | - figures out which deps are local (present on one of the repoDirs) 77 | - figures out which deps are external (complement) 78 | - orders the modules so that linking can be in a safe order without having to query npmjs.org more than necessary 79 | 80 | Once everything has been ordered, a bunch of commands are generated for each module from the order of least inclusion; 81 | 82 | - `npm link (localDeps) ∪ ((globals ∩ externalDeps))` 83 | - `npm install (externalDeps ∖ globals)` 84 | - `npm link` 85 | 86 | I.e. link in all locally available dependencies + extenal globals that were requested explicitly, install the rest, then link the module itself so the modules with more inclusions can safely link the module in. 87 | 88 | ## Globally linked modules 89 | Test dependencies are often the same everywhere, and, to save querying npmjs, you could just give them the version you have installed (provided it is compatible, and installed globally): 90 | 91 | In the example above, every module that uses `jscoverage`, `nodeunit` or `coveralls` will get the relevant modules linked in. 92 | 93 | ## License 94 | MIT-Licensed. See LICENSE file for details. 95 | -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var argv = require('yargs') 3 | .usage('Usage: symlink [options]') 4 | .demand(1) 5 | .example('symlink ./repos') 6 | .example('symlink ./repos -g coveralls -g nodeunit') 7 | .alias('e', 'execute') 8 | .describe('e', 'Execute the commands generated') 9 | .boolean('e') 10 | .alias('g', 'global') 11 | .describe('g', 'globally installed module to be linked') 12 | .array('g') // NB: will be unset if not used (default strings are ugly) 13 | .alias('h', 'help') 14 | .help('h') 15 | .argv; 16 | 17 | require('./').cli(argv, function (err) { 18 | if (err) { 19 | console.error(err.message); 20 | process.exit(1); 21 | } 22 | process.exit(0); 23 | }); 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = process.env.SYMLINK_COV ? 2 | require('./lib-cov/symlink.js'): 3 | require('./lib/symlink.js'); 4 | 5 | module.exports.cli = process.env.SYMLINK_COV ? 6 | require('./lib-cov/cli.js'): 7 | require('./lib/cli.js'); 8 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | var cp = require('child_process'); 2 | var path = require('path'); 3 | var async = require('async'); 4 | 5 | var defaultHandler = function (cmds, done) { 6 | cmds.forEach(function (cmd) { 7 | console.log(cmd); 8 | }); 9 | done(null); 10 | }; 11 | 12 | var executeHandler = function (cmds, done) { 13 | var iterator = function (cmd, cb) { 14 | console.log(cmd); 15 | cp.exec(cmd, function (err, stdout) { 16 | console.log(stdout); 17 | cb(err, stdout); 18 | }); 19 | }; 20 | async.mapSeries(cmds, iterator, done); 21 | }; 22 | 23 | module.exports = function (argv, done) { 24 | // repoDirs are the unnamed arguments (usually just one super dir) 25 | var dirs = argv._.map(function (dir) { 26 | return path.join(process.cwd(), dir); 27 | }); 28 | 29 | // execute main module function with one of the handlers as the cb 30 | var handler = argv.e ? executeHandler : defaultHandler; 31 | require('./symlink')(dirs, argv.g || [], function (err, cmds) { 32 | err ? done(err) : handler(cmds, done); 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /lib/symlink.js: -------------------------------------------------------------------------------- 1 | var join = require('path').join 2 | , $ = require('interlude') 3 | , fs = require('fs') 4 | , async = require('async'); 5 | 6 | var getJson = function (pth, cb) { 7 | var pkgjson = join(pth, 'package.json'); 8 | fs.exists(pkgjson, function (exists) { 9 | if (exists) { 10 | fs.readFile(pkgjson, function (err, data) { 11 | cb(err, err ? null : { path: pth, data: JSON.parse(data) }); 12 | }); 13 | } 14 | else { 15 | cb(null, null); // no error but no package.json 16 | } 17 | }); 18 | }; 19 | 20 | var getJsons = function (dir, cb) { 21 | fs.readdir(dir, function (err, data) { 22 | if (err) { 23 | return cb(err); 24 | } 25 | var paths = data.map(function (str) { 26 | return join(dir, str); 27 | }); 28 | async.map(paths, getJson, cb); 29 | }); 30 | }; 31 | 32 | var getJsonsFromDirectories = function (dirs, cb) { 33 | async.map(dirs, getJsons, cb); 34 | }; 35 | 36 | var analyze = function (deps, absPaths, globals, names) { 37 | var ownDeps = {} // deps in names 38 | , foreignDeps = {}; // deps not in names 39 | 40 | // partition dependencies 41 | Object.keys(deps).forEach(function (k) { 42 | ownDeps[k] = deps[k].filter($.elem(names)); 43 | foreignDeps[k] = deps[k].filter($.notElem(names)); 44 | }); 45 | 46 | // sort deps in order of safe linking between each other 47 | var sorted = $.range(names.length).map(function () { 48 | var safe = $.firstBy(function (n) { 49 | // safe to link iff no local deps unlinked 50 | return !$.intersect(ownDeps[n], names).length; 51 | }, names); 52 | 53 | if (!safe) { 54 | // impossible to link a to b if b also tries to link to a without querying npm 55 | var err = "cannot link cyclically dependent: " + JSON.stringify(names); 56 | throw new Error(err); 57 | } 58 | names.splice(names.indexOf(safe), 1); // remove it from names 59 | return safe; 60 | }); 61 | 62 | var cmds = []; 63 | sorted.forEach(function (n) { 64 | // then find all commands required for each module in the found safe order 65 | var cd = 'cd ' + absPaths[n] + ' && '; 66 | 67 | // npm link in -g requested modules and internal deps when they are specified 68 | var linked = $.intersect(globals, foreignDeps[n]).concat(ownDeps[n]); 69 | if (linked.length > 0) { 70 | cmds.push(cd + 'npm link ' + linked.join(' ')); 71 | } 72 | 73 | // npm install remaining deps 74 | var remaining = foreignDeps[n].filter($.notElem(linked)); 75 | if (remaining.length > 0) { 76 | cmds.push(cd + 'npm install ' + remaining.join(' ')); 77 | } 78 | 79 | // npm link (to make this available to the modules with more dependencies) 80 | cmds.push(cd + 'npm link'); 81 | }); 82 | return cmds; 83 | }; 84 | 85 | 86 | module.exports = function (dirs, globals, cb) { 87 | var deps = {} // { module name -> [jsonDeps++jsonDevDeps] } 88 | , absPaths = {}; // { module name -> abs module path } 89 | 90 | getJsonsFromDirectories(dirs, function (err, datas) { 91 | if (err) { 92 | return cb(err); 93 | } 94 | var names = datas.reduce(function (acc, data) { 95 | var namesCurr = data.filter(function (o) { 96 | return o !== null; // folders with package.json 97 | }).map(function (o) { 98 | var json = o.data; 99 | var name = json.name; 100 | var mDeps = $.extend(json.dependencies || {}, json.devDependencies || {}); 101 | deps[name] = Object.keys(mDeps); 102 | absPaths[name] = o.path; 103 | return name; 104 | }); 105 | return acc.concat(namesCurr); 106 | }, []); 107 | 108 | var cmds; 109 | try { 110 | cmds = analyze(deps, absPaths, globals, names); 111 | } 112 | catch (err) { 113 | return cb(err); 114 | } 115 | return cb(null, cmds); 116 | }); 117 | }; 118 | -------------------------------------------------------------------------------- /manpage.md: -------------------------------------------------------------------------------- 1 | # symlink(1) 2 | Symlink `node` repositories together as quickly as possible. 3 | 4 | ## SYNOPSIS 5 | 6 | `symlink [OPTION]... repoDir..` 7 | 8 | ## DESCRIPTION 9 | Links together all repositories using `npm link` after having fetched the smallest amount of dependencies using `npm install`. 10 | 11 | Performs dependency analysis to ensure the least dependent modules are made available via `npm link` first. 12 | 13 | ## OPTIONS 14 | 15 | `-g,--global` Globally installed module to be linked 16 | 17 | `-e,--execute` Execute commands - same as piping through `sh` 18 | 19 | ## EXAMPLES 20 | See how a set of repositories would be linked together 21 | 22 | `symlink repos` 23 | 24 | Actually run the set of commands 25 | 26 | `symlink repos | sh` 27 | 28 | Reuse large globally installed modules if you know what you are doing 29 | 30 | `symlink repos -g gulp` 31 | 32 | ## INSTALLATION 33 | Install globally through npm 34 | 35 | `npm install -g symlink` 36 | 37 | ## ALGORITHM 38 | There is an analysis step: 39 | 40 | - read `package.json` of each module in the given directory/directories and collects `dependencies` + `devDependencies` 41 | - finds the `local` ones (found in given directories) 42 | - finds the `external` ones (complement) 43 | - orders the modules by least inclusion 44 | 45 | Then the execution step for each module (by least inclusion): 46 | 47 | - `npm link (localDeps) ∪ ((globals ∩ externalDeps))` 48 | - `npm install (externalDeps ∖ globals)` 49 | - `npm link` 50 | 51 | This ensures your repository directories are all linked together. 52 | 53 | ## BUGS 54 | Please report bugs [at](https://github.com/clux/symlink/issues) 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symlink", 3 | "description": "npm link together local modules && npm install remaining dependencies", 4 | "author": "Eirik Albrigtsen ", 5 | "version": "2.1.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "clux/symlink" 9 | }, 10 | "keywords": [ 11 | "dependency", 12 | "development", 13 | "repository", 14 | "install" 15 | ], 16 | "scripts": { 17 | "test": "nodeunit --reporter=verbose test/*.js", 18 | "precoverage": "jscoverage lib", 19 | "coverage": "SYMLINK_COV=1 nodeunit --reporter=lcov test/*.js", 20 | "man": "marked-man manpage.md -o symlink.1" 21 | }, 22 | "man": "symlink.1", 23 | "bin": "bin.js", 24 | "main": "index.js", 25 | "dependencies": { 26 | "async": "^1.5.0", 27 | "interlude": "~1.1.0", 28 | "yargs": "^3.6.0" 29 | }, 30 | "devDependencies": { 31 | "jscoverage": "^0.5.5", 32 | "nodeunit": "^0.9.0" 33 | }, 34 | "bugs": { 35 | "url": "http://github.com/clux/symlink/issues" 36 | }, 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /symlink.1: -------------------------------------------------------------------------------- 1 | .TH "SYMLINK" "1" "November 2015" "" "" 2 | .SH "NAME" 3 | \fBsymlink\fR 4 | .P 5 | Symlink \fBnode\fP repositories together as quickly as possible\. 6 | .SH SYNOPSIS 7 | .P 8 | \fBsymlink [OPTION]\.\.\. repoDir\.\.\fP 9 | .SH DESCRIPTION 10 | .P 11 | Links together all repositories using \fBnpm link\fP after having fetched the smallest amount of dependencies using \fBnpm install\fP\|\. 12 | .P 13 | Performs dependency analysis to ensure the least dependent modules are made available via \fBnpm link\fP first\. 14 | .SH OPTIONS 15 | .P 16 | \fB\-g,\-\-global\fP Globally installed module to be linked 17 | .P 18 | \fB\-e,\-\-execute\fP Execute commands \- same as piping through \fBsh\fP 19 | .SH EXAMPLES 20 | .P 21 | See how a set of repositories would be linked together 22 | .P 23 | \fBsymlink repos\fP 24 | .P 25 | Actually run the set of commands 26 | .P 27 | \fBsymlink repos | sh\fP 28 | .P 29 | Reuse large globally installed modules if you know what you are doing 30 | .P 31 | \fBsymlink repos \-g gulp\fP 32 | .SH INSTALLATION 33 | .P 34 | Install globally through npm 35 | .P 36 | \fBnpm install \-g symlink\fP 37 | .SH ALGORITHM 38 | .P 39 | There is an analysis step: 40 | .RS 0 41 | .IP \(bu 2 42 | read \fBpackage\.json\fP of each module in the given directory/directories and collects \fBdependencies\fP + \fBdevDependencies\fP 43 | .IP \(bu 2 44 | finds the \fBlocal\fP ones (found in given directories) 45 | .IP \(bu 2 46 | finds the \fBexternal\fP ones (complement) 47 | .IP \(bu 2 48 | orders the modules by least inclusion 49 | 50 | .RE 51 | .P 52 | Then the execution step for each module (by least inclusion): 53 | .RS 0 54 | .IP \(bu 2 55 | \fBnpm link (localDeps) ∪ ((globals ∩ externalDeps))\fP 56 | .IP \(bu 2 57 | \fBnpm install (externalDeps ∖ globals)\fP 58 | .IP \(bu 2 59 | \fBnpm link\fP 60 | 61 | .RE 62 | .P 63 | This ensures your repository directories are all linked together\. 64 | .SH BUGS 65 | .P 66 | Please report bugs at \fIhttps://github\.com/clux/symlink/issues\fR 67 | -------------------------------------------------------------------------------- /test/cli.test.js: -------------------------------------------------------------------------------- 1 | var cli = require('../').cli; 2 | var fs = require('fs'); 3 | 4 | exports.enoent = function (t) { 5 | var argv = { 6 | _ : [ './test/missing_dir' ] 7 | }; 8 | 9 | cli(argv, function (err) { 10 | t.ok(/ENOENT/.test(err), "ENOENT exception from cli"); 11 | t.done(); 12 | }); 13 | }; 14 | 15 | exports.ecyclical = function (t) { 16 | var argv = { 17 | _ : [ './test/cyclicals' ] 18 | }; 19 | cli(argv, function (err) { 20 | t.ok(/cannot link cyclically dep/.test(err), "cyclical deps"); 21 | t.done(); 22 | }); 23 | }; 24 | 25 | // success cases (thes log a bit) 26 | exports.dryRun = function (t) { 27 | var argv = { 28 | _ : [ './test/ok' ], 29 | }; 30 | 31 | cli(argv, function (err) { 32 | t.ok(!err, "no error on dry run"); 33 | t.done(); 34 | }); 35 | }; 36 | 37 | exports.executePass = function (t) { 38 | // NB: it's a bit lucky that this works on travis 39 | // thankfully they have a local prefix under /home/travis/.nvm/v0.X.X/.. 40 | var argv = { 41 | _ : [ './test/ok' ], 42 | e: true 43 | }; 44 | 45 | cli(argv, function (clierr) { 46 | t.ok(!clierr, "execute did not throw"); 47 | // verify that linking actually occurred 48 | fs.lstat('./test/ok/parent/node_modules/dep', function (err, stat) { 49 | t.ok(!err, "could get stat of link"); 50 | t.ok(stat.isSymbolicLink(), "dep linked from parent"); 51 | t.done(); 52 | }); 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /test/cyclicals/module6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module6", 3 | "dependencies": { 4 | "module7": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/cyclicals/module7/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module7", 3 | "dependencies": { 4 | "module8": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/cyclicals/module8/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module8", 3 | "dependencies": { 4 | "module6": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/extradir1/module4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module4", 3 | "dependencies": { 4 | "module5": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/extradir2/module5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module5", 3 | "dependencies": { 4 | "module2": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/module1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module1", 3 | "dependencies": { 4 | "module2": "latest" 5 | }, 6 | "devDependencies": { 7 | "module3": "latest", 8 | "external2": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/module2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module2", 3 | "dependencies": { 4 | "external1" : "latest", 5 | "external2" : "latest" 6 | }, 7 | "devDependencies": { 8 | "global1" : "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/module3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module3", 3 | "dependencies": { 4 | "module2": "latest", 5 | "global2" : "latest" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/ok/dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dep", 3 | "dependencies": {} 4 | } 5 | -------------------------------------------------------------------------------- /test/ok/parent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parent", 3 | "dependencies": { 4 | "dep": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/order.test.js: -------------------------------------------------------------------------------- 1 | var symlink = require('../'); 2 | var join = require('path').join; 3 | 4 | var verify = function (t, globals, output, dirs) { 5 | symlink(dirs, globals, function (err, cmds) { 6 | if (err) { 7 | t.equal(err, null); 8 | } 9 | else { 10 | t.equal(cmds.length, output.length, "same number of cmds"); 11 | var expected = cmds.map(function (l) { 12 | return l.match(/\/symlink\/(.*)/)[1]; 13 | }); 14 | t.deepEqual(expected, output, "output deepEquals expected"); 15 | } 16 | t.done(); 17 | }); 18 | }; 19 | 20 | exports.basic = function (t) { 21 | // symlink test/ -d 22 | var output = [ 23 | "test/module2 && npm install external1 external2 global1", 24 | "test/module2 && npm link", 25 | "test/module3 && npm link module2", 26 | "test/module3 && npm install global2", 27 | "test/module3 && npm link", 28 | "test/module1 && npm link module2 module3", 29 | "test/module1 && npm install external2", 30 | "test/module1 && npm link" 31 | ]; 32 | verify(t, [], output, [__dirname]); 33 | }; 34 | 35 | exports.global = function (t) { 36 | // symlink test/ -d -g global1 37 | var output = [ 38 | "test/module2 && npm link global1", 39 | "test/module2 && npm install external1 external2", 40 | "test/module2 && npm link", 41 | "test/module3 && npm link module2", 42 | "test/module3 && npm install global2", 43 | "test/module3 && npm link", 44 | "test/module1 && npm link module2 module3", 45 | "test/module1 && npm install external2", 46 | "test/module1 && npm link" 47 | ]; 48 | verify(t, ['global1'], output, [__dirname]); 49 | }; 50 | 51 | exports.globals = function (t) { 52 | // symlink test/ -d -g global1 -g global2 53 | var output = [ 54 | "test/module2 && npm link global1", 55 | "test/module2 && npm install external1 external2", 56 | "test/module2 && npm link", 57 | "test/module3 && npm link global2 module2", 58 | "test/module3 && npm link", 59 | "test/module1 && npm link module2 module3", 60 | "test/module1 && npm install external2", 61 | "test/module1 && npm link" 62 | ]; 63 | verify(t, ['global1', 'global2'], output, [__dirname]); 64 | }; 65 | 66 | exports.multidir = function (t) { 67 | // symlink test/ test/extradir1 test/extradir2 -d 68 | var output = [ 69 | 'test/module2 && npm install external1 external2 global1', 70 | 'test/module2 && npm link', 71 | 'test/module3 && npm link module2', 72 | 'test/module3 && npm install global2', 73 | 'test/module3 && npm link', 74 | 'test/module1 && npm link module2 module3', 75 | 'test/module1 && npm install external2', 76 | 'test/module1 && npm link', 77 | 'test/extradir2/module5 && npm link module2', 78 | 'test/extradir2/module5 && npm link', 79 | 'test/extradir1/module4 && npm link module5', 80 | 'test/extradir1/module4 && npm link', 81 | ]; 82 | 83 | var dirs = [ 84 | __dirname, 85 | join(__dirname, 'extradir1'), 86 | join(__dirname, 'extradir2') 87 | ]; 88 | verify(t, [], output, dirs); 89 | }; 90 | -------------------------------------------------------------------------------- /test/throw.test.js: -------------------------------------------------------------------------------- 1 | var symlink = require('../'); 2 | var join = require('path').join; 3 | 4 | exports.cyclical = function (t) { 5 | // symlink test/cyclicals -d 6 | var expct = 'cannot link cyclically dependent: ["module6","module7","module8"]'; 7 | symlink([ join(__dirname, 'cyclicals') ], [], function (err, cmds) { 8 | if (!err) { 9 | t.ok(false, "error was not set"); 10 | } 11 | if (err) { 12 | t.equal(err.message, expct, "expected error"); 13 | t.equal(cmds, undefined, "no cmds output"); 14 | } 15 | t.done(); 16 | }); 17 | }; 18 | 19 | exports.baddir = function (t) { 20 | // symlink test/noent -d 21 | symlink([ join(__dirname, 'noent') ], [], function (err, cmds) { 22 | if (!err) { 23 | t.ok(false, "error was not set"); 24 | } 25 | if (err) { 26 | t.equal(err.message.slice(0, 6), "ENOENT", "expected error"); 27 | t.equal(cmds, undefined, "no cmds output"); 28 | } 29 | t.done(); 30 | }); 31 | }; 32 | --------------------------------------------------------------------------------