diff --git a/apps/lockfile-explorer-web/src/parsing/readLockfile.ts b/apps/lockfile-explorer-web/src/parsing/readLockfile.ts index fbc270a80e..ce78139f67 100644 --- a/apps/lockfile-explorer-web/src/parsing/readLockfile.ts +++ b/apps/lockfile-explorer-web/src/parsing/readLockfile.ts @@ -139,21 +139,17 @@ export function generateLockfileGraph( for (const [dependencyKey, dependencyValue] of Object.entries(lockfile.packages)) { // const normalizedPath = new Path(dependencyKey).makeAbsolute('/').toString(); - let packageDepKey = dependencyKey; - if (pnpmLockfileVersion === PnpmLockfileVersion.V6) { - packageDepKey = dependencyKey.replace('@', '/'); - } - const currEntry = new LockfileEntry({ // entryId: normalizedPath, - rawEntryId: packageDepKey, + rawEntryId: dependencyKey, kind: LockfileEntryFilter.Package, - rawYamlData: dependencyValue + rawYamlData: dependencyValue, + subspaceName }); allPackages.push(currEntry); allEntries.push(currEntry); - allEntriesById[packageDepKey] = currEntry; + allEntriesById[dependencyKey] = currEntry; } } @@ -171,9 +167,11 @@ export function generateLockfileGraph( dependency.resolvedEntry = matchedEntry; matchedEntry.referrers.push(entry); } else { - // Local package - // eslint-disable-next-line no-console - console.error('Could not resolve dependency entryId: ', dependency.entryId, dependency); + if (dependency.entryId.startsWith('/')) { + // Local package + // eslint-disable-next-line no-console + console.error('Could not resolve dependency entryId: ', dependency.entryId, dependency); + } } } } diff --git a/apps/lockfile-explorer/package.json b/apps/lockfile-explorer/package.json index 054bebf9ed..a4009eee1d 100644 --- a/apps/lockfile-explorer/package.json +++ b/apps/lockfile-explorer/package.json @@ -45,22 +45,24 @@ } }, "devDependencies": { - "@microsoft/rush-lib": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/lockfile-explorer-web": "workspace:*", "@types/cors": "~2.8.12", "@types/express": "4.17.21", "@types/js-yaml": "3.12.1", "@types/update-notifier": "~6.0.1", - "local-node-rig": "workspace:*" + "local-node-rig": "workspace:*", + "@pnpm/lockfile-types": "~6.0.0" }, "dependencies": { + "@microsoft/rush-lib": "workspace:*", "@rushstack/node-core-library": "workspace:*", "@rushstack/terminal": "workspace:*", "cors": "~2.8.5", "express": "4.19.2", "js-yaml": "~3.13.1", "open": "~8.4.0", - "update-notifier": "~5.1.0" + "update-notifier": "~5.1.0", + "@pnpm/dependency-path": "~2.1.2" } } diff --git a/apps/lockfile-explorer/src/start.ts b/apps/lockfile-explorer/src/start.ts index 3cbe3b0c8c..205bab99aa 100644 --- a/apps/lockfile-explorer/src/start.ts +++ b/apps/lockfile-explorer/src/start.ts @@ -11,7 +11,9 @@ import { AlreadyReportedError } from '@rushstack/node-core-library'; import { FileSystem, type IPackageJson, JsonFile, PackageJsonLookup } from '@rushstack/node-core-library'; import type { IAppContext } from '@rushstack/lockfile-explorer-web/lib/AppContext'; import { Colorize } from '@rushstack/terminal'; +import type { Lockfile } from '@pnpm/lockfile-types'; +import { convertLockfileV6DepPathToV5DepPath } from './utils'; import { init } from './init'; import type { IAppState } from './state'; import { type ICommandLine, parseCommandLine } from './commandLine'; @@ -103,7 +105,34 @@ function startApp(debugMode: boolean): void { app.get('/api/lockfile', async (req: express.Request, res: express.Response) => { const pnpmLockfileText: string = await FileSystem.readFileAsync(appState.pnpmLockfileLocation); - const doc = yaml.load(pnpmLockfileText); + const doc = yaml.load(pnpmLockfileText) as Lockfile; + const { packages, lockfileVersion } = doc; + + let shrinkwrapFileMajorVersion: number; + if (typeof lockfileVersion === 'string') { + const isDotIncluded: boolean = lockfileVersion.includes('.'); + shrinkwrapFileMajorVersion = parseInt( + lockfileVersion.substring(0, isDotIncluded ? lockfileVersion.indexOf('.') : undefined), + 10 + ); + } else if (typeof lockfileVersion === 'number') { + shrinkwrapFileMajorVersion = Math.floor(lockfileVersion); + } else { + shrinkwrapFileMajorVersion = 0; + } + + if (shrinkwrapFileMajorVersion < 5 || shrinkwrapFileMajorVersion > 6) { + throw new Error('The current lockfile version is not supported.'); + } + + if (packages && shrinkwrapFileMajorVersion === 6) { + const updatedPackages: Lockfile['packages'] = {}; + for (const [dependencyPath, dependency] of Object.entries(packages)) { + updatedPackages[convertLockfileV6DepPathToV5DepPath(dependencyPath)] = dependency; + } + doc.packages = updatedPackages; + } + res.send({ doc, subspaceName diff --git a/apps/lockfile-explorer/src/utils.ts b/apps/lockfile-explorer/src/utils.ts new file mode 100644 index 0000000000..9f5c5f60af --- /dev/null +++ b/apps/lockfile-explorer/src/utils.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as dp from '@pnpm/dependency-path'; + +export function convertLockfileV6DepPathToV5DepPath(newDepPath: string): string { + if (!newDepPath.includes('@', 2) || newDepPath.startsWith('file:')) return newDepPath; + const index = newDepPath.indexOf('@', newDepPath.indexOf('/@') + 2); + if (newDepPath.includes('(') && index > dp.indexOfPeersSuffix(newDepPath)) return newDepPath; + return `${newDepPath.substring(0, index)}/${newDepPath.substring(index + 1)}`; +} diff --git a/common/changes/@microsoft/rush/main_2024-05-14-23-45.json b/common/changes/@microsoft/rush/main_2024-05-14-23-45.json new file mode 100644 index 0000000000..fbabe34c5b --- /dev/null +++ b/common/changes/@microsoft/rush/main_2024-05-14-23-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Optimize the judgment of shrinkwrap file major version.", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/common/changes/@rushstack/lockfile-explorer/main_2024-05-11-08-08.json b/common/changes/@rushstack/lockfile-explorer/main_2024-05-11-08-08.json new file mode 100644 index 0000000000..67b13d9f46 --- /dev/null +++ b/common/changes/@rushstack/lockfile-explorer/main_2024-05-11-08-08.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/lockfile-explorer", + "comment": "Fix some issues when parsing certain lockfile syntaxes", + "type": "patch" + } + ], + "packageName": "@rushstack/lockfile-explorer" +} \ No newline at end of file diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index 9fb98be1e5..389653fc07 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -94,6 +94,10 @@ "name": "@pnpm/link-bins", "allowedCategories": [ "libraries" ] }, + { + "name": "@pnpm/lockfile-types", + "allowedCategories": [ "libraries" ] + }, { "name": "@pnpm/logger", "allowedCategories": [ "libraries" ] diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 463addbc9b..337a6b8635 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -189,6 +189,12 @@ importers: ../../../apps/lockfile-explorer: dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@pnpm/dependency-path': + specifier: ~2.1.2 + version: 2.1.8 '@rushstack/node-core-library': specifier: workspace:* version: link:../../libraries/node-core-library @@ -211,9 +217,9 @@ importers: specifier: ~5.1.0 version: 5.1.0 devDependencies: - '@microsoft/rush-lib': - specifier: workspace:* - version: link:../../libraries/rush-lib + '@pnpm/lockfile-types': + specifier: ~6.0.0 + version: 6.0.0 '@rushstack/heft': specifier: workspace:* version: link:../heft @@ -1847,7 +1853,7 @@ importers: version: 29.5.12 '@types/node': specifier: ts4.9 - version: 20.12.11 + version: 20.12.12 eslint: specifier: ~8.57.0 version: 8.57.0 @@ -9249,7 +9255,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 17.0.41 + '@types/node': 20.12.12 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -9529,6 +9535,13 @@ packages: ramda: 0.27.2 dev: false + /@pnpm/lockfile-types@6.0.0: + resolution: {integrity: sha512-a4/ULIPLZIIq8Qmi2HEoFgRTtEouGU5RNhuGDxnSmkxu1BjlNMNjLJeEI5jzMZCGOjBoML+AirY/XOO3bcEQ/w==} + engines: {node: '>=18.12'} + dependencies: + '@pnpm/types': 10.0.0 + dev: true + /@pnpm/logger@4.0.0: resolution: {integrity: sha512-SIShw+k556e7S7tLZFVSIHjCdiVog1qWzcKW2RbLEHPItdisAFVNIe34kYd9fMSswTlSRLS/qRjw3ZblzWmJ9Q==} engines: {node: '>=12.17'} @@ -9581,6 +9594,11 @@ packages: strip-bom: 4.0.0 dev: false + /@pnpm/types@10.0.0: + resolution: {integrity: sha512-P608MRTOExt5BkIN2hsrb/ycEchwaPW/x80ujJUAqxKZSXNVAOrlEu3KJ+2+jTCunyWmo/EcE01ZdwCw8jgVrQ==} + engines: {node: '>=18.12'} + dev: true + /@pnpm/types@6.4.0: resolution: {integrity: sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==} engines: {node: '>=10.16'} @@ -12770,11 +12788,10 @@ packages: dependencies: undici-types: 5.26.5 - /@types/node@20.12.11: - resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==} + /@types/node@20.12.12: + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} dependencies: undici-types: 5.26.5 - dev: true /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -20862,7 +20879,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 17.0.41 + '@types/node': 20.12.12 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 diff --git a/common/config/subspaces/default/repo-state.json b/common/config/subspaces/default/repo-state.json index 32204326ea..a011dcdc51 100644 --- a/common/config/subspaces/default/repo-state.json +++ b/common/config/subspaces/default/repo-state.json @@ -1,5 +1,5 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "4b7734088f9537c4644d58706e7a1e2cbb41b5d3", + "pnpmShrinkwrapHash": "fdb1015454ffa0bac21eeff77ab0314887083c7e", "preferredVersionsHash": "ce857ea0536b894ec8f346aaea08cfd85a5af648" } diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts index 53c93c7257..4338a1220f 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts @@ -273,12 +273,13 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { // Normalize the data const lockfileVersion: string | number | undefined = shrinkwrapJson.lockfileVersion; if (typeof lockfileVersion === 'string') { + const isDotIncluded: boolean = lockfileVersion.includes('.'); this.shrinkwrapFileMajorVersion = parseInt( - lockfileVersion.substring(0, lockfileVersion.indexOf('.')), + lockfileVersion.substring(0, isDotIncluded ? lockfileVersion.indexOf('.') : undefined), 10 ); } else if (typeof lockfileVersion === 'number') { - this.shrinkwrapFileMajorVersion = lockfileVersion; + this.shrinkwrapFileMajorVersion = Math.floor(lockfileVersion); } else { this.shrinkwrapFileMajorVersion = 0; }