@@ -48,24 +48,20 @@ export async function importBuilders(
4848) : Promise < Map < string , BuilderWithPkg > > {
4949 const buildersDir = join ( cwd , VERCEL_DIR , 'builders' ) ;
5050
51- let importResult = await resolveBuilders ( cwd , buildersDir , builderSpecs ) ;
51+ let importResult = await resolveBuilders ( buildersDir , builderSpecs ) ;
5252
5353 if ( 'buildersToAdd' in importResult ) {
5454 const installResult = await installBuilders (
5555 buildersDir ,
5656 importResult . buildersToAdd
5757 ) ;
5858
59- // resolve builders again, after they've been installed
60- // with specs from the newly installed builders.
6159 importResult = await resolveBuilders (
62- cwd ,
6360 buildersDir ,
6461 builderSpecs ,
6562 installResult . resolvedSpecs
6663 ) ;
6764
68- // We shouldn't get buildersToAdd a second time from resolveBuilders.
6965 if ( 'buildersToAdd' in importResult ) {
7066 throw new Error ( 'Something went wrong!' ) ;
7167 }
@@ -81,44 +77,19 @@ export async function importBuilders(
8177 return importResult . builders ;
8278}
8379
84- // Cache for CLI package.json peerDependencies
85- let peerDependencies : Record < string , string > | undefined ;
86-
87- function getPeerDependencies ( ) : Record < string , string > {
88- if ( ! peerDependencies ) {
89- try {
90- const cliPkgPath = require_ . resolve ( 'vercel/package.json' , {
91- paths : [ __dirname ] ,
92- } ) ;
93- const cliPkg = require_ ( cliPkgPath ) as PackageJson ;
94- peerDependencies =
95- ( cliPkg . peerDependencies as Record < string , string > ) || { } ;
96- } catch ( e ) {
97- output . error (
98- 'Failed to parse peer dependencies from vercel/package.json'
99- ) ;
100- peerDependencies = { } ;
101- }
102- }
103-
104- return peerDependencies ;
105- }
106-
10780export async function resolveBuilders (
108- cwd : string ,
10981 buildersDir : string ,
11082 builderSpecs : Set < string > ,
11183 resolvedSpecs ?: Map < string , string >
11284) : Promise < ResolveBuildersResult > {
11385 const builders = new Map < string , BuilderWithPkg > ( ) ;
11486 const buildersToAdd = new Set < string > ( ) ;
115- const peerDeps = getPeerDependencies ( ) ;
11687
11788 for ( const spec of builderSpecs ) {
11889 const resolvedSpec = resolvedSpecs ?. get ( spec ) || spec ;
11990 const parsed = npa ( resolvedSpec ) ;
120- const { name } = parsed ;
12191
92+ const { name } = parsed ;
12293 if ( ! name ) {
12394 // A URL was specified - will need to install it and resolve the
12495 // proper package name from the written `package.json` file
@@ -137,103 +108,93 @@ export async function resolveBuilders(
137108 continue ;
138109 }
139110
140- // Resolution priority:
141- // 1. .vercel/builders (where we dynamically install builders)
142- // 2. CLI's node_modules (it looks at wherever this file and associated node modules)
143- // 3. Install into .vercel/builders if not found in either
111+ try {
112+ let pkgPath : string | undefined ;
113+ let builderPkg : PackageJson | undefined ;
144114
145- let pkgPath : string | undefined ;
146- let builderPkg : PackageJson | undefined ;
147- const peerVersion = peerDeps [ name ] ;
115+ try {
116+ // First try `.vercel/builders`. The package name should always be available
117+ // at the top-level of `node_modules` since CLI is installing those directly.
118+ pkgPath = join ( buildersDir , 'node_modules' , name , 'package.json' ) ;
119+ builderPkg = await readJSON ( pkgPath ) ;
120+ } catch ( error : unknown ) {
121+ if ( ! isErrnoException ( error ) ) {
122+ throw error ;
123+ }
124+ if ( error . code !== 'ENOENT' ) {
125+ throw error ;
126+ }
148127
149- // 1. Try .vercel/builders (where we install builders)
150- try {
151- pkgPath = join ( buildersDir , 'node_modules' , name , 'package.json' ) ;
152- const cachedPkg : PackageJson = await readJSON ( pkgPath ) ;
153- output . debug ( `"${ name } @${ cachedPkg . version } " found in .vercel/builders` ) ;
128+ // If `pkgPath` wasn't found in `.vercel/builders` then try as a CLI local
129+ // dependency. `require.resolve()` will throw if the Builder is not a CLI
130+ // dep, in which case we'll install it into `.vercel/builders`.
131+ pkgPath = require_ . resolve ( `${ name } /package.json` , {
132+ paths : [ __dirname ] ,
133+ } ) ;
134+ builderPkg = await readJSON ( pkgPath ) ;
135+ }
154136
155- // Verify cached version matches peerDeps exactly
156- if ( peerVersion && cachedPkg . version !== peerVersion ) {
137+ if ( ! builderPkg || ! pkgPath ) {
138+ throw new Error ( `Failed to load \`package.json\` for "${ name } "` ) ;
139+ }
140+
141+ if ( typeof builderPkg . version !== 'string' ) {
142+ throw new Error (
143+ `\`package.json\` for "${ name } " does not contain a "version" field`
144+ ) ;
145+ }
146+
147+ if ( parsed . type === 'version' && parsed . rawSpec !== builderPkg . version ) {
148+ // An explicit Builder version was specified but it does
149+ // not match the version that is currently installed
157150 output . debug (
158- `"${ name } @${ cachedPkg . version } " does not match peerDep "${ peerVersion } ", will reinstall `
151+ `Installed version "${ name } @${ builderPkg . version } " does not match "${ parsed . rawSpec } " `
159152 ) ;
160- buildersToAdd . add ( ` ${ name } @ ${ peerVersion } ` ) ;
153+ buildersToAdd . add ( spec ) ;
161154 continue ;
162155 }
163- builderPkg = cachedPkg ;
164- } catch ( err : unknown ) {
165- if ( ! isErrnoException ( err ) || err . code !== 'ENOENT' ) {
166- throw err ;
167- }
168- output . debug ( `"${ name } @${ peerVersion } " not found in .vercel/builders` ) ;
169- }
170156
171- // 2. Try CLI's node_modules (wherever CLI is installed - globally or as a project dep)
172- if ( ! builderPkg ) {
173- try {
174- pkgPath = require_ . resolve ( `${ name } /package.json` , {
175- paths : [ __dirname ] ,
176- } ) ;
177- builderPkg = await readJSON ( pkgPath ) ;
178- output . debug ( `Found "${ name } " in CLI's node_modules` ) ;
179- } catch ( err : unknown ) {
180- if ( ! isErrnoException ( err ) || err . code !== 'MODULE_NOT_FOUND' ) {
181- throw err ;
182- }
183- // Not found in cache or CLI - install (or error on second run if no peerDep)
184- if ( resolvedSpecs ) {
185- throw new Error ( `Builder "${ name } " not found` ) ;
186- }
187- output . debug ( `"${ name } " not found anywhere, will install` ) ;
188- buildersToAdd . add ( peerVersion ? `${ name } @${ peerVersion } ` : spec ) ;
157+ if (
158+ parsed . type === 'range' &&
159+ ! satisfies ( builderPkg . version , parsed . rawSpec )
160+ ) {
161+ // An explicit Builder range was specified but it is not
162+ // compatible with the version that is currently installed
163+ output . debug (
164+ `Installed version "${ name } @${ builderPkg . version } " is not compatible with "${ parsed . rawSpec } "`
165+ ) ;
166+ buildersToAdd . add ( spec ) ;
189167 continue ;
190168 }
191- }
192169
193- if ( ! builderPkg || ! pkgPath ) {
194- throw new Error ( `Failed to load \`package.json\` for "${ name } "` ) ;
195- }
170+ // TODO: handle `parsed.type === 'tag'` ("latest" vs. anything else?)
196171
197- if ( typeof builderPkg . version !== 'string' ) {
198- throw new Error (
199- `\`package.json\` for "${ name } " does not contain a "version" field`
200- ) ;
201- }
172+ const path = join ( dirname ( pkgPath ) , builderPkg . main || 'index.js' ) ;
202173
203- // Validate explicit version/range requirements from spec
204- if ( parsed . type === 'version' && parsed . rawSpec !== builderPkg . version ) {
205- output . debug (
206- `Installed version "${ name } @${ builderPkg . version } " does not match "${ parsed . rawSpec } "`
207- ) ;
208- buildersToAdd . add ( spec ) ;
209- continue ;
210- }
174+ const builder = require_ ( path ) ;
211175
212- if (
213- parsed . type === 'range' &&
214- ! satisfies ( builderPkg . version , parsed . rawSpec )
215- ) {
216- output . debug (
217- `Installed version "${ name } @${ builderPkg . version } " is not compatible with "${ parsed . rawSpec } "`
218- ) ;
219- buildersToAdd . add ( spec ) ;
220- continue ;
176+ builders . set ( spec , {
177+ builder,
178+ pkg : {
179+ name,
180+ ...builderPkg ,
181+ } ,
182+ path,
183+ pkgPath,
184+ } ) ;
185+ output . debug ( `Imported Builder "${ name } " from "${ dirname ( pkgPath ) } "` ) ;
186+ } catch ( err : any ) {
187+ // `resolvedSpecs` is only passed into this function on the 2nd run,
188+ // so if MODULE_NOT_FOUND happens in that case then we don't want to
189+ // try to install again. Instead just pass through the error to the user
190+ if ( err . code === 'MODULE_NOT_FOUND' && ! resolvedSpecs ) {
191+ output . debug ( `Failed to import "${ name } ": ${ err } ` ) ;
192+ buildersToAdd . add ( spec ) ;
193+ } else {
194+ err . message = `Importing "${ name } ": ${ err . message } ` ;
195+ throw err ;
196+ }
221197 }
222-
223- // TODO: handle `parsed.type === 'tag'` ("latest" vs. anything else?)
224- const path = join ( dirname ( pkgPath ) , builderPkg . main || 'index.js' ) ;
225- const builder = require_ ( path ) ;
226-
227- builders . set ( spec , {
228- builder,
229- pkg : {
230- name,
231- ...builderPkg ,
232- } ,
233- path,
234- pkgPath,
235- } ) ;
236- output . debug ( `Imported Builder "${ name } " from "${ dirname ( pkgPath ) } "` ) ;
237198 }
238199
239200 // Add any Builders that are not yet present into `.vercel/builders`
@@ -249,9 +210,6 @@ async function installBuilders(
249210 buildersToAdd : Set < string >
250211) {
251212 const resolvedSpecs = new Map < string , string > ( ) ;
252-
253- // First create an empty package.json in the cache dir where
254- // we store our downloaded builders.
255213 const buildersPkgPath = join ( buildersDir , 'package.json' ) ;
256214 try {
257215 const emptyPkgJson = {
@@ -265,7 +223,6 @@ async function installBuilders(
265223 if ( err . code !== 'EEXIST' ) throw err ;
266224 }
267225
268- // Then npm install the list of packages we need to install.
269226 output . log (
270227 `Installing ${ plural ( 'Builder' , buildersToAdd . size ) } : ${ Array . from (
271228 buildersToAdd
@@ -314,6 +271,7 @@ async function installBuilders(
314271 // Symlink `@now/build-utils` -> `@vercel/build-utils` to support legacy Builders
315272 const nowScopePath = join ( buildersDir , 'node_modules/@now' ) ;
316273 await mkdirp ( nowScopePath ) ;
274+
317275 try {
318276 await symlink ( '../@vercel/build-utils' , join ( nowScopePath , 'build-utils' ) ) ;
319277 } catch ( err : unknown ) {
0 commit comments