33const BB = require ( 'bluebird' )
44
55const cacache = require ( 'cacache' )
6- const createReadStream = require ( 'graceful-fs' ) . createReadStream
7- const getPublishConfig = require ( './utils/get-publish-config.js' )
6+ const figgyPudding = require ( 'figgy-pudding' )
7+ const libpub = require ( 'libnpm/publish' )
8+ const libunpub = require ( 'libnpm/unpublish' )
89const lifecycle = BB . promisify ( require ( './utils/lifecycle.js' ) )
910const log = require ( 'npmlog' )
10- const mapToRegistry = require ( './utils/map-to-registry.js' )
11- const npa = require ( 'npm-package-arg' )
12- const npm = require ( './npm.js' )
11+ const npa = require ( 'libnpm/parse-arg' )
12+ const npmConfig = require ( './config/figgy-config.js' )
1313const output = require ( './utils/output.js' )
14+ const otplease = require ( './utils/otplease.js' )
1415const pack = require ( './pack' )
15- const pacote = require ( 'pacote ' )
16+ const { tarball , extract } = require ( 'libnpm ' )
1617const pacoteOpts = require ( './config/pacote' )
1718const path = require ( 'path' )
19+ const readFileAsync = BB . promisify ( require ( 'graceful-fs' ) . readFile )
1820const readJson = BB . promisify ( require ( 'read-package-json' ) )
19- const readUserInfo = require ( './utils/read-user-info.js' )
2021const semver = require ( 'semver' )
2122const statAsync = BB . promisify ( require ( 'graceful-fs' ) . stat )
2223
@@ -31,6 +32,16 @@ publish.completion = function (opts, cb) {
3132 return cb ( )
3233}
3334
35+ const PublishConfig = figgyPudding ( {
36+ dryRun : 'dry-run' ,
37+ 'dry-run' : { default : false } ,
38+ force : { default : false } ,
39+ json : { default : false } ,
40+ Promise : { default : ( ) => Promise } ,
41+ tag : { default : 'latest' } ,
42+ tmp : { }
43+ } )
44+
3445module . exports = publish
3546function publish ( args , isRetry , cb ) {
3647 if ( typeof cb !== 'function' ) {
@@ -42,15 +53,16 @@ function publish (args, isRetry, cb) {
4253
4354 log . verbose ( 'publish' , args )
4455
45- const t = npm . config . get ( 'tag' ) . trim ( )
56+ const opts = PublishConfig ( npmConfig ( ) )
57+ const t = opts . tag . trim ( )
4658 if ( semver . validRange ( t ) ) {
4759 return cb ( new Error ( 'Tag name must not be a valid SemVer range: ' + t ) )
4860 }
4961
50- return publish_ ( args [ 0 ] )
62+ return publish_ ( args [ 0 ] , opts )
5163 . then ( ( tarball ) => {
5264 const silent = log . level === 'silent'
53- if ( ! silent && npm . config . get ( ' json' ) ) {
65+ if ( ! silent && opts . json ) {
5466 output ( JSON . stringify ( tarball , null , 2 ) )
5567 } else if ( ! silent ) {
5668 output ( `+ ${ tarball . id } ` )
@@ -59,7 +71,7 @@ function publish (args, isRetry, cb) {
5971 . nodeify ( cb )
6072}
6173
62- function publish_ ( arg ) {
74+ function publish_ ( arg , opts ) {
6375 return statAsync ( arg ) . then ( ( stat ) => {
6476 if ( stat . isDirectory ( ) ) {
6577 return stat
@@ -69,17 +81,17 @@ function publish_ (arg) {
6981 throw err
7082 }
7183 } ) . then ( ( ) => {
72- return publishFromDirectory ( arg )
84+ return publishFromDirectory ( arg , opts )
7385 } , ( err ) => {
7486 if ( err . code !== 'ENOENT' && err . code !== 'ENOTDIR' ) {
7587 throw err
7688 } else {
77- return publishFromPackage ( arg )
89+ return publishFromPackage ( arg , opts )
7890 }
7991 } )
8092}
8193
82- function publishFromDirectory ( arg ) {
94+ function publishFromDirectory ( arg , opts ) {
8395 // All this readJson is because any of the given scripts might modify the
8496 // package.json in question, so we need to refresh after every step.
8597 let contents
@@ -90,12 +102,12 @@ function publishFromDirectory (arg) {
90102 } ) . then ( ( ) => {
91103 return readJson ( path . join ( arg , 'package.json' ) )
92104 } ) . then ( ( pkg ) => {
93- return cacache . tmp . withTmp ( npm . tmp , { tmpPrefix : 'fromDir' } , ( tmpDir ) => {
105+ return cacache . tmp . withTmp ( opts . tmp , { tmpPrefix : 'fromDir' } , ( tmpDir ) => {
94106 const target = path . join ( tmpDir , 'package.tgz' )
95107 return pack . packDirectory ( pkg , arg , target , null , true )
96108 . tap ( ( c ) => { contents = c } )
97- . then ( ( c ) => ! npm . config . get ( ' json' ) && pack . logContents ( c ) )
98- . then ( ( ) => upload ( arg , pkg , false , target ) )
109+ . then ( ( c ) => ! opts . json && pack . logContents ( c ) )
110+ . then ( ( ) => upload ( pkg , false , target , opts ) )
99111 } )
100112 } ) . then ( ( ) => {
101113 return readJson ( path . join ( arg , 'package.json' ) )
@@ -107,121 +119,51 @@ function publishFromDirectory (arg) {
107119 . then ( ( ) => contents )
108120}
109121
110- function publishFromPackage ( arg ) {
111- return cacache . tmp . withTmp ( npm . tmp , { tmpPrefix : 'fromPackage' } , ( tmp ) => {
122+ function publishFromPackage ( arg , opts ) {
123+ return cacache . tmp . withTmp ( opts . tmp , { tmpPrefix : 'fromPackage' } , tmp => {
112124 const extracted = path . join ( tmp , 'package' )
113125 const target = path . join ( tmp , 'package.json' )
114- const opts = pacoteOpts ( )
115- return pacote . tarball . toFile ( arg , target , opts )
116- . then ( ( ) => pacote . extract ( arg , extracted , opts ) )
126+ const pacOpts = pacoteOpts ( )
127+ return tarball . toFile ( arg , target , pacOpts )
128+ . then ( ( ) => extract ( arg , extracted , pacOpts ) )
117129 . then ( ( ) => readJson ( path . join ( extracted , 'package.json' ) ) )
118130 . then ( ( pkg ) => {
119131 return BB . resolve ( pack . getContents ( pkg , target ) )
120- . tap ( ( c ) => ! npm . config . get ( ' json' ) && pack . logContents ( c ) )
121- . tap ( ( ) => upload ( arg , pkg , false , target ) )
132+ . tap ( ( c ) => ! opts . json && pack . logContents ( c ) )
133+ . tap ( ( ) => upload ( pkg , false , target , opts ) )
122134 } )
123135 } )
124136}
125137
126- function upload ( arg , pkg , isRetry , cached ) {
127- if ( ! pkg ) {
128- return BB . reject ( new Error ( 'no package.json file found' ) )
129- }
130- if ( pkg . private ) {
131- return BB . reject ( new Error (
132- 'This package has been marked as private\n' +
133- "Remove the 'private' field from the package.json to publish it."
134- ) )
135- }
136- const mappedConfig = getPublishConfig (
137- pkg . publishConfig ,
138- npm . config ,
139- npm . registry
140- )
141- const config = mappedConfig . config
142- const registry = mappedConfig . client
143-
144- pkg . _npmVersion = npm . version
145- pkg . _nodeVersion = process . versions . node
146-
147- delete pkg . modules
148-
149- return BB . fromNode ( ( cb ) => {
150- mapToRegistry ( pkg . name , config , ( err , registryURI , auth , registryBase ) => {
151- if ( err ) { return cb ( err ) }
152- cb ( null , [ registryURI , auth , registryBase ] )
153- } )
154- } ) . spread ( ( registryURI , auth , registryBase ) => {
155- // we just want the base registry URL in this case
156- log . verbose ( 'publish' , 'registryBase' , registryBase )
157- log . silly ( 'publish' , 'uploading' , cached )
158-
159- pkg . _npmUser = {
160- name : auth . username ,
161- email : auth . email
162- }
163-
164- const params = {
165- metadata : pkg ,
166- body : ! npm . config . get ( 'dry-run' ) && createReadStream ( cached ) ,
167- auth : auth
168- }
169-
170- function closeFile ( ) {
171- if ( ! npm . config . get ( 'dry-run' ) ) {
172- params . body . close ( )
173- }
174- }
175-
176- // registry-frontdoor cares about the access level, which is only
177- // configurable for scoped packages
178- if ( config . get ( 'access' ) ) {
179- if ( ! npa ( pkg . name ) . scope && config . get ( 'access' ) === 'restricted' ) {
180- throw new Error ( "Can't restrict access to unscoped packages." )
181- }
182-
183- params . access = config . get ( 'access' )
184- }
185-
186- if ( npm . config . get ( 'dry-run' ) ) {
187- log . verbose ( 'publish' , '--dry-run mode enabled. Skipping upload.' )
188- return BB . resolve ( )
189- }
190-
191- log . showProgress ( 'publish:' + pkg . _id )
192- return BB . fromNode ( ( cb ) => {
193- registry . publish ( registryBase , params , cb )
194- } ) . catch ( ( err ) => {
195- if (
196- err . code === 'EPUBLISHCONFLICT' &&
197- npm . config . get ( 'force' ) &&
198- ! isRetry
199- ) {
200- log . warn ( 'publish' , 'Forced publish over ' + pkg . _id )
201- return BB . fromNode ( ( cb ) => {
202- npm . commands . unpublish ( [ pkg . _id ] , cb )
203- } ) . finally ( ( ) => {
204- // close the file we are trying to upload, we will open it again.
205- closeFile ( )
206- // ignore errors. Use the force. Reach out with your feelings.
207- return upload ( arg , pkg , true , cached ) . catch ( ( ) => {
208- // but if it fails again, then report the first error.
209- throw err
138+ function upload ( pkg , isRetry , cached , opts ) {
139+ if ( ! opts . dryRun ) {
140+ return readFileAsync ( cached ) . then ( tarball => {
141+ return otplease ( opts , opts => {
142+ return libpub ( pkg , tarball , opts )
143+ } ) . catch ( err => {
144+ if (
145+ err . code === 'EPUBLISHCONFLICT' &&
146+ opts . force &&
147+ ! isRetry
148+ ) {
149+ log . warn ( 'publish' , 'Forced publish over ' + pkg . _id )
150+ return otplease ( opts , opts => libunpub (
151+ npa . resolve ( pkg . name , pkg . version ) , opts
152+ ) ) . finally ( ( ) => {
153+ // ignore errors. Use the force. Reach out with your feelings.
154+ return otplease ( opts , opts => {
155+ return upload ( pkg , true , tarball , opts )
156+ } ) . catch ( ( ) => {
157+ // but if it fails again, then report the first error.
158+ throw err
159+ } )
210160 } )
211- } )
212- } else {
213- // close the file we are trying to upload, all attempts to resume will open it again
214- closeFile ( )
215- throw err
216- }
217- } )
218- } ) . catch ( ( err ) => {
219- if ( err . code !== 'EOTP' && ! ( err . code === 'E401' && / o n e - t i m e p a s s / . test ( err . message ) ) ) throw err
220- // we prompt on stdout and read answers from stdin, so they need to be ttys.
221- if ( ! process . stdin . isTTY || ! process . stdout . isTTY ) throw err
222- return readUserInfo . otp ( ) . then ( ( otp ) => {
223- npm . config . set ( 'otp' , otp )
224- return upload ( arg , pkg , isRetry , cached )
161+ } else {
162+ throw err
163+ }
164+ } )
225165 } )
226- } )
166+ } else {
167+ return opts . Promise . resolve ( true )
168+ }
227169}
0 commit comments