@@ -88,44 +88,94 @@ function loadNxConfig(configFile) {
8888 return JSON . parse ( nx ) ;
8989}
9090
91+ /**
92+ * Detects whether to use npm or yarn to publish based on .npmrc existence
93+ * @returns {boolean } true if npm should be used, false if yarn should be used
94+ * @throws {Error } if neither .npmrc nor .yarnrc.yml exists
95+ */
96+ function shouldUseNpm ( ) {
97+ const hasNpmrc = fs . existsSync ( '.npmrc' ) ;
98+ const hasYarnrc = fs . existsSync ( '.yarnrc.yml' ) ;
99+
100+ if ( ! hasNpmrc && ! hasYarnrc ) {
101+ error ( 'No package manager configuration found. Expected either .npmrc or .yarnrc.yml file.' ) ;
102+ throw new Error ( 'No package manager configuration found' ) ;
103+ }
104+
105+ if ( hasNpmrc && hasYarnrc ) {
106+ // If both exist, prefer npm (could be changed based on project preference)
107+ info ( 'Both .npmrc and .yarnrc.yml found, using npm configuration' ) ;
108+ return true ;
109+ }
110+
111+ return hasNpmrc ;
112+ }
113+
91114function verifyNpmAuth ( registry = NPM_DEFEAULT_REGISTRY ) {
92- const npmErrorRegex = / n p m e r r o r c o d e ( \w + ) / ;
115+ const useNpm = shouldUseNpm ( ) ;
93116 const spawnOptions = {
94117 stdio : /** @type {const } */ ( "pipe" ) ,
95118 shell : true ,
96119 windowsVerbatimArguments : true ,
97120 } ;
98121
99- const whoamiArgs = [ "whoami" , "--registry" , registry ] ;
100- const whoami = spawnSync ( "npm" , whoamiArgs , spawnOptions ) ;
101- if ( whoami . status !== 0 ) {
102- const error = whoami . stderr . toString ( ) ;
103- const m = error . match ( npmErrorRegex ) ;
104- const errorCode = m && m [ 1 ] ;
105- switch ( errorCode ) {
106- case "EINVALIDNPMTOKEN" :
107- throw new Error ( `Invalid auth token for npm registry: ${ registry } ` ) ;
108- case "ENEEDAUTH" :
109- throw new Error ( `Missing auth token for npm registry: ${ registry } ` ) ;
110- default :
111- throw new Error ( error ) ;
122+ if ( useNpm ) {
123+ info ( "Using npm for authentication (found .npmrc)" ) ;
124+ const npmErrorRegex = / n p m e r r o r c o d e ( \w + ) / ;
125+
126+ const whoamiArgs = [ "whoami" , "--registry" , registry ] ;
127+ const whoami = spawnSync ( "npm" , whoamiArgs , spawnOptions ) ;
128+ if ( whoami . status !== 0 ) {
129+ const error = whoami . stderr . toString ( ) ;
130+ const m = error . match ( npmErrorRegex ) ;
131+ const errorCode = m && m [ 1 ] ;
132+ switch ( errorCode ) {
133+ case "EINVALIDNPMTOKEN" :
134+ throw new Error ( `Invalid auth token for npm registry: ${ registry } ` ) ;
135+ case "ENEEDAUTH" :
136+ throw new Error ( `Missing auth token for npm registry: ${ registry } ` ) ;
137+ default :
138+ throw new Error ( error ) ;
139+ }
112140 }
113- }
114141
115- const tokenArgs = [ "token" , "list" , "--registry" , registry ] ;
116- const token = spawnSync ( "npm" , tokenArgs , spawnOptions ) ;
117- if ( token . status !== 0 ) {
118- const error = token . stderr . toString ( ) ;
119- const m = error . match ( npmErrorRegex ) ;
120- const errorCode = m && m [ 1 ] ;
142+ const tokenArgs = [ "token" , "list" , "--registry" , registry ] ;
143+ const token = spawnSync ( "npm" , tokenArgs , spawnOptions ) ;
144+ if ( token . status !== 0 ) {
145+ const error = token . stderr . toString ( ) ;
146+ const m = error . match ( npmErrorRegex ) ;
147+ const errorCode = m && m [ 1 ] ;
148+
149+ // E403 means the token doesn't have permission to list tokens, but that's
150+ // not required for publishing. Only fail for other error codes.
151+ if ( errorCode === "E403" ) {
152+ info ( `Token verification skipped: token doesn't have permission to list tokens (${ errorCode } )` ) ;
153+ } else {
154+ throw new Error ( m ? `Auth token for '${ registry } ' returned error code ${ errorCode } ` : error ) ;
155+ }
156+ }
157+ } else {
158+ info ( "Using yarn for authentication (no .npmrc found)" ) ;
121159
122- // E403 means the token doesn't have permission to list tokens, but that's
123- // not required for publishing. Only fail for other error codes.
124- if ( errorCode === "E403" ) {
125- info ( `Token verification skipped: token doesn't have permission to list tokens (${ errorCode } )` ) ;
126- } else {
127- throw new Error ( m ? `Auth token for '${ registry } ' returned error code ${ errorCode } ` : error ) ;
160+ const whoamiArgs = [ "npm" , "whoami" , "--publish" ] ;
161+ const whoami = spawnSync ( "yarn" , whoamiArgs , spawnOptions ) ;
162+ if ( whoami . status !== 0 ) {
163+ const stderr = whoami . stderr . toString ( ) . trim ( ) ;
164+ const stdout = whoami . stdout . toString ( ) . trim ( ) ;
165+ const errorOutput = stderr || stdout || 'No error message available' ;
166+
167+ // Yarn uses different error format
168+ if ( errorOutput . includes ( "Invalid authentication" ) || errorOutput . includes ( "Failed with errors" ) ) {
169+ throw new Error ( `Invalid or missing auth token for registry: ${ registry } ` ) ;
170+ }
171+
172+ // Provide more context about the yarn authentication failure
173+ throw new Error ( `Yarn authentication failed (exit code ${ whoami . status } ): ${ errorOutput } ` ) ;
128174 }
175+
176+ // Skip token listing for yarn since it doesn't support npm token commands
177+ // The whoami check above is sufficient to verify authentication
178+ info ( "Skipping token list check when using yarn (not required for publishing)" ) ;
129179 }
130180}
131181
0 commit comments