@@ -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.
@@ -575,3 +507,59 @@ export function parseBunDependencies(
575507
576508 return dependencies ;
577509}
510+
511+ /**
512+ * Parses the output of `yarn info --name-only --json`.
513+ *
514+ * The expected output is a JSON stream (JSONL) of strings.
515+ * Each string represents a package locator.
516+ *
517+ * ```
518+ * "karma@npm:6.4.4"
519+ * "@angular/core@npm:20.3.15"
520+ * ```
521+ *
522+ * @param stdout The standard output of the command.
523+ * @param logger An optional logger instance.
524+ * @returns A map of package names to their installed package details.
525+ */
526+ export function parseYarnModernDependencies (
527+ stdout : string ,
528+ logger ?: Logger ,
529+ ) : Map < string , InstalledPackage > {
530+ logger ?. debug ( 'Parsing Yarn Berry dependency list...' ) ;
531+ logStdout ( stdout , logger ) ;
532+
533+ const dependencies = new Map < string , InstalledPackage > ( ) ;
534+ if ( ! stdout ) {
535+ return dependencies ;
536+ }
537+
538+ for ( const json of parseJsonLines ( stdout , logger ) ) {
539+ if ( typeof json === 'string' ) {
540+ const match = json . match ( / ^ ( @ ? [ ^ @ ] + ) @ ( .+ ) $ / ) ;
541+ if ( match ) {
542+ const name = match [ 1 ] ;
543+ let version = match [ 2 ] ;
544+
545+ // Handle "npm:" prefix
546+ if ( version . startsWith ( 'npm:' ) ) {
547+ version = version . slice ( 4 ) ;
548+ }
549+
550+ // Handle complex locators with embedded version metadata (e.g., "patch:...", "virtual:...")
551+ // Yarn Berry often appends metadata like "::version=x.y.z"
552+ const versionParamMatch = version . match ( / : : v e r s i o n = ( [ ^ & ] + ) / ) ;
553+ if ( versionParamMatch ) {
554+ version = versionParamMatch [ 1 ] ;
555+ }
556+
557+ dependencies . set ( name , { name, version } ) ;
558+ }
559+ }
560+ }
561+
562+ logger ?. debug ( ` Found ${ dependencies . size } dependencies.` ) ;
563+
564+ return dependencies ;
565+ }
0 commit comments