UNPKG

5.04 kBJavaScriptView Raw
1
2module.exports = publish
3
4var npm = require("./npm.js")
5 , log = require("npmlog")
6 , path = require("path")
7 , readJson = require("read-package-json")
8 , lifecycle = require("./utils/lifecycle.js")
9 , chain = require("slide").chain
10 , Conf = require("./config/core.js").Conf
11 , CachingRegClient = require("./cache/caching-client.js")
12 , mapToRegistry = require("./utils/map-to-registry.js")
13 , cachedPackageRoot = require("./cache/cached-package-root.js")
14 , createReadStream = require("graceful-fs").createReadStream
15 , npa = require("npm-package-arg")
16
17publish.usage = "npm publish <tarball>"
18 + "\nnpm publish <folder>"
19 + "\n\nPublishes '.' if no argument supplied"
20
21publish.completion = function (opts, cb) {
22 // publish can complete to a folder with a package.json
23 // or a tarball, or a tarball url.
24 // for now, not yet implemented.
25 return cb()
26}
27
28function publish (args, isRetry, cb) {
29 if (typeof cb !== "function") {
30 cb = isRetry
31 isRetry = false
32 }
33 if (args.length === 0) args = ["."]
34 if (args.length !== 1) return cb(publish.usage)
35
36 log.verbose("publish", args)
37 var arg = args[0]
38 // if it's a local folder, then run the prepublish there, first.
39 readJson(path.resolve(arg, "package.json"), function (er, data) {
40 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
41
42 if (data) {
43 if (!data.name) return cb(new Error("No name provided"))
44 if (!data.version) return cb(new Error("No version provided"))
45 }
46
47 // Error is OK. Could be publishing a URL or tarball, however, that means
48 // that we will not have automatically run the prepublish script, since
49 // that gets run when adding a folder to the cache.
50 if (er) return cacheAddPublish(arg, false, isRetry, cb)
51 else cacheAddPublish(arg, true, isRetry, cb)
52 })
53}
54
55// didPre in this case means that we already ran the prepublish script,
56// and that the "dir" is an actual directory, and not something silly
57// like a tarball or name@version thing.
58// That means that we can run publish/postpublish in the dir, rather than
59// in the cache dir.
60function cacheAddPublish (dir, didPre, isRetry, cb) {
61 npm.commands.cache.add(dir, null, null, false, function (er, data) {
62 if (er) return cb(er)
63 log.silly("publish", data)
64 var cachedir = path.resolve(cachedPackageRoot(data), "package")
65 chain([ !didPre &&
66 [lifecycle, data, "prepublish", cachedir]
67 , [publish_, dir, data, isRetry, cachedir]
68 , [lifecycle, data, "publish", didPre ? dir : cachedir]
69 , [lifecycle, data, "postpublish", didPre ? dir : cachedir] ]
70 , cb )
71 })
72}
73
74function publish_ (arg, data, isRetry, cachedir, cb) {
75 if (!data) return cb(new Error("no package.json file found"))
76
77 var registry = npm.registry
78 var config = npm.config
79
80 // check for publishConfig hash
81 if (data.publishConfig) {
82 config = new Conf(npm.config)
83 config.save = npm.config.save.bind(npm.config)
84
85 // don't modify the actual publishConfig object, in case we have
86 // to set a login token or some other data.
87 config.unshift(Object.keys(data.publishConfig).reduce(function (s, k) {
88 s[k] = data.publishConfig[k]
89 return s
90 }, {}))
91 registry = new CachingRegClient(config)
92 }
93
94 data._npmVersion = npm.version
95 data._nodeVersion = process.versions.node
96
97 delete data.modules
98 if (data.private) return cb(
99 new Error(
100 "This package has been marked as private\n" +
101 "Remove the 'private' field from the package.json to publish it."
102 )
103 )
104
105 mapToRegistry(data.name, config, function (er, registryURI, auth, registryBase) {
106 if (er) return cb(er)
107
108 var tarballPath = cachedir + ".tgz"
109
110 // we just want the base registry URL in this case
111 log.verbose("publish", "registryBase", registryBase)
112 log.silly("publish", "uploading", tarballPath)
113
114 data._npmUser = {
115 name : auth.username,
116 email : auth.email
117 }
118
119 var params = {
120 metadata : data,
121 body : createReadStream(tarballPath),
122 auth : auth
123 }
124
125 // registry-frontdoor cares about the access level, which is only
126 // configurable for scoped packages
127 if (npa(data.name).scope) {
128 params.access = config.get("access")
129 }
130 else {
131 params.access = "public"
132 }
133
134 registry.publish(registryBase, params, function (er) {
135 if (er && er.code === "EPUBLISHCONFLICT" &&
136 npm.config.get("force") && !isRetry) {
137 log.warn("publish", "Forced publish over " + data._id)
138 return npm.commands.unpublish([data._id], function (er) {
139 // ignore errors. Use the force. Reach out with your feelings.
140 // but if it fails again, then report the first error.
141 publish([arg], er || true, cb)
142 })
143 }
144 // report the unpublish error if this was a retry and unpublish failed
145 if (er && isRetry && isRetry !== true) return cb(isRetry)
146 if (er) return cb(er)
147 console.log("+ " + data._id)
148 cb()
149 })
150 })
151}