@@ -48,7 +48,8 @@ export default async function convertPackageLockToShrinkwrap(workspaceRootDir, t
4848 path : workspaceRootDir ,
4949 } ) ;
5050 const tree = await arb . loadVirtual ( ) ;
51- const cliNode = Array . from ( tree . tops ) . find ( ( node ) => node . packageName === targetPackageName ) ;
51+ const tops = Array . from ( tree . tops . values ( ) ) ;
52+ const cliNode = tops . find ( ( node ) => node . packageName === targetPackageName ) ;
5253 if ( ! cliNode ) {
5354 throw new Error ( `Target package "${ targetPackageName } " not found in workspace` ) ;
5455 }
@@ -70,7 +71,10 @@ export default async function convertPackageLockToShrinkwrap(workspaceRootDir, t
7071 if ( extractedPackages [ packageLoc ] ) {
7172 throw new Error ( `Duplicate root package entry for "${ targetPackageName } "` ) ;
7273 }
73- } else if ( ! pkg . resolved ) {
74+ } else {
75+ packageLoc = normalizePackageLocation ( packageLoc , node , targetPackageName , tree . packageName ) ;
76+ }
77+ if ( packageLoc !== "" && ! pkg . resolved ) {
7478 // For all but the root package, ensure that "resolved" and "integrity" fields are present
7579 // These are always missing for locally linked packages, but sometimes also for others (e.g. if installed
7680 // from local cache)
@@ -100,6 +104,31 @@ export default async function convertPackageLockToShrinkwrap(workspaceRootDir, t
100104 return shrinkwrap ;
101105}
102106
107+ /**
108+ * Normalize package locations from workspace-specific paths to standard npm paths.
109+ * Examples (assuming @ui5/cli is the targetPackageName):
110+ * - packages/cli/node_modules/foo -> node_modules/foo
111+ * - packages/fs/node_modules/bar -> node_modules/@ui5/fs/node_modules/bar
112+ *
113+ * @param {string } location - Package location from arborist
114+ * @param {object } node - Package node from arborist
115+ * @param {string } targetPackageName - Target package name for shrinkwrap file
116+ * @param {string } rootPackageName - Root / workspace package name
117+ * @returns {string } - Normalized location for npm-shrinkwrap.json
118+ */
119+ function normalizePackageLocation ( location , node , targetPackageName , rootPackageName ) {
120+ const topPackageName = node . top . packageName ;
121+ if ( topPackageName === targetPackageName ) {
122+ // Remove location for packages within target package (e.g. @ui5/cli)
123+ return location . substring ( node . top . location . length + 1 ) ;
124+ } else if ( topPackageName !== rootPackageName ) {
125+ // Add package within node_modules of actual package name (e.g. @ui5/fs)
126+ return `node_modules/${ topPackageName } /${ location . substring ( node . top . location . length + 1 ) } ` ;
127+ }
128+ // If it's already within the root workspace package, keep as-is
129+ return location ;
130+ }
131+
103132function collectDependencies ( node , relevantPackageLocations ) {
104133 if ( relevantPackageLocations . has ( node . location ) ) {
105134 // Already processed
0 commit comments