@@ -170,74 +170,6 @@ export function parseYarnClassicDependencies(
170170 return dependencies ;
171171}
172172
173- /**
174- * Parses the output of `yarn list` (modern).
175- *
176- * The expected JSON structure is a single object.
177- * Yarn modern does not provide a path, so the `path` property will be `undefined`.
178- *
179- * ```json
180- * {
181- * "trees": [
182- * { "name": "@angular /cli@18.0.0", "children": [] }
183- * ]
184- * }
185- * ```
186- *
187- * @param stdout The standard output of the command.
188- * @param logger An optional logger instance.
189- * @returns A map of package names to their installed package details.
190- */
191- export function parseYarnModernDependencies (
192- stdout : string ,
193- logger ?: Logger ,
194- ) : Map < string , InstalledPackage > {
195- logger ?. debug ( `Parsing yarn modern dependency list...` ) ;
196- logStdout ( stdout , logger ) ;
197-
198- const dependencies = new Map < string , InstalledPackage > ( ) ;
199- if ( ! stdout ) {
200- logger ?. debug ( ' stdout is empty. No dependencies found.' ) ;
201-
202- return dependencies ;
203- }
204-
205- // Modern yarn `list` command outputs a single JSON object with a `trees` property.
206- // Each line is not a separate JSON object.
207- try {
208- const data = JSON . parse ( stdout ) ;
209- for ( const info of data . trees ) {
210- const name = info . name . split ( '@' ) [ 0 ] ;
211- const version = info . name . split ( '@' ) . pop ( ) ;
212- dependencies . set ( name , {
213- name,
214- version,
215- } ) ;
216- }
217- } catch ( e ) {
218- logger ?. debug (
219- ` Failed to parse as single JSON object: ${ e } . Falling back to line-by-line parsing.` ,
220- ) ;
221- // Fallback for older versions of yarn berry that might still output json lines
222- for ( const json of parseJsonLines ( stdout , logger ) ) {
223- if ( json . type === 'tree' && json . data ?. trees ) {
224- for ( const info of json . data . trees ) {
225- const name = info . name . split ( '@' ) [ 0 ] ;
226- const version = info . name . split ( '@' ) . pop ( ) ;
227- dependencies . set ( name , {
228- name,
229- version,
230- } ) ;
231- }
232- }
233- }
234- }
235-
236- logger ?. debug ( ` Found ${ dependencies . size } dependencies.` ) ;
237-
238- return dependencies ;
239- }
240-
241173/**
242174 * Parses the output of `npm view` or a compatible command to get a package manifest.
243175 * @param stdout The standard output of the command.
@@ -573,3 +505,59 @@ export function parseBunDependencies(
573505
574506 return dependencies ;
575507}
508+
509+ /**
510+ * Parses the output of `yarn info --name-only --json`.
511+ *
512+ * The expected output is a JSON stream (JSONL) of strings.
513+ * Each string represents a package locator.
514+ *
515+ * ```
516+ * "karma@npm:6.4.4"
517+ * "@angular/core@npm:20.3.15"
518+ * ```
519+ *
520+ * @param stdout The standard output of the command.
521+ * @param logger An optional logger instance.
522+ * @returns A map of package names to their installed package details.
523+ */
524+ export function parseYarnModernDependencies (
525+ stdout : string ,
526+ logger ?: Logger ,
527+ ) : Map < string , InstalledPackage > {
528+ logger ?. debug ( 'Parsing Yarn Berry dependency list...' ) ;
529+ logStdout ( stdout , logger ) ;
530+
531+ const dependencies = new Map < string , InstalledPackage > ( ) ;
532+ if ( ! stdout ) {
533+ return dependencies ;
534+ }
535+
536+ for ( const json of parseJsonLines ( stdout , logger ) ) {
537+ if ( typeof json === 'string' ) {
538+ const match = json . match ( / ^ ( @ ? [ ^ @ ] + ) @ ( .+ ) $ / ) ;
539+ if ( match ) {
540+ const name = match [ 1 ] ;
541+ let version = match [ 2 ] ;
542+
543+ // Handle "npm:" prefix
544+ if ( version . startsWith ( 'npm:' ) ) {
545+ version = version . slice ( 4 ) ;
546+ }
547+
548+ // Handle complex locators with embedded version metadata (e.g., "patch:...", "virtual:...")
549+ // Yarn Berry often appends metadata like "::version=x.y.z"
550+ const versionParamMatch = version . match ( / : : v e r s i o n = ( [ ^ & ] + ) / ) ;
551+ if ( versionParamMatch ) {
552+ version = versionParamMatch [ 1 ] ;
553+ }
554+
555+ dependencies . set ( name , { name, version } ) ;
556+ }
557+ }
558+ }
559+
560+ logger ?. debug ( ` Found ${ dependencies . size } dependencies.` ) ;
561+
562+ return dependencies ;
563+ }
0 commit comments