@@ -27,6 +27,7 @@ function setupPacoteMock() {
2727 */
2828async function setupFixtureSymlink ( fixtureDir ) {
2929 const symlinkPath = path . join ( fixtureDir , "package-lock.json" ) ;
30+ await await unlink ( symlinkPath ) . catch ( ( ) => { } ) ;
3031 const targetPath = "package-lock.fixture.json" ;
3132 await symlink ( targetPath , symlinkPath ) ;
3233 return symlinkPath ;
@@ -86,37 +87,51 @@ test("Convert package-lock.json to shrinkwrap", async (t) => {
8687
8788test ( "Workspace paths should be normalized to node_modules format" , async ( t ) => {
8889 const __dirname = import . meta. dirname ;
89-
9090 const cwd = path . join ( __dirname , ".." , "fixture" , "project.a" ) ;
9191 const symlinkPath = await setupFixtureSymlink ( cwd ) ;
9292 t . after ( async ( ) => await unlink ( symlinkPath ) . catch ( ( ) => { } ) ) ;
9393
94- const targetPackageName = "@ui5/cli" ;
95- const shrinkwrapJson = await convertPackageLockToShrinkwrap ( cwd , targetPackageName ) ;
96-
97- // Verify that no package paths contain workspace prefixes like "packages/cli/node_modules/..."
98- const packagePaths = Object . keys ( shrinkwrapJson . packages ) ;
94+ const shrinkwrapJson = await convertPackageLockToShrinkwrap ( cwd , "@ui5/cli" ) ;
95+ const packagePaths = Object . keys ( shrinkwrapJson . packages ) . filter ( ( p ) => p !== "" ) ;
9996
97+ // All paths must start with node_modules/, never with packages/
10098 for ( const packagePath of packagePaths ) {
101- // Skip root package (empty string)
102- if ( packagePath === "" ) continue ;
103-
104- // Assert that no path starts with "packages/"
10599 assert . ok ( ! packagePath . startsWith ( "packages/" ) ,
106- `Package path "${ packagePath } " should not start with "packages/" prefix` ) ;
107-
108- // Assert that non-root paths start with "node_modules/"
100+ `Path "${ packagePath } " should not contain workspace prefix` ) ;
109101 assert . ok ( packagePath . startsWith ( "node_modules/" ) ,
110- `Package path "${ packagePath } " should start with " node_modules/" prefix ` ) ;
102+ `Path "${ packagePath } " should start with node_modules/` ) ;
111103 }
112104
113- // Specifically check a package that would have been under packages/cli/node_modules in the monorepo
114- // The "@npmcli/config" package is a direct dependency that exists in the CLI's node_modules
115- const npmCliConfigPackage = shrinkwrapJson . packages [ "node_modules/@npmcli/config" ] ;
116- assert . ok ( npmCliConfigPackage , "The '@npmcli/config' package should be present at normalized path" ) ;
117- assert . equal ( npmCliConfigPackage . version , "9.0.0" , "@npmcli/config package should have correct version" ) ;
105+ // Verify a CLI dependency was normalized correctly
106+ const npmCliConfig = shrinkwrapJson . packages [ "node_modules/@npmcli/config" ] ;
107+ assert . ok ( npmCliConfig , "@npmcli/config should be at normalized path" ) ;
108+ assert . equal ( npmCliConfig . version , "9.0.0" ) ;
109+
110+ console . log ( `✓ All ${ packagePaths . length } package paths correctly normalized` ) ;
111+ } ) ;
112+
113+ test ( "Version collisions: root packages get priority at top level" , async ( t ) => {
114+ const __dirname = import . meta. dirname ;
115+ const cwd = path . join ( __dirname , ".." , "fixture" , "project.b" ) ;
116+ const symlinkPath = await setupFixtureSymlink ( cwd ) ;
117+ t . after ( async ( ) => await unlink ( symlinkPath ) . catch ( ( ) => { } ) ) ;
118+
119+ const shrinkwrapJson = await convertPackageLockToShrinkwrap ( cwd , "@ui5/cli" ) ;
120+
121+ // ansi-regex: root has v6.2.2, CLI workspace has v5.0.1
122+ // Root version should be at top level, workspace version nested
123+ const rootAnsiRegex = shrinkwrapJson . packages [ "node_modules/ansi-regex" ] ;
124+ assert . equal ( rootAnsiRegex ?. version , "6.2.2" , "Root ansi-regex at top level" ) ;
125+
126+ const cliAnsiRegex = shrinkwrapJson . packages [ "node_modules/@ui5/cli/node_modules/ansi-regex" ] ;
127+ assert . equal ( cliAnsiRegex ?. version , "5.0.1" , "Workspace ansi-regex nested under @ui5/cli" ) ;
128+
129+ // Verify root version satisfies dependents
130+ const stripAnsi = shrinkwrapJson . packages [ "node_modules/strip-ansi" ] ;
131+ assert . equal ( stripAnsi . dependencies [ "ansi-regex" ] , "^6.0.1" ) ;
132+ assert . ok ( rootAnsiRegex . version . startsWith ( "6." ) , "Root v6.2.2 satisfies ^6.0.1" ) ;
118133
119- console . log ( `✓ All ${ packagePaths . length - 1 } package paths correctly normalized` ) ;
134+ console . log ( "✓ Root package prioritized at top level, workspace version nested" ) ;
120135} ) ;
121136
122137test ( "Compare generated shrinkwrap with expected result: package.a" , async ( t ) => {
0 commit comments