1 |
|
2 |
|
3 |
|
4 | var npm = require('./npm.js')
|
5 | var symlink = require('./utils/link.js')
|
6 | var fs = require('graceful-fs')
|
7 | var log = require('npmlog')
|
8 | var asyncMap = require('slide').asyncMap
|
9 | var chain = require('slide').chain
|
10 | var path = require('path')
|
11 | var build = require('./build.js')
|
12 | var npa = require('npm-package-arg')
|
13 | var usage = require('./utils/usage')
|
14 | var output = require('./utils/output.js')
|
15 |
|
16 | module.exports = link
|
17 |
|
18 | link.usage = usage(
|
19 | 'link',
|
20 | 'npm link (in package dir)' +
|
21 | '\nnpm link [<@scope>/]<pkg>[@<version>]'
|
22 | )
|
23 |
|
24 | link.completion = function (opts, cb) {
|
25 | var dir = npm.globalDir
|
26 | fs.readdir(dir, function (er, files) {
|
27 | cb(er, files.filter(function (f) {
|
28 | return !f.match(/^[\._-]/)
|
29 | }))
|
30 | })
|
31 | }
|
32 |
|
33 | function link (args, cb) {
|
34 | if (process.platform === 'win32') {
|
35 | var semver = require('semver')
|
36 | if (!semver.gte(process.version, '0.7.9')) {
|
37 | var msg = 'npm link not supported on windows prior to node 0.7.9'
|
38 | var e = new Error(msg)
|
39 | e.code = 'ENOTSUP'
|
40 | e.errno = require('constants').ENOTSUP
|
41 | return cb(e)
|
42 | }
|
43 | }
|
44 |
|
45 | if (npm.config.get('global')) {
|
46 | return cb(new Error(
|
47 | 'link should never be --global.\n' +
|
48 | 'Please re-run this command with --local'
|
49 | ))
|
50 | }
|
51 |
|
52 | if (args.length === 1 && args[0] === '.') args = []
|
53 | if (args.length) return linkInstall(args, cb)
|
54 | linkPkg(npm.prefix, cb)
|
55 | }
|
56 |
|
57 | function parentFolder (id, folder) {
|
58 | if (id[0] === '@') {
|
59 | return path.resolve(folder, '..', '..')
|
60 | } else {
|
61 | return path.resolve(folder, '..')
|
62 | }
|
63 | }
|
64 |
|
65 | function linkInstall (pkgs, cb) {
|
66 | asyncMap(pkgs, function (pkg, cb) {
|
67 | var t = path.resolve(npm.globalDir, '..')
|
68 | var pp = path.resolve(npm.globalDir, pkg)
|
69 | var rp = null
|
70 | var target = path.resolve(npm.dir, pkg)
|
71 |
|
72 | function n (er, data) {
|
73 | if (er) return cb(er, data)
|
74 |
|
75 | var installed = data.filter(function (info) {
|
76 | var id = info[0]
|
77 | var folder = info[1]
|
78 | return parentFolder(id, folder) === npm.globalDir
|
79 | })
|
80 | var id = installed[0][0]
|
81 | pp = installed[0][1]
|
82 | var what = npa(id)
|
83 | pkg = what.name
|
84 | target = path.resolve(npm.dir, pkg)
|
85 | next()
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 | if (pkg[0] !== '@' && (pkg.indexOf('/') !== -1 || pkg.indexOf('\\') !== -1)) {
|
91 | return fs.lstat(path.resolve(pkg), function (er, st) {
|
92 | if (er || !st.isDirectory()) {
|
93 | npm.commands.install(t, pkg, n)
|
94 | } else {
|
95 | rp = path.resolve(pkg)
|
96 | linkPkg(rp, n)
|
97 | }
|
98 | })
|
99 | }
|
100 |
|
101 | fs.lstat(pp, function (er, st) {
|
102 | if (er) {
|
103 | rp = pp
|
104 | return npm.commands.install(t, [pkg], n)
|
105 | } else if (!st.isSymbolicLink()) {
|
106 | rp = pp
|
107 | next()
|
108 | } else {
|
109 | return fs.realpath(pp, function (er, real) {
|
110 | if (er) log.warn('invalid symbolic link', pkg)
|
111 | else rp = real
|
112 | next()
|
113 | })
|
114 | }
|
115 | })
|
116 |
|
117 | function next () {
|
118 | if (npm.config.get('dry-run')) return resultPrinter(pkg, pp, target, rp, cb)
|
119 | chain(
|
120 | [
|
121 | [ function (cb) {
|
122 | log.verbose('link', 'symlinking %s to %s', pp, target)
|
123 | cb()
|
124 | } ],
|
125 | [symlink, pp, target],
|
126 |
|
127 | rp && [build, [target], npm.config.get('global'), build._noLC, true],
|
128 | [resultPrinter, pkg, pp, target, rp]
|
129 | ],
|
130 | cb
|
131 | )
|
132 | }
|
133 | }, cb)
|
134 | }
|
135 |
|
136 | function linkPkg (folder, cb_) {
|
137 | var me = folder || npm.prefix
|
138 | var readJson = require('read-package-json')
|
139 |
|
140 | log.verbose('linkPkg', folder)
|
141 |
|
142 | readJson(path.resolve(me, 'package.json'), function (er, d) {
|
143 | function cb (er) {
|
144 | return cb_(er, [[d && d._id, target, null, null]])
|
145 | }
|
146 | if (er) return cb(er)
|
147 | if (!d.name) {
|
148 | er = new Error('Package must have a name field to be linked')
|
149 | return cb(er)
|
150 | }
|
151 | if (npm.config.get('dry-run')) return resultPrinter(path.basename(me), me, target, cb)
|
152 | var target = path.resolve(npm.globalDir, d.name)
|
153 | symlink(me, target, false, true, function (er) {
|
154 | if (er) return cb(er)
|
155 | log.verbose('link', 'build target', target)
|
156 |
|
157 | npm.commands.install(me, [], function (er) {
|
158 | if (er) return cb(er)
|
159 |
|
160 |
|
161 | build([target], true, build._noLC, true, function (er) {
|
162 | if (er) return cb(er)
|
163 | resultPrinter(path.basename(me), me, target, cb)
|
164 | })
|
165 | })
|
166 | })
|
167 | })
|
168 | }
|
169 |
|
170 | function resultPrinter (pkg, src, dest, rp, cb) {
|
171 | if (typeof cb !== 'function') {
|
172 | cb = rp
|
173 | rp = null
|
174 | }
|
175 | var where = dest
|
176 | rp = (rp || '').trim()
|
177 | src = (src || '').trim()
|
178 |
|
179 | if (npm.config.get('parseable')) {
|
180 | return parseableOutput(dest, rp || src, cb)
|
181 | }
|
182 | if (rp === src) rp = null
|
183 | output(where + ' -> ' + src + (rp ? ' -> ' + rp : ''))
|
184 | cb()
|
185 | }
|
186 |
|
187 | function parseableOutput (dest, rp, cb) {
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 | output(dest + '::' + rp)
|
196 | cb()
|
197 | }
|