@@ -88,44 +88,94 @@ function loadNxConfig(configFile) {
88
88
return JSON . parse ( nx ) ;
89
89
}
90
90
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
+
91
114
function 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 ( ) ;
93
116
const spawnOptions = {
94
117
stdio : /** @type {const } */ ( "pipe" ) ,
95
118
shell : true ,
96
119
windowsVerbatimArguments : true ,
97
120
} ;
98
121
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
+ }
112
140
}
113
- }
114
141
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)" ) ;
121
159
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 } ` ) ;
128
174
}
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)" ) ;
129
179
}
130
180
}
131
181
0 commit comments