@@ -16,6 +16,7 @@ import * as httpUtils from './httpUtils
16
16
import * as nodeUtils from './nodeUtils' ;
17
17
import * as npmRegistryUtils from './npmRegistryUtils' ;
18
18
import { RegistrySpec , Descriptor , Locator , PackageManagerSpec } from './types' ;
19
+ import { BinList , BinSpec , InstallSpec } from './types' ;
19
20
20
21
export function getRegistryFromPackageManagerSpec ( spec : PackageManagerSpec ) {
21
22
return process . env . COREPACK_NPM_REGISTRY
@@ -124,7 +125,15 @@ function parseURLReference(locator: Locator) {
124
125
return { version : encodeURIComponent ( href ) , build : [ ] } ;
125
126
}
126
127
127
- export async function installVersion ( installTarget : string , locator : Locator , { spec} : { spec : PackageManagerSpec } ) {
128
+ function isValidBinList ( x : unknown ) : x is BinList {
129
+ return Array . isArray ( x ) && x . length > 0 ;
130
+ }
131
+
132
+ function isValidBinSpec ( x : unknown ) : x is BinSpec {
133
+ return typeof x === `object` && x !== null && ! Array . isArray ( x ) && Object . keys ( x ) . length > 0 ;
134
+ }
135
+
136
+ export async function installVersion ( installTarget : string , locator : Locator , { spec} : { spec : PackageManagerSpec } ) : Promise < InstallSpec > {
128
137
const locatorIsASupportedPackageManager = isSupportedPackageManagerLocator ( locator ) ;
129
138
const locatorReference = locatorIsASupportedPackageManager ? semver . parse ( locator . reference ) ! : parseURLReference ( locator ) ;
130
139
const { version, build} = locatorReference ;
@@ -152,13 +161,18 @@ export async function installVersion(installTarget: string, locator: Locator, {s
152
161
153
162
let url : string ;
154
163
if ( locatorIsASupportedPackageManager ) {
155
- const defaultNpmRegistryURL = spec . url . replace ( `{}` , version ) ;
156
- url = process . env . COREPACK_NPM_REGISTRY ?
157
- defaultNpmRegistryURL . replace (
158
- npmRegistryUtils . DEFAULT_NPM_REGISTRY_URL ,
159
- ( ) => process . env . COREPACK_NPM_REGISTRY ! ,
160
- ) :
161
- defaultNpmRegistryURL ;
164
+ url = spec . url . replace ( `{}` , version ) ;
165
+ if ( process . env . COREPACK_NPM_REGISTRY ) {
166
+ const registry = getRegistryFromPackageManagerSpec ( spec ) ;
167
+ if ( registry . type === `npm` ) {
168
+ url = await npmRegistryUtils . fetchTarballUrl ( registry . package , version ) ;
169
+ } else {
170
+ url = url . replace (
171
+ npmRegistryUtils . DEFAULT_NPM_REGISTRY_URL ,
172
+ ( ) => process . env . COREPACK_NPM_REGISTRY ! ,
173
+ ) ;
174
+ }
175
+ }
162
176
} else {
163
177
url = decodeURIComponent ( version ) ;
164
178
}
@@ -191,13 +205,34 @@ export async function installVersion(installTarget: string, locator: Locator, {s
191
205
const hash = stream . pipe ( createHash ( algo ) ) ;
192
206
await once ( sendTo , `finish` ) ;
193
207
194
- let bin ;
195
- if ( ! locatorIsASupportedPackageManager ) {
196
- if ( ext === `.tgz` ) {
197
- bin = require ( path . join ( tmpFolder , `package.json` ) ) . bin ;
198
- } else if ( ext === `.js` ) {
208
+ let bin : BinSpec | BinList ;
209
+ const isSingleFile = outputFile !== null ;
210
+
211
+ // In config, yarn berry is expected to be downloaded as a single file,
212
+ // and therefore `spec.bin` is an array. However, when dowloaded from
213
+ // custom npm registry as tarball, `bin` should be a map.
214
+ // In this case, we ignore the configured `spec.bin`.
215
+
216
+ if ( isSingleFile ) {
217
+ if ( locatorIsASupportedPackageManager && isValidBinList ( spec . bin ) ) {
218
+ bin = spec . bin ;
219
+ } else {
199
220
bin = [ locator . name ] ;
200
221
}
222
+ } else {
223
+ if ( locatorIsASupportedPackageManager && isValidBinSpec ( spec . bin ) ) {
224
+ bin = spec . bin ;
225
+ } else {
226
+ const { name : packageName , bin : packageBin } = require ( path . join ( tmpFolder , `package.json` ) ) ;
227
+ if ( typeof packageBin === `string` ) {
228
+ // When `bin` is a string, the name of the executable is the name of the package.
229
+ bin = { [ packageName ] : packageBin } ;
230
+ } else if ( isValidBinSpec ( packageBin ) ) {
231
+ bin = packageBin ;
232
+ } else {
233
+ throw new Error ( `Unable to locate bin in package.json` ) ;
234
+ }
235
+ }
201
236
}
202
237
203
238
const actualHash = hash . digest ( `hex` ) ;
@@ -292,18 +327,19 @@ async function renameUnderWindows(oldPath: fs.PathLike, newPath: fs.PathLike) {
292
327
/**
293
328
* Loads the binary, taking control of the current process.
294
329
*/
295
- export async function runVersion ( locator : Locator , installSpec : { location : string , spec : PackageManagerSpec } , binName : string , args : Array < string > ) : Promise < void > {
330
+ export async function runVersion ( locator : Locator , installSpec : InstallSpec & { spec : PackageManagerSpec } , binName : string , args : Array < string > ) : Promise < void > {
296
331
let binPath : string | null = null ;
297
- if ( Array . isArray ( installSpec . spec . bin ) ) {
298
- if ( installSpec . spec . bin . some ( bin => bin === binName ) ) {
332
+ const bin = installSpec . bin ?? installSpec . spec . bin ;
333
+ if ( Array . isArray ( bin ) ) {
334
+ if ( bin . some ( name => name === binName ) ) {
299
335
const parsedUrl = new URL ( installSpec . spec . url ) ;
300
336
const ext = path . posix . extname ( parsedUrl . pathname ) ;
301
337
if ( ext === `.js` ) {
302
338
binPath = path . join ( installSpec . location , path . posix . basename ( parsedUrl . pathname ) ) ;
303
339
}
304
340
}
305
341
} else {
306
- for ( const [ name , dest ] of Object . entries ( installSpec . spec . bin ) ) {
342
+ for ( const [ name , dest ] of Object . entries ( bin ) ) {
307
343
if ( name === binName ) {
308
344
binPath = path . join ( installSpec . location , dest ) ;
309
345
break ;
0 commit comments