diff --git a/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx b/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx index 8b0fb3769e1..eec60868444 100644 --- a/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx @@ -172,7 +172,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { Selected Dependency:{' '} - {inspectDependency.name}: {inspectDependency.version} + {inspectDependency.name}: {inspectDependency.versionPath}
@@ -180,11 +180,11 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { package.json spec:{' '} - {inspectDependency.dependencyType === LfxDependencyKind.Peer + {inspectDependency.dependencyKind === LfxDependencyKind.Peer ? `"${inspectDependency.peerDependencyMeta.version}" ${ inspectDependency.peerDependencyMeta.optional ? 'Optional' : 'Required' } Peer` - : inspectDependency.version} + : inspectDependency.versionPath}
@@ -204,7 +204,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { const renderPeerDependencies = (): JSX.Element | ReactNull => { if (!selectedEntry) return ReactNull; - const peerDeps = selectedEntry.dependencies.filter((d) => d.dependencyType === LfxDependencyKind.Peer); + const peerDeps = selectedEntry.dependencies.filter((d) => d.dependencyKind === LfxDependencyKind.Peer); if (!peerDeps.length) { return (
@@ -212,7 +212,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
); } - if (!inspectDependency || inspectDependency.dependencyType !== LfxDependencyKind.Peer) { + if (!inspectDependency || inspectDependency.dependencyKind !== LfxDependencyKind.Peer) { return (
Select a peer dependency to view its influencers @@ -337,14 +337,14 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { > Name: {dependency.name}{' '} - {dependency.dependencyType === LfxDependencyKind.Peer + {dependency.dependencyKind === LfxDependencyKind.Peer ? `${ dependency.peerDependencyMeta.optional ? '(Optional)' : '(Non-optional)' } Peer Dependency` : ''}
- Version: {dependency.version} + Version: {dependency.versionPath} Entry ID: {dependency.entryId}
diff --git a/apps/lockfile-explorer-web/src/packlets/lfx-shared/IJsonLfxGraph.ts b/apps/lockfile-explorer-web/src/packlets/lfx-shared/IJsonLfxGraph.ts index 7632e332f8f..35baa33daca 100644 --- a/apps/lockfile-explorer-web/src/packlets/lfx-shared/IJsonLfxGraph.ts +++ b/apps/lockfile-explorer-web/src/packlets/lfx-shared/IJsonLfxGraph.ts @@ -18,13 +18,13 @@ export interface IJsonPeerDependencyMeta { export interface IJsonLfxDependency { name: string; - version: string; + versionPath: string; entryId: string; - dependencyType: LfxDependencyKind; + originalSpecifier: string; + dependencyKind: LfxDependencyKind; + peerDependencyMeta: IJsonPeerDependencyMeta; resolvedEntryJsonId?: number; - - peerDependencyMeta: IJsonPeerDependencyMeta; } export enum LfxDependencyKind { diff --git a/apps/lockfile-explorer-web/src/packlets/lfx-shared/LfxGraph.ts b/apps/lockfile-explorer-web/src/packlets/lfx-shared/LfxGraph.ts index 11ce9f58c98..a45371b7ba3 100644 --- a/apps/lockfile-explorer-web/src/packlets/lfx-shared/LfxGraph.ts +++ b/apps/lockfile-explorer-web/src/packlets/lfx-shared/LfxGraph.ts @@ -6,39 +6,75 @@ import type { IJsonLfxWorkspace } from './IJsonLfxWorkspace'; export interface ILfxGraphDependencyOptions { name: string; - version: string; - dependencyType: LfxDependencyKind; - containingEntry: LfxGraphEntry; - peerDependencyMeta: IJsonPeerDependencyMeta; + versionPath: string; + entryId: string; + + originalSpecifier: string; + dependencyKind: LfxDependencyKind; + peerDependencyMeta: IJsonPeerDependencyMeta; + + containingEntry: LfxGraphEntry; } /** - * Represents a dependency listed under a LockfileEntry - * - * @remarks - * Each dependency listed under a package in the lockfile should have a separate entry. These Dependencies - * will link to the "containingEntry", which is the LockfileEntry that specified this dependency. - * The "resolvedEntry" field is the corresponding LockfileEntry for this dependency, as all dependencies also have - * their own entries in the pnpm lockfile. + * Represents an graph edge, which is an exact dependency version obtained from the lockfile. */ export class LfxGraphDependency { + /** + * The referenced package name. + * Example: `@scope/package-name` + */ public readonly name: string; - public readonly version: string; - public readonly dependencyType: LfxDependencyKind; - public readonly containingEntry: LfxGraphEntry; + + /** + * The lockfile's raw string that either indicates an external reference such as `link:../target-folder`, + * or else can be combined with the `name` field to construct an `entryId` found in the lockfile. + * The exact syntax varies between lockfile file format versions. + * + * Example: `link:../target-folder` + * + * Example: `1.0.0` + * + * Example: `1.0.0_@rushstack+m@1.0.0` (version 5.4) + * Example: `1.0.0(@rushstack/m@1.0.0)` (version 6.0 and 9.0) + */ + public readonly versionPath: string; + + /** + * If this dependency refers to an entry in the lockfile, this field should match a corresponding + * {@link LfxGraphEntry.entryId} and `resolvedEntry` will be defined (unless the loader encountered an error). + * + * For external references such as `link:../target-folder`, the `entryId` is the empty string. + */ public readonly entryId: string; + + /** + * The lockfile sometimes records the original SemVer specifier that was used to choose the versionPath, + * usually either because it can change (e.g. a workspace project's dependencies) or because it's a peer dependency + * that affects graph relationships beyond the current node. If not, then `originalSpecifier` will be the + * empty string. + * + * @remarks + * Because this field is only available for certain dependencies, it is generally less useful than specifiers + * obtained from the package.json files. + */ + public readonly originalSpecifier: string; + public readonly dependencyKind: LfxDependencyKind; public readonly peerDependencyMeta: IJsonPeerDependencyMeta; + public readonly containingEntry: LfxGraphEntry; public resolvedEntry: LfxGraphEntry | undefined = undefined; public constructor(options: ILfxGraphDependencyOptions) { this.name = options.name; - this.version = options.version; - this.dependencyType = options.dependencyType; - this.containingEntry = options.containingEntry; + this.versionPath = options.versionPath; this.entryId = options.entryId; + this.originalSpecifier = options.originalSpecifier; + this.dependencyKind = options.dependencyKind; this.peerDependencyMeta = options.peerDependencyMeta; + + this.containingEntry = options.containingEntry; } } @@ -67,14 +103,19 @@ export class LfxGraphEntry { public readonly kind: LfxGraphEntryKind; /** - * A unique (human-readable) identifier for this lockfile entry. For projects, this is just - * `Project:` + the package json path for this project. + * A unique identifier for this lockfile entry, based on `rawEntryId` but adjusted to be unique for both + * project and external package entries. */ public readonly entryId: string; /** * The unique identifier assigned to this project/package in the lockfile. * e.g. `/@emotion/core/10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q:` + * + * @remarks + * In the `pnpm-lock.yaml` file, "importers" (workspace projects) and "packages" (external packages) + * are tracked separately, so it's not required for their keys to be unique. `entryId` solves this problem + * by adding a `project:` prefix for importers. */ public readonly rawEntryId: string; diff --git a/apps/lockfile-explorer-web/src/packlets/lfx-shared/lfxGraphSerializer.ts b/apps/lockfile-explorer-web/src/packlets/lfx-shared/lfxGraphSerializer.ts index b175dbd2080..f2f4be485e9 100644 --- a/apps/lockfile-explorer-web/src/packlets/lfx-shared/lfxGraphSerializer.ts +++ b/apps/lockfile-explorer-web/src/packlets/lfx-shared/lfxGraphSerializer.ts @@ -51,9 +51,10 @@ export function serializeToJson(graph: LfxGraph): IJsonLfxGraph { for (const dependency of entry.dependencies) { const jsonLfxDependency: IJsonLfxDependency = { name: dependency.name, - version: dependency.version, + versionPath: dependency.versionPath, entryId: dependency.entryId, - dependencyType: dependency.dependencyType, + originalSpecifier: dependency.originalSpecifier, + dependencyKind: dependency.dependencyKind, peerDependencyMeta: { name: dependency.peerDependencyMeta.name, version: dependency.peerDependencyMeta.version, @@ -111,15 +112,16 @@ export function deserializeFromJson(jsonLfxGraph: IJsonLfxGraph): LfxGraph { for (const jsonLfxDependency of jsonLfxEntry.dependencies) { const dependency: LfxGraphDependency = new LfxGraphDependency({ name: jsonLfxDependency.name, - version: jsonLfxDependency.version, - dependencyType: jsonLfxDependency.dependencyType, - containingEntry: entry, + versionPath: jsonLfxDependency.versionPath, entryId: jsonLfxDependency.entryId, + originalSpecifier: jsonLfxDependency.originalSpecifier, + dependencyKind: jsonLfxDependency.dependencyKind, peerDependencyMeta: { name: jsonLfxDependency.peerDependencyMeta.name, version: jsonLfxDependency.peerDependencyMeta.version, optional: jsonLfxDependency.peerDependencyMeta.optional - } + }, + containingEntry: entry }); if (jsonLfxDependency.resolvedEntryJsonId) { diff --git a/apps/lockfile-explorer/.vscode/launch.json b/apps/lockfile-explorer/.vscode/launch.json index 2ee133e0e98..34810e71664 100644 --- a/apps/lockfile-explorer/.vscode/launch.json +++ b/apps/lockfile-explorer/.vscode/launch.json @@ -14,13 +14,20 @@ "console": "integratedTerminal", "sourceMaps": true }, - { + { "type": "node", "request": "launch", "name": "Single Jest test", "program": "${workspaceFolder}/node_modules/@rushstack/heft/lib/start.js", "cwd": "${workspaceFolder}", - "args": ["--debug", "test", "--clean", "-u", "--test-path-pattern", "lfxGraph-website-sample-1-v6.0.test"], + "args": [ + "--debug", + "test", + "--clean", + "-u", + "--test-path-pattern", + "lfxGraph-website-sample-1-v5.4.test" + ], "console": "integratedTerminal", "sourceMaps": true }, diff --git a/apps/lockfile-explorer/src/graph/lfxGraphLoader.ts b/apps/lockfile-explorer/src/graph/lfxGraphLoader.ts index ba421861c21..ee493855030 100644 --- a/apps/lockfile-explorer/src/graph/lfxGraphLoader.ts +++ b/apps/lockfile-explorer/src/graph/lfxGraphLoader.ts @@ -22,7 +22,8 @@ type PeerDependenciesMeta = lockfileTypes.LockfilePackageInfo['peerDependenciesM function createPackageLockfileDependency(options: { name: string; - version: string; + versionPath: string; + originalSpecifier: string; kind: LfxDependencyKind; containingEntry: LfxGraphEntry; peerDependenciesMeta?: PeerDependenciesMeta; @@ -31,8 +32,9 @@ function createPackageLockfileDependency(options: { }): LfxGraphDependency { const { name, - version, - kind: dependencyType, + versionPath, + originalSpecifier, + kind: dependencyKind, containingEntry, peerDependenciesMeta, pnpmLockfileVersion @@ -40,15 +42,16 @@ function createPackageLockfileDependency(options: { const result: ILfxGraphDependencyOptions = { name, - version, - dependencyType, - containingEntry, + versionPath, entryId: '', - peerDependencyMeta: {} + originalSpecifier, + dependencyKind, + peerDependencyMeta: {}, + containingEntry }; - if (version.startsWith('link:')) { - const relativePath: string = version.substring('link:'.length); + if (versionPath.startsWith('link:')) { + const relativePath: string = versionPath.substring('link:'.length); if (containingEntry.kind === LfxGraphEntryKind.Project) { // TODO: Here we assume it's a "workspace:" link and try to resolve it to another workspace project, @@ -63,80 +66,90 @@ function createPackageLockfileDependency(options: { // This could be a link to anywhere on the local computer, so we don't expect it to have a lockfile entry result.entryId = ''; } - } else if (result.version.startsWith('/')) { - result.entryId = version; - } else if (result.dependencyType === LfxDependencyKind.Peer) { - result.peerDependencyMeta = { - name: result.name, - version: version, - optional: peerDependenciesMeta?.[result.name] ? peerDependenciesMeta[result.name].optional : false - }; - result.entryId = 'Peer: ' + result.name; + } else if (result.versionPath.startsWith('/')) { + result.entryId = versionPath; } else { // Version 5.4: /@rushstack/m/1.0.0: // Version 6.0: /@rushstack/m@1.0.0: + // Version 9.0: @rushstack/m@1.0.0: // // Version 5.4: /@rushstack/j/1.0.0_@rushstack+n@2.0.0 // Version 6.0: /@rushstack/j@1.0.0(@rushstack/n@2.0.0) - const versionDelimiter: string = pnpmLockfileVersion === 54 ? '/' : '@'; - result.entryId = '/' + result.name + versionDelimiter + result.version; + // Version 9.0: @rushstack/j@1.0.0(@rushstack/n@2.0.0) + const versionDelimiter: string = pnpmLockfileVersion < 60 ? '/' : '@'; + result.entryId = + (pnpmLockfileVersion < 90 ? '/' : '') + result.name + versionDelimiter + result.versionPath; + } + + if (result.dependencyKind === LfxDependencyKind.Peer) { + result.peerDependencyMeta = { + name: result.name, + version: versionPath, + optional: peerDependenciesMeta?.[result.name] ? peerDependenciesMeta[result.name].optional : false + }; } return new LfxGraphDependency(result); } -// v5.4 used this to parse projects ("importers") also -function parsePackageDependencies( - dependencies: LfxGraphDependency[], - lockfileEntry: LfxGraphEntry, - either: lockfileTypes.ProjectSnapshot | lockfileTypes.PackageSnapshot, - pnpmLockfileVersion: PnpmLockfileVersion, - workspace: IJsonLfxWorkspace -): void { +function parsePackageDependencies(options: { + dependencies: LfxGraphDependency[]; + lockfileEntry: LfxGraphEntry; + /** + * Used to obtain versionPath exact references. + */ + mainEntry: lockfileTypes.LockfilePackageSnapshot; + /** + * Used to obtain informational version ranges. + */ + specifierEntry: lockfileTypes.LockfilePackageInfo | undefined; + pnpmLockfileVersion: PnpmLockfileVersion; + workspace: IJsonLfxWorkspace; +}): void { + const { dependencies, lockfileEntry, mainEntry, specifierEntry, pnpmLockfileVersion, workspace } = options; + const node: Partial = - either as unknown as Partial; + mainEntry as unknown as Partial; + + function createDependency(kind: LfxDependencyKind, packageName: string, versionPath: string): void { + let originalSpecifier: string | undefined = undefined; + + if (specifierEntry && specifierEntry.peerDependencies) { + originalSpecifier = specifierEntry.peerDependencies[packageName]; + if (originalSpecifier) { + kind = LfxDependencyKind.Peer; + } + } + + dependencies.push( + createPackageLockfileDependency({ + kind, + name: packageName, + versionPath, + originalSpecifier: originalSpecifier ?? '', + containingEntry: lockfileEntry, + peerDependenciesMeta: specifierEntry?.peerDependenciesMeta, + pnpmLockfileVersion, + workspace + }) + ); + } + if (node.dependencies) { - for (const [packageName, version] of Object.entries(node.dependencies)) { - dependencies.push( - createPackageLockfileDependency({ - kind: LfxDependencyKind.Regular, - name: packageName, - version: version, - containingEntry: lockfileEntry, - pnpmLockfileVersion, - workspace - }) - ); + for (const [packageName, versionPath] of Object.entries(node.dependencies)) { + createDependency(LfxDependencyKind.Regular, packageName, versionPath); } } - if (node.devDependencies) { - for (const [packageName, version] of Object.entries(node.devDependencies)) { - dependencies.push( - createPackageLockfileDependency({ - kind: LfxDependencyKind.Dev, - name: packageName, - version: version, - containingEntry: lockfileEntry, - pnpmLockfileVersion, - workspace - }) - ); + if (node.optionalDependencies) { + for (const [packageName, versionPath] of Object.entries(node.optionalDependencies)) { + createDependency(LfxDependencyKind.Regular, packageName, versionPath); } } - if (node.peerDependencies) { - for (const [packageName, version] of Object.entries(node.peerDependencies)) { - dependencies.push( - createPackageLockfileDependency({ - kind: LfxDependencyKind.Peer, - name: packageName, - version: version, - containingEntry: lockfileEntry, - peerDependenciesMeta: node.peerDependenciesMeta, - pnpmLockfileVersion, - workspace - }) - ); + if (node.devDependencies) { + for (const [packageName, versionPath] of Object.entries(node.devDependencies)) { + createDependency(LfxDependencyKind.Dev, packageName, versionPath); } } + if (node.transitivePeerDependencies) { for (const dep of node.transitivePeerDependencies) { lockfileEntry.transitivePeerDependencies.add(dep); @@ -144,6 +157,58 @@ function parsePackageDependencies( } } +function parseProjectDependencies54(options: { + dependencies: LfxGraphDependency[]; + lockfileEntry: LfxGraphEntry; + /** + * Used to obtain versionPath exact references and informational version ranges + */ + mainEntry: lockfileTypes.ProjectSnapshot; + pnpmLockfileVersion: PnpmLockfileVersion; + workspace: IJsonLfxWorkspace; +}): void { + const { dependencies, lockfileEntry, mainEntry, pnpmLockfileVersion, workspace } = options; + + const node: Partial = + mainEntry as unknown as Partial; + + function createDependency(kind: LfxDependencyKind, packageName: string, versionPath: string): void { + let originalSpecifier: string | undefined = undefined; + + if (mainEntry.specifiers) { + originalSpecifier = mainEntry.specifiers[packageName]; + } + + dependencies.push( + createPackageLockfileDependency({ + kind, + name: packageName, + versionPath, + originalSpecifier: originalSpecifier ?? '', + containingEntry: lockfileEntry, + pnpmLockfileVersion, + workspace + }) + ); + } + + if (node.dependencies) { + for (const [packageName, versionPath] of Object.entries(node.dependencies)) { + createDependency(LfxDependencyKind.Regular, packageName, versionPath); + } + } + if (node.optionalDependencies) { + for (const [packageName, versionPath] of Object.entries(node.optionalDependencies)) { + createDependency(LfxDependencyKind.Regular, packageName, versionPath); + } + } + if (node.devDependencies) { + for (const [packageName, versionPath] of Object.entries(node.devDependencies)) { + createDependency(LfxDependencyKind.Dev, packageName, versionPath); + } + } +} + function parseProjectDependencies60( dependencies: LfxGraphDependency[], lockfileEntry: LfxGraphEntry, @@ -151,32 +216,37 @@ function parseProjectDependencies60( pnpmLockfileVersion: PnpmLockfileVersion, workspace: IJsonLfxWorkspace ): void { + function createDependency( + kind: LfxDependencyKind, + packageName: string, + specifierAndResolution: lockfileTypes.SpecifierAndResolution + ): void { + dependencies.push( + createPackageLockfileDependency({ + kind, + name: packageName, + versionPath: specifierAndResolution.version, + originalSpecifier: specifierAndResolution.specifier, + containingEntry: lockfileEntry, + pnpmLockfileVersion, + workspace + }) + ); + } + if (snapshot.dependencies) { for (const [packageName, specifierAndResolution] of Object.entries(snapshot.dependencies)) { - dependencies.push( - createPackageLockfileDependency({ - kind: LfxDependencyKind.Regular, - name: packageName, - version: specifierAndResolution.version, - containingEntry: lockfileEntry, - pnpmLockfileVersion, - workspace - }) - ); + createDependency(LfxDependencyKind.Regular, packageName, specifierAndResolution); + } + } + if (snapshot.optionalDependencies) { + for (const [packageName, specifierAndResolution] of Object.entries(snapshot.optionalDependencies)) { + createDependency(LfxDependencyKind.Regular, packageName, specifierAndResolution); } } if (snapshot.devDependencies) { for (const [packageName, specifierAndResolution] of Object.entries(snapshot.devDependencies)) { - dependencies.push( - createPackageLockfileDependency({ - kind: LfxDependencyKind.Dev, - name: packageName, - version: specifierAndResolution.version, - containingEntry: lockfileEntry, - pnpmLockfileVersion, - workspace - }) - ); + createDependency(LfxDependencyKind.Dev, packageName, specifierAndResolution); } } } @@ -228,38 +298,44 @@ function createProjectLockfileEntry(options: { function createPackageLockfileEntry(options: { rawEntryId: string; - rawYamlData: lockfileTypes.PackageSnapshot; workspace: IJsonLfxWorkspace; pnpmLockfileVersion: PnpmLockfileVersion; }): LfxGraphEntry { - const { rawEntryId, rawYamlData, pnpmLockfileVersion, workspace } = options; + const { rawEntryId, pnpmLockfileVersion, workspace } = options; const result: ILfxGraphEntryOptions = { kind: LfxGraphEntryKind.Package, - entryId: '', - rawEntryId: '', + entryId: rawEntryId, + rawEntryId: rawEntryId, packageJsonFolderPath: '', entryPackageName: '', - displayText: '', + displayText: rawEntryId, entryPackageVersion: '', entrySuffix: '' }; - result.rawEntryId = rawEntryId; - // Example: pnpmLockfilePath = 'common/temp/my-subspace/pnpm-lock.yaml' // Example: pnpmLockfileFolder = 'common/temp/my-subspace' const pnpmLockfileFolder: string = workspace.pnpmLockfileFolder; - result.displayText = rawEntryId; + let slashlessRawEntryId: string; - if (!rawEntryId.startsWith('/')) { - throw new Error('Expecting leading "/" in path: ' + JSON.stringify(rawEntryId)); + if (pnpmLockfileVersion >= 90) { + // The leading slash is omitted starting in V9.0 + if (rawEntryId.startsWith('/')) { + throw new Error('Not expecting leading "/" in path: ' + JSON.stringify(rawEntryId)); + } + slashlessRawEntryId = rawEntryId; + } else { + if (!rawEntryId.startsWith('/')) { + throw new Error('Expecting leading "/" in path: ' + JSON.stringify(rawEntryId)); + } + slashlessRawEntryId = rawEntryId.substring(1); } let dotPnpmSubfolder: string; - if (pnpmLockfileVersion === 54) { + if (pnpmLockfileVersion < 60) { const lastSlashIndex: number = rawEntryId.lastIndexOf('/'); if (lastSlashIndex < 0) { throw new Error('Expecting "/" in path: ' + JSON.stringify(rawEntryId)); @@ -292,31 +368,32 @@ function createPackageLockfileEntry(options: { (result.entrySuffix ? `_${result.entrySuffix}` : ''); } else { // Example inputs: - // /@rushstack/eslint-config@3.0.1 - // /@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + // @rushstack/eslint-config@3.0.1 + // @rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) let versionAtSignIndex: number; - if (rawEntryId.startsWith('/@')) { - versionAtSignIndex = rawEntryId.indexOf('@', 2); + if (slashlessRawEntryId.startsWith('@')) { + versionAtSignIndex = slashlessRawEntryId.indexOf('@', 1); } else { - versionAtSignIndex = rawEntryId.indexOf('@', 1); + versionAtSignIndex = slashlessRawEntryId.indexOf('@'); } - const packageName: string = rawEntryId.substring(1, versionAtSignIndex); + + const packageName: string = slashlessRawEntryId.substring(0, versionAtSignIndex); result.entryPackageName = packageName; - const leftParenIndex: number = rawEntryId.indexOf('(', versionAtSignIndex); + const leftParenIndex: number = slashlessRawEntryId.indexOf('(', versionAtSignIndex); if (leftParenIndex < 0) { - const version: string = rawEntryId.substring(versionAtSignIndex + 1); + const version: string = slashlessRawEntryId.substring(versionAtSignIndex + 1); result.entryPackageVersion = version; - // /@rushstack/eslint-config@3.0.1 + // @rushstack/eslint-config@3.0.1 // --> @rushstack/eslint-config 3.0.1 result.displayText = packageName + ' ' + version; } else { - const version: string = rawEntryId.substring(versionAtSignIndex + 1, leftParenIndex); + const version: string = slashlessRawEntryId.substring(versionAtSignIndex + 1, leftParenIndex); result.entryPackageVersion = version; // "(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)" - let suffix: string = rawEntryId.substring(leftParenIndex); + let suffix: string = slashlessRawEntryId.substring(leftParenIndex); // Rewrite to: // "@rushstack/m@1.0.0; @rushstack/n@2.0.0" @@ -325,17 +402,16 @@ function createPackageLockfileEntry(options: { suffix = Text.replaceAll(suffix, ')', ''); result.entrySuffix = suffix; - // /@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + // @rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) // --> @rushstack/l 1.0.0 [@rushstack/m@1.0.0; @rushstack/n@2.0.0] result.displayText = packageName + ' ' + version + ' [' + suffix + ']'; } - // Example: /@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + // Example: @rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) // --> @rushstack+l@1.0.0_@rushstack+m@1.0.0_@rushstack+n@2.0.0 // @rushstack/l 1.0.0 (@rushstack/m@1.0.0)(@rushstack/n@2.0.0) - dotPnpmSubfolder = rawEntryId.substring(1); - dotPnpmSubfolder = Text.replaceAll(dotPnpmSubfolder, '/', '+'); + dotPnpmSubfolder = Text.replaceAll(slashlessRawEntryId, '/', '+'); dotPnpmSubfolder = Text.replaceAll(dotPnpmSubfolder, ')(', '_'); dotPnpmSubfolder = Text.replaceAll(dotPnpmSubfolder, '(', '_'); dotPnpmSubfolder = Text.replaceAll(dotPnpmSubfolder, ')', ''); @@ -351,13 +427,6 @@ function createPackageLockfileEntry(options: { ); const lockfileEntry: LfxGraphEntry = new LfxGraphEntry(result); - parsePackageDependencies( - lockfileEntry.dependencies, - lockfileEntry, - rawYamlData, - pnpmLockfileVersion, - workspace - ); return lockfileEntry; } @@ -381,10 +450,10 @@ export function generateLockfileGraph(lockfileJson: unknown, workspace: IJsonLfx case '6.0': pnpmLockfileVersion = 60; break; - //case '9': - //case '9.0': - // pnpmLockfileVersion = 90; - // break; + case '9': + case '9.0': + pnpmLockfileVersion = 90; + break; default: throw new Error('Unsupported PNPM lockfile version ' + JSON.stringify(lockfile.lockfileVersion)); } @@ -424,17 +493,18 @@ export function generateLockfileGraph(lockfileJson: unknown, workspace: IJsonLfx pnpmLockfileVersion }); - if (pnpmLockfileVersion === 54) { + if (pnpmLockfileVersion < 60) { const lockfile54: lockfileTypes.LockfileObject = lockfileJson as lockfileTypes.LockfileObject; const importerValue: lockfileTypes.ProjectSnapshot = lockfile54.importers[importerKey as pnpmTypes.ProjectId]; - parsePackageDependencies( - importer.dependencies, - importer, - importerValue, + + parseProjectDependencies54({ + dependencies: importer.dependencies, + lockfileEntry: importer, + mainEntry: importerValue, pnpmLockfileVersion, workspace - ); + }); } else { const lockfile60: lockfileTypes.LockfileFile = lockfileJson as lockfileTypes.LockfileFile; if (lockfile60.importers) { @@ -456,21 +526,62 @@ export function generateLockfileGraph(lockfileJson: unknown, workspace: IJsonLfx } } - const allPackages: LfxGraphEntry[] = []; - if (lockfile.packages) { - for (const [dependencyKey, dependencyValue] of Object.entries(lockfile.packages ?? {})) { - // const normalizedPath = new Path(dependencyKey).makeAbsolute('/').toString(); + if (pnpmLockfileVersion < 90) { + if (lockfile.packages) { + for (const [dependencyKey, dependencyValue] of Object.entries(lockfile.packages)) { + const lockfileEntry: LfxGraphEntry = createPackageLockfileEntry({ + rawEntryId: dependencyKey, + workspace, + pnpmLockfileVersion + }); + parsePackageDependencies({ + dependencies: lockfileEntry.dependencies, + lockfileEntry: lockfileEntry, + mainEntry: dependencyValue, + specifierEntry: dependencyValue, + pnpmLockfileVersion, + workspace + }); + allEntries.push(lockfileEntry); + allEntriesById.set(dependencyKey, lockfileEntry); + } + } + } else { + const packagesByKey: Map = new Map(); + if (lockfile.packages) { + for (const [dependencyKey, dependencyValue] of Object.entries(lockfile.packages)) { + packagesByKey.set(dependencyKey, dependencyValue); + } + } - const currEntry: LfxGraphEntry = createPackageLockfileEntry({ - rawEntryId: dependencyKey, - rawYamlData: dependencyValue as lockfileTypes.PackageSnapshot, - workspace, - pnpmLockfileVersion - }); + // In v9.0 format, the dependency graph for non-workspace packages is found under "snapshots" not "packages". + // (The "packages" section now stores other fields that are unrelated to the graph itself.) + const lockfile90: lockfileTypes.LockfileFile = lockfileJson as lockfileTypes.LockfileFile; + if (lockfile90.snapshots) { + for (const [dependencyKey, dependencyValue] of Object.entries(lockfile90.snapshots)) { + const lockfileEntry: LfxGraphEntry = createPackageLockfileEntry({ + rawEntryId: dependencyKey, + workspace, + pnpmLockfileVersion + }); + + // Example: "@scope/my-package@1.0.0" + const packageInfoKey: string = + lockfileEntry.entryPackageName + '@' + lockfileEntry.entryPackageVersion; + const packageInfo: lockfileTypes.LockfilePackageInfo | undefined = packagesByKey.get(packageInfoKey); + + parsePackageDependencies({ + dependencies: lockfileEntry.dependencies, + lockfileEntry, + mainEntry: dependencyValue, + specifierEntry: packageInfo, + pnpmLockfileVersion, + workspace + }); - allPackages.push(currEntry); - allEntries.push(currEntry); - allEntriesById.set(dependencyKey, currEntry); + allEntries.push(lockfileEntry); + allEntriesById.set(lockfileEntry.entryId, lockfileEntry); + } } } @@ -478,7 +589,7 @@ export function generateLockfileGraph(lockfileJson: unknown, workspace: IJsonLfx for (const entry of allEntries) { for (const dependency of entry.dependencies) { // Peer dependencies do not have a matching entry - if (dependency.dependencyType === LfxDependencyKind.Peer) { + if (dependency.dependencyKind === LfxDependencyKind.Peer) { continue; } diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v5.4.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v5.4.test.ts.snap index ff8058f4180..a8af1106857 100644 --- a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v5.4.test.ts.snap +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v5.4.test.ts.snap @@ -3,159 +3,280 @@ exports[`lfxGraph-edge-cases-v5.4 loads a workspace 1`] = ` "entries: - dependencies: - - dependencyType: regular + - dependencyKind: regular + entryId: /mime-db/1.54.0 + name: mime-db + originalSpecifier: ^1.52.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 11 + versionPath: 1.54.0 + - dependencyKind: dev + entryId: /color-string/2.1.2 + name: color-string + originalSpecifier: ^2.1.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: dev-dependencies' + entryId: project:packages/dev-dependencies + entryPackageName: dev-dependencies + entryPackageVersion: '' + entrySuffix: '' + jsonId: 0 + kind: 1 + packageJsonFolderPath: packages/dev-dependencies + rawEntryId: packages/dev-dependencies + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular entryId: /color/5.0.2 name: color + originalSpecifier: ^5.0.2 peerDependencyMeta: {} - resolvedEntryJsonId: 6 - version: 5.0.2 - displayText: 'Project: duplicate (duplicate-1/duplicate)' - entryId: project:duplicate-1/duplicate - entryPackageName: duplicate (duplicate-1/duplicate) + resolvedEntryJsonId: 9 + versionPath: 5.0.2 + displayText: 'Project: duplicate (packages/duplicate-1/duplicate)' + entryId: project:packages/duplicate-1/duplicate + entryPackageName: duplicate (packages/duplicate-1/duplicate) entryPackageVersion: '' entrySuffix: '' - jsonId: 0 + jsonId: 1 kind: 1 - packageJsonFolderPath: duplicate-1/duplicate - rawEntryId: duplicate-1/duplicate + packageJsonFolderPath: packages/duplicate-1/duplicate + rawEntryId: packages/duplicate-1/duplicate referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /color-string/2.1.2 name: color-string + originalSpecifier: ^2.1.2 peerDependencyMeta: {} - resolvedEntryJsonId: 5 - version: 2.1.2 - displayText: 'Project: duplicate (duplicate-2/duplicate)' - entryId: project:duplicate-2/duplicate - entryPackageName: duplicate (duplicate-2/duplicate) + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: duplicate (packages/duplicate-2/duplicate)' + entryId: project:packages/duplicate-2/duplicate + entryPackageName: duplicate (packages/duplicate-2/duplicate) entryPackageVersion: '' entrySuffix: '' - jsonId: 1 + jsonId: 2 kind: 1 - packageJsonFolderPath: duplicate-2/duplicate - rawEntryId: duplicate-2/duplicate + packageJsonFolderPath: packages/duplicate-2/duplicate + rawEntryId: packages/duplicate-2/duplicate referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /has-symbols/1.0.2 name: has-symbols + originalSpecifier: 1.0.2 peerDependencyMeta: {} - resolvedEntryJsonId: 7 - version: 1.0.2 - - dependencyType: regular - entryId: project:link-specifier/target-folder + resolvedEntryJsonId: 10 + versionPath: 1.0.2 + - dependencyKind: regular + entryId: project:packages/link-specifier/linker/packages/link-specifier/target-folder name: target-folder + originalSpecifier: link:packages/link-specifier/target-folder peerDependencyMeta: {} - version: link:../target-folder + versionPath: link:packages/link-specifier/target-folder displayText: 'Project: linker' - entryId: project:link-specifier/linker + entryId: project:packages/link-specifier/linker entryPackageName: linker entryPackageVersion: '' entrySuffix: '' - jsonId: 2 + jsonId: 3 kind: 1 - packageJsonFolderPath: link-specifier/linker - rawEntryId: link-specifier/linker + packageJsonFolderPath: packages/link-specifier/linker + rawEntryId: packages/link-specifier/linker referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular + entryId: /address/1.2.2 + name: address + originalSpecifier: ^1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 5 + versionPath: 1.2.2 + - dependencyKind: regular + entryId: /uri-js/4.4.1 + name: uri-js + originalSpecifier: ^4.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 13 + versionPath: 4.4.1 + displayText: 'Project: pnpmfile-transforms' + entryId: project:packages/pnpmfile-transforms + entryPackageName: pnpmfile-transforms + entryPackageVersion: '' + entrySuffix: '' + jsonId: 4 + kind: 1 + packageJsonFolderPath: packages/pnpmfile-transforms + rawEntryId: packages/pnpmfile-transforms + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: [] + displayText: address 1.2.2 + entryId: /address/1.2.2 + entryPackageName: address + entryPackageVersion: 1.2.2 + entrySuffix: '' + jsonId: 5 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/address@1.2.2/node_modules/address + rawEntryId: /address/1.2.2 + referrerJsonIds: + - 4 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular entryId: /color-name/2.0.2 name: color-name + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 4 - version: 2.0.2 + resolvedEntryJsonId: 7 + versionPath: 2.0.2 displayText: color-convert 3.1.2 - entryId: '' + entryId: /color-convert/3.1.2 entryPackageName: color-convert entryPackageVersion: 3.1.2 entrySuffix: '' - jsonId: 3 + jsonId: 6 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-convert@3.1.2/node_modules/color-convert rawEntryId: /color-convert/3.1.2 referrerJsonIds: - - 6 + - 9 transitivePeerDependencies: [] - dependencies: [] displayText: color-name 2.0.2 - entryId: '' + entryId: /color-name/2.0.2 entryPackageName: color-name entryPackageVersion: 2.0.2 entrySuffix: '' - jsonId: 4 + jsonId: 7 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-name@2.0.2/node_modules/color-name rawEntryId: /color-name/2.0.2 referrerJsonIds: - - 3 - - 5 + - 6 + - 8 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /color-name/2.0.2 name: color-name + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 4 - version: 2.0.2 + resolvedEntryJsonId: 7 + versionPath: 2.0.2 displayText: color-string 2.1.2 - entryId: '' + entryId: /color-string/2.1.2 entryPackageName: color-string entryPackageVersion: 2.1.2 entrySuffix: '' - jsonId: 5 + jsonId: 8 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-string@2.1.2/node_modules/color-string rawEntryId: /color-string/2.1.2 referrerJsonIds: - - 1 - - 6 + - 0 + - 2 + - 9 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /color-convert/3.1.2 name: color-convert + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 3 - version: 3.1.2 - - dependencyType: regular + resolvedEntryJsonId: 6 + versionPath: 3.1.2 + - dependencyKind: regular entryId: /color-string/2.1.2 name: color-string + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 5 - version: 2.1.2 + resolvedEntryJsonId: 8 + versionPath: 2.1.2 displayText: color 5.0.2 - entryId: '' + entryId: /color/5.0.2 entryPackageName: color entryPackageVersion: 5.0.2 entrySuffix: '' - jsonId: 6 + jsonId: 9 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color@5.0.2/node_modules/color rawEntryId: /color/5.0.2 referrerJsonIds: - - 0 + - 1 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: '' name: target-folder + originalSpecifier: '' peerDependencyMeta: {} - version: link:link-specifier/target-folder + versionPath: link:link-specifier/target-folder displayText: has-symbols 1.0.2 - entryId: '' + entryId: /has-symbols/1.0.2 entryPackageName: has-symbols entryPackageVersion: 1.0.2 entrySuffix: '' - jsonId: 7 + jsonId: 10 kind: 2 packageJsonFolderPath: node_modules/.pnpm/has-symbols@1.0.2/node_modules/has-symbols rawEntryId: /has-symbols/1.0.2 referrerJsonIds: - - 2 + - 3 + transitivePeerDependencies: [] + - dependencies: [] + displayText: mime-db 1.54.0 + entryId: /mime-db/1.54.0 + entryPackageName: mime-db + entryPackageVersion: 1.54.0 + entrySuffix: '' + jsonId: 11 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db + rawEntryId: /mime-db/1.54.0 + referrerJsonIds: + - 0 + transitivePeerDependencies: [] + - dependencies: [] + displayText: punycode 2.3.1 + entryId: /punycode/2.3.1 + entryPackageName: punycode + entryPackageVersion: 2.3.1 + entrySuffix: '' + jsonId: 12 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/punycode@2.3.1/node_modules/punycode + rawEntryId: /punycode/2.3.1 + referrerJsonIds: + - 13 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: /punycode/2.3.1 + name: punycode + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 12 + versionPath: 2.3.1 + displayText: uri-js 4.4.1 + entryId: /uri-js/4.4.1 + entryPackageName: uri-js + entryPackageVersion: 4.4.1 + entrySuffix: '' + jsonId: 13 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/uri-js@4.4.1/node_modules/uri-js + rawEntryId: /uri-js/4.4.1 + referrerJsonIds: + - 4 transitivePeerDependencies: [] workspace: pnpmLockfileFolder: '' diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v6.0.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v6.0.test.ts.snap index ff8058f4180..fd6f0c80fdd 100644 --- a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v6.0.test.ts.snap +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v6.0.test.ts.snap @@ -1,161 +1,282 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`lfxGraph-edge-cases-v5.4 loads a workspace 1`] = ` +exports[`lfxGraph-edge-cases-v6.0 loads a workspace 1`] = ` "entries: - dependencies: - - dependencyType: regular - entryId: /color/5.0.2 - name: color + - dependencyKind: regular + entryId: /mime-db@1.54.0 + name: mime-db + originalSpecifier: ^1.52.0 peerDependencyMeta: {} - resolvedEntryJsonId: 6 - version: 5.0.2 - displayText: 'Project: duplicate (duplicate-1/duplicate)' - entryId: project:duplicate-1/duplicate - entryPackageName: duplicate (duplicate-1/duplicate) + resolvedEntryJsonId: 11 + versionPath: 1.54.0 + - dependencyKind: dev + entryId: /color-string@2.1.2 + name: color-string + originalSpecifier: ^2.1.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: dev-dependencies' + entryId: project:packages/dev-dependencies + entryPackageName: dev-dependencies entryPackageVersion: '' entrySuffix: '' jsonId: 0 kind: 1 - packageJsonFolderPath: duplicate-1/duplicate - rawEntryId: duplicate-1/duplicate + packageJsonFolderPath: packages/dev-dependencies + rawEntryId: packages/dev-dependencies referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular - entryId: /color-string/2.1.2 - name: color-string + - dependencyKind: regular + entryId: /color@5.0.2 + name: color + originalSpecifier: ^5.0.2 peerDependencyMeta: {} - resolvedEntryJsonId: 5 - version: 2.1.2 - displayText: 'Project: duplicate (duplicate-2/duplicate)' - entryId: project:duplicate-2/duplicate - entryPackageName: duplicate (duplicate-2/duplicate) + resolvedEntryJsonId: 9 + versionPath: 5.0.2 + displayText: 'Project: duplicate (packages/duplicate-1/duplicate)' + entryId: project:packages/duplicate-1/duplicate + entryPackageName: duplicate (packages/duplicate-1/duplicate) entryPackageVersion: '' entrySuffix: '' jsonId: 1 kind: 1 - packageJsonFolderPath: duplicate-2/duplicate - rawEntryId: duplicate-2/duplicate + packageJsonFolderPath: packages/duplicate-1/duplicate + rawEntryId: packages/duplicate-1/duplicate referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular - entryId: /has-symbols/1.0.2 + - dependencyKind: regular + entryId: /color-string@2.1.2 + name: color-string + originalSpecifier: ^2.1.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: duplicate (packages/duplicate-2/duplicate)' + entryId: project:packages/duplicate-2/duplicate + entryPackageName: duplicate (packages/duplicate-2/duplicate) + entryPackageVersion: '' + entrySuffix: '' + jsonId: 2 + kind: 1 + packageJsonFolderPath: packages/duplicate-2/duplicate + rawEntryId: packages/duplicate-2/duplicate + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: /has-symbols@1.0.2 name: has-symbols + originalSpecifier: 1.0.2 peerDependencyMeta: {} - resolvedEntryJsonId: 7 - version: 1.0.2 - - dependencyType: regular - entryId: project:link-specifier/target-folder + resolvedEntryJsonId: 10 + versionPath: 1.0.2 + - dependencyKind: regular + entryId: project:packages/link-specifier/linker/packages/link-specifier/target-folder name: target-folder + originalSpecifier: link:packages/link-specifier/target-folder peerDependencyMeta: {} - version: link:../target-folder + versionPath: link:packages/link-specifier/target-folder displayText: 'Project: linker' - entryId: project:link-specifier/linker + entryId: project:packages/link-specifier/linker entryPackageName: linker entryPackageVersion: '' entrySuffix: '' - jsonId: 2 + jsonId: 3 kind: 1 - packageJsonFolderPath: link-specifier/linker - rawEntryId: link-specifier/linker + packageJsonFolderPath: packages/link-specifier/linker + rawEntryId: packages/link-specifier/linker referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular - entryId: /color-name/2.0.2 + - dependencyKind: regular + entryId: /address@1.2.2 + name: address + originalSpecifier: ^1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 5 + versionPath: 1.2.2 + - dependencyKind: regular + entryId: /uri-js@4.4.1 + name: uri-js + originalSpecifier: ^4.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 13 + versionPath: 4.4.1 + displayText: 'Project: pnpmfile-transforms' + entryId: project:packages/pnpmfile-transforms + entryPackageName: pnpmfile-transforms + entryPackageVersion: '' + entrySuffix: '' + jsonId: 4 + kind: 1 + packageJsonFolderPath: packages/pnpmfile-transforms + rawEntryId: packages/pnpmfile-transforms + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: [] + displayText: address 1.2.2 + entryId: /address@1.2.2 + entryPackageName: address + entryPackageVersion: 1.2.2 + entrySuffix: '' + jsonId: 5 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/address@1.2.2/node_modules/address + rawEntryId: /address@1.2.2 + referrerJsonIds: + - 4 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: /color-name@2.0.2 name: color-name + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 4 - version: 2.0.2 + resolvedEntryJsonId: 7 + versionPath: 2.0.2 displayText: color-convert 3.1.2 - entryId: '' + entryId: /color-convert@3.1.2 entryPackageName: color-convert entryPackageVersion: 3.1.2 entrySuffix: '' - jsonId: 3 + jsonId: 6 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-convert@3.1.2/node_modules/color-convert - rawEntryId: /color-convert/3.1.2 + rawEntryId: /color-convert@3.1.2 referrerJsonIds: - - 6 + - 9 transitivePeerDependencies: [] - dependencies: [] displayText: color-name 2.0.2 - entryId: '' + entryId: /color-name@2.0.2 entryPackageName: color-name entryPackageVersion: 2.0.2 entrySuffix: '' - jsonId: 4 + jsonId: 7 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-name@2.0.2/node_modules/color-name - rawEntryId: /color-name/2.0.2 + rawEntryId: /color-name@2.0.2 referrerJsonIds: - - 3 - - 5 + - 6 + - 8 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular - entryId: /color-name/2.0.2 + - dependencyKind: regular + entryId: /color-name@2.0.2 name: color-name + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 4 - version: 2.0.2 + resolvedEntryJsonId: 7 + versionPath: 2.0.2 displayText: color-string 2.1.2 - entryId: '' + entryId: /color-string@2.1.2 entryPackageName: color-string entryPackageVersion: 2.1.2 entrySuffix: '' - jsonId: 5 + jsonId: 8 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color-string@2.1.2/node_modules/color-string - rawEntryId: /color-string/2.1.2 + rawEntryId: /color-string@2.1.2 referrerJsonIds: - - 1 - - 6 + - 0 + - 2 + - 9 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular - entryId: /color-convert/3.1.2 + - dependencyKind: regular + entryId: /color-convert@3.1.2 name: color-convert + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 3 - version: 3.1.2 - - dependencyType: regular - entryId: /color-string/2.1.2 + resolvedEntryJsonId: 6 + versionPath: 3.1.2 + - dependencyKind: regular + entryId: /color-string@2.1.2 name: color-string + originalSpecifier: '' peerDependencyMeta: {} - resolvedEntryJsonId: 5 - version: 2.1.2 + resolvedEntryJsonId: 8 + versionPath: 2.1.2 displayText: color 5.0.2 - entryId: '' + entryId: /color@5.0.2 entryPackageName: color entryPackageVersion: 5.0.2 entrySuffix: '' - jsonId: 6 + jsonId: 9 kind: 2 packageJsonFolderPath: node_modules/.pnpm/color@5.0.2/node_modules/color - rawEntryId: /color/5.0.2 + rawEntryId: /color@5.0.2 referrerJsonIds: - - 0 + - 1 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: '' name: target-folder + originalSpecifier: '' peerDependencyMeta: {} - version: link:link-specifier/target-folder + versionPath: link:link-specifier/target-folder displayText: has-symbols 1.0.2 - entryId: '' + entryId: /has-symbols@1.0.2 entryPackageName: has-symbols entryPackageVersion: 1.0.2 entrySuffix: '' - jsonId: 7 + jsonId: 10 kind: 2 packageJsonFolderPath: node_modules/.pnpm/has-symbols@1.0.2/node_modules/has-symbols - rawEntryId: /has-symbols/1.0.2 + rawEntryId: /has-symbols@1.0.2 referrerJsonIds: - - 2 + - 3 + transitivePeerDependencies: [] + - dependencies: [] + displayText: mime-db 1.54.0 + entryId: /mime-db@1.54.0 + entryPackageName: mime-db + entryPackageVersion: 1.54.0 + entrySuffix: '' + jsonId: 11 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db + rawEntryId: /mime-db@1.54.0 + referrerJsonIds: + - 0 + transitivePeerDependencies: [] + - dependencies: [] + displayText: punycode 2.3.1 + entryId: /punycode@2.3.1 + entryPackageName: punycode + entryPackageVersion: 2.3.1 + entrySuffix: '' + jsonId: 12 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/punycode@2.3.1/node_modules/punycode + rawEntryId: /punycode@2.3.1 + referrerJsonIds: + - 13 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: /punycode@2.3.1 + name: punycode + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 12 + versionPath: 2.3.1 + displayText: uri-js 4.4.1 + entryId: /uri-js@4.4.1 + entryPackageName: uri-js + entryPackageVersion: 4.4.1 + entrySuffix: '' + jsonId: 13 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/uri-js@4.4.1/node_modules/uri-js + rawEntryId: /uri-js@4.4.1 + referrerJsonIds: + - 4 transitivePeerDependencies: [] workspace: pnpmLockfileFolder: '' diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v9.0.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v9.0.test.ts.snap new file mode 100644 index 00000000000..e2683f0e50d --- /dev/null +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-edge-cases-v9.0.test.ts.snap @@ -0,0 +1,287 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lfxGraph-edge-cases-v9.0 loads a workspace 1`] = ` +"entries: + - dependencies: + - dependencyKind: regular + entryId: mime-db@1.54.0 + name: mime-db + originalSpecifier: ^1.52.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 11 + versionPath: 1.54.0 + - dependencyKind: dev + entryId: color-string@2.1.2 + name: color-string + originalSpecifier: ^2.1.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: dev-dependencies' + entryId: project:packages/dev-dependencies + entryPackageName: dev-dependencies + entryPackageVersion: '' + entrySuffix: '' + jsonId: 0 + kind: 1 + packageJsonFolderPath: packages/dev-dependencies + rawEntryId: packages/dev-dependencies + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: color@5.0.2 + name: color + originalSpecifier: ^5.0.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 9 + versionPath: 5.0.2 + displayText: 'Project: duplicate (packages/duplicate-1/duplicate)' + entryId: project:packages/duplicate-1/duplicate + entryPackageName: duplicate (packages/duplicate-1/duplicate) + entryPackageVersion: '' + entrySuffix: '' + jsonId: 1 + kind: 1 + packageJsonFolderPath: packages/duplicate-1/duplicate + rawEntryId: packages/duplicate-1/duplicate + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: color-string@2.1.2 + name: color-string + originalSpecifier: ^2.1.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: 'Project: duplicate (packages/duplicate-2/duplicate)' + entryId: project:packages/duplicate-2/duplicate + entryPackageName: duplicate (packages/duplicate-2/duplicate) + entryPackageVersion: '' + entrySuffix: '' + jsonId: 2 + kind: 1 + packageJsonFolderPath: packages/duplicate-2/duplicate + rawEntryId: packages/duplicate-2/duplicate + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: has-symbols@1.0.2 + name: has-symbols + originalSpecifier: 1.0.2 + peerDependencyMeta: {} + resolvedEntryJsonId: 10 + versionPath: 1.0.2 + - dependencyKind: regular + entryId: project:packages/link-specifier/linker/packages/link-specifier/target-folder + name: target-folder + originalSpecifier: link:packages/link-specifier/target-folder + peerDependencyMeta: {} + versionPath: link:packages/link-specifier/target-folder + displayText: 'Project: linker' + entryId: project:packages/link-specifier/linker + entryPackageName: linker + entryPackageVersion: '' + entrySuffix: '' + jsonId: 3 + kind: 1 + packageJsonFolderPath: packages/link-specifier/linker + rawEntryId: packages/link-specifier/linker + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: address@1.2.2 + name: address + originalSpecifier: ^1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 5 + versionPath: 1.2.2 + - dependencyKind: regular + entryId: uri-js@4.4.1 + name: uri-js + originalSpecifier: ^4.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 13 + versionPath: 4.4.1 + displayText: 'Project: pnpmfile-transforms' + entryId: project:packages/pnpmfile-transforms + entryPackageName: pnpmfile-transforms + entryPackageVersion: '' + entrySuffix: '' + jsonId: 4 + kind: 1 + packageJsonFolderPath: packages/pnpmfile-transforms + rawEntryId: packages/pnpmfile-transforms + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: [] + displayText: address 1.2.2 + entryId: address@1.2.2 + entryPackageName: address + entryPackageVersion: 1.2.2 + entrySuffix: '' + jsonId: 5 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/address@1.2.2/node_modules/address + rawEntryId: address@1.2.2 + referrerJsonIds: + - 4 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: color-name@2.0.2 + name: color-name + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 7 + versionPath: 2.0.2 + displayText: color-convert 3.1.2 + entryId: color-convert@3.1.2 + entryPackageName: color-convert + entryPackageVersion: 3.1.2 + entrySuffix: '' + jsonId: 6 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/color-convert@3.1.2/node_modules/color-convert + rawEntryId: color-convert@3.1.2 + referrerJsonIds: + - 9 + transitivePeerDependencies: [] + - dependencies: [] + displayText: color-name 2.0.2 + entryId: color-name@2.0.2 + entryPackageName: color-name + entryPackageVersion: 2.0.2 + entrySuffix: '' + jsonId: 7 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/color-name@2.0.2/node_modules/color-name + rawEntryId: color-name@2.0.2 + referrerJsonIds: + - 6 + - 8 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: color-name@2.0.2 + name: color-name + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 7 + versionPath: 2.0.2 + displayText: color-string 2.1.2 + entryId: color-string@2.1.2 + entryPackageName: color-string + entryPackageVersion: 2.1.2 + entrySuffix: '' + jsonId: 8 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/color-string@2.1.2/node_modules/color-string + rawEntryId: color-string@2.1.2 + referrerJsonIds: + - 0 + - 2 + - 9 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: color-convert@3.1.2 + name: color-convert + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 6 + versionPath: 3.1.2 + - dependencyKind: regular + entryId: color-string@2.1.2 + name: color-string + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 2.1.2 + displayText: color 5.0.2 + entryId: color@5.0.2 + entryPackageName: color + entryPackageVersion: 5.0.2 + entrySuffix: '' + jsonId: 9 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/color@5.0.2/node_modules/color + rawEntryId: color@5.0.2 + referrerJsonIds: + - 1 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: '' + name: target-folder + originalSpecifier: '' + peerDependencyMeta: {} + versionPath: link:link-specifier/target-folder + displayText: has-symbols 1.0.2 + entryId: has-symbols@1.0.2 + entryPackageName: has-symbols + entryPackageVersion: 1.0.2 + entrySuffix: '' + jsonId: 10 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/has-symbols@1.0.2/node_modules/has-symbols + rawEntryId: has-symbols@1.0.2 + referrerJsonIds: + - 3 + transitivePeerDependencies: [] + - dependencies: [] + displayText: mime-db 1.54.0 + entryId: mime-db@1.54.0 + entryPackageName: mime-db + entryPackageVersion: 1.54.0 + entrySuffix: '' + jsonId: 11 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db + rawEntryId: mime-db@1.54.0 + referrerJsonIds: + - 0 + transitivePeerDependencies: [] + - dependencies: [] + displayText: punycode 2.3.1 + entryId: punycode@2.3.1 + entryPackageName: punycode + entryPackageVersion: 2.3.1 + entrySuffix: '' + jsonId: 12 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/punycode@2.3.1/node_modules/punycode + rawEntryId: punycode@2.3.1 + referrerJsonIds: + - 13 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: punycode@2.3.1 + name: punycode + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 12 + versionPath: 2.3.1 + displayText: uri-js 4.4.1 + entryId: uri-js@4.4.1 + entryPackageName: uri-js + entryPackageVersion: 4.4.1 + entrySuffix: '' + jsonId: 13 + kind: 2 + packageJsonFolderPath: node_modules/.pnpm/uri-js@4.4.1/node_modules/uri-js + rawEntryId: uri-js@4.4.1 + referrerJsonIds: + - 4 + transitivePeerDependencies: [] +workspace: + pnpmLockfileFolder: '' + pnpmLockfilePath: pnpm-lock.yaml + pnpmfilePath: .pnpmfile.cjs + workspaceRootFullPath: /repo +" +`; diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v5.4.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v5.4.test.ts.snap index 58b8bf4cb78..804e1219938 100644 --- a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v5.4.test.ts.snap +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v5.4.test.ts.snap @@ -3,12 +3,13 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` "entries: - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/d name: '@rushstack/d' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 3 - version: link:../d + versionPath: link:../d displayText: 'Project: a' entryId: project:projects/a entryPackageName: a @@ -21,18 +22,20 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/d name: '@rushstack/d' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 3 - version: link:../d - - dependencyType: regular + versionPath: link:../d + - dependencyKind: regular entryId: /@rushstack/n/2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 11 - version: 2.0.0 + versionPath: 2.0.0 displayText: 'Project: b' entryId: project:projects/b entryPackageName: b @@ -45,24 +48,27 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/e name: '@rushstack/e' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 4 - version: link:../e - - dependencyType: regular + versionPath: link:../e + - dependencyKind: regular entryId: /@rushstack/k/1.0.0_@rushstack+m@1.0.0 name: '@rushstack/k' + originalSpecifier: ^1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 6 - version: 1.0.0_@rushstack+m@1.0.0 - - dependencyType: regular + versionPath: 1.0.0_@rushstack+m@1.0.0 + - dependencyKind: regular entryId: /@rushstack/m/1.0.0 name: '@rushstack/m' + originalSpecifier: ~1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 10 - version: 1.0.0 + versionPath: 1.0.0 displayText: 'Project: c' entryId: project:projects/c entryPackageName: c @@ -75,24 +81,27 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/e name: '@rushstack/e' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 4 - version: link:../e - - dependencyType: regular + versionPath: link:../e + - dependencyKind: regular entryId: /@rushstack/j/1.0.0_@rushstack+n@2.0.0 name: '@rushstack/j' + originalSpecifier: ^1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 5 - version: 1.0.0_@rushstack+n@2.0.0 - - dependencyType: regular + versionPath: 1.0.0_@rushstack+n@2.0.0 + - dependencyKind: regular entryId: /@rushstack/n/2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 11 - version: 2.0.0 + versionPath: 2.0.0 displayText: 'Project: d' entryId: project:projects/d entryPackageName: d @@ -107,12 +116,13 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` - 1 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/n/3.0.0 name: '@rushstack/n' + originalSpecifier: ^3.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 12 - version: 3.0.0 + versionPath: 3.0.0 displayText: 'Project: e' entryId: project:projects/e entryPackageName: e @@ -127,20 +137,22 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` - 3 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/k/1.0.0_wxpgugna4ivthu7yyu4fmciltu name: '@rushstack/k' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 7 - version: 1.0.0_wxpgugna4ivthu7yyu4fmciltu - - dependencyType: regular + versionPath: 1.0.0_wxpgugna4ivthu7yyu4fmciltu + - dependencyKind: regular entryId: /@rushstack/m/1.0.0 name: '@rushstack/m' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 10 - version: 1.0.0 + versionPath: 1.0.0 displayText: '@rushstack/j 1.0.0 (@rushstack+n@2.0.0)' - entryId: '' + entryId: /@rushstack/j/1.0.0_@rushstack+n@2.0.0 entryPackageName: '@rushstack/j' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack+n@2.0.0' @@ -153,14 +165,15 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` transitivePeerDependencies: - '@rushstack/n' - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/l/1.0.0_@rushstack+m@1.0.0 name: '@rushstack/l' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 8 - version: 1.0.0_@rushstack+m@1.0.0 + versionPath: 1.0.0_@rushstack+m@1.0.0 displayText: '@rushstack/k 1.0.0 (@rushstack+m@1.0.0)' - entryId: '' + entryId: /@rushstack/k/1.0.0_@rushstack+m@1.0.0 entryPackageName: '@rushstack/k' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack+m@1.0.0' @@ -174,14 +187,15 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` - '@rushstack/m' - '@rushstack/n' - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/l/1.0.0_wxpgugna4ivthu7yyu4fmciltu name: '@rushstack/l' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 9 - version: 1.0.0_wxpgugna4ivthu7yyu4fmciltu + versionPath: 1.0.0_wxpgugna4ivthu7yyu4fmciltu displayText: '@rushstack/k 1.0.0 (wxpgugna4ivthu7yyu4fmciltu)' - entryId: '' + entryId: /@rushstack/k/1.0.0_wxpgugna4ivthu7yyu4fmciltu entryPackageName: '@rushstack/k' entryPackageVersion: 1.0.0 entrySuffix: wxpgugna4ivthu7yyu4fmciltu @@ -195,30 +209,17 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` - '@rushstack/m' - '@rushstack/n' - dependencies: - - dependencyType: regular + - dependencyKind: peer entryId: /@rushstack/m/1.0.0 name: '@rushstack/m' - peerDependencyMeta: {} - resolvedEntryJsonId: 10 - version: 1.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/m' - name: '@rushstack/m' + originalSpecifier: ^1.0.0 peerDependencyMeta: name: '@rushstack/m' optional: false - version: ^1.0.0 - version: ^1.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/n' - name: '@rushstack/n' - peerDependencyMeta: - name: '@rushstack/n' - optional: true - version: ^2.0.0 - version: ^2.0.0 + version: 1.0.0 + versionPath: 1.0.0 displayText: '@rushstack/l 1.0.0 (@rushstack+m@1.0.0)' - entryId: '' + entryId: /@rushstack/l/1.0.0_@rushstack+m@1.0.0 entryPackageName: '@rushstack/l' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack+m@1.0.0' @@ -230,36 +231,26 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` - 6 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: peer entryId: /@rushstack/m/1.0.0 name: '@rushstack/m' - peerDependencyMeta: {} - resolvedEntryJsonId: 10 - version: 1.0.0 - - dependencyType: regular - entryId: /@rushstack/n/2.0.0 - name: '@rushstack/n' - peerDependencyMeta: {} - resolvedEntryJsonId: 11 - version: 2.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/m' - name: '@rushstack/m' + originalSpecifier: ^1.0.0 peerDependencyMeta: name: '@rushstack/m' optional: false - version: ^1.0.0 - version: ^1.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/n' + version: 1.0.0 + versionPath: 1.0.0 + - dependencyKind: peer + entryId: /@rushstack/n/2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: name: '@rushstack/n' optional: true - version: ^2.0.0 - version: ^2.0.0 + version: 2.0.0 + versionPath: 2.0.0 displayText: '@rushstack/l 1.0.0 (wxpgugna4ivthu7yyu4fmciltu)' - entryId: '' + entryId: /@rushstack/l/1.0.0_wxpgugna4ivthu7yyu4fmciltu entryPackageName: '@rushstack/l' entryPackageVersion: 1.0.0 entrySuffix: wxpgugna4ivthu7yyu4fmciltu @@ -272,7 +263,7 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/m 1.0.0' - entryId: '' + entryId: /@rushstack/m/1.0.0 entryPackageName: '@rushstack/m' entryPackageVersion: 1.0.0 entrySuffix: '' @@ -283,12 +274,10 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` referrerJsonIds: - 2 - 5 - - 8 - - 9 transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/n 2.0.0' - entryId: '' + entryId: /@rushstack/n/2.0.0 entryPackageName: '@rushstack/n' entryPackageVersion: 2.0.0 entrySuffix: '' @@ -299,11 +288,10 @@ exports[`lfxGraph-website-sample-1-v5.4 loads a workspace 1`] = ` referrerJsonIds: - 1 - 3 - - 9 transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/n 3.0.0' - entryId: '' + entryId: /@rushstack/n/3.0.0 entryPackageName: '@rushstack/n' entryPackageVersion: 3.0.0 entrySuffix: '' diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v6.0.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v6.0.test.ts.snap index 64f461236c5..f53e290cbcd 100644 --- a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v6.0.test.ts.snap +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v6.0.test.ts.snap @@ -3,12 +3,13 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` "entries: - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/d name: '@rushstack/d' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 3 - version: link:../d + versionPath: link:../d displayText: 'Project: a' entryId: project:projects/a entryPackageName: a @@ -21,18 +22,20 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/d name: '@rushstack/d' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 3 - version: link:../d - - dependencyType: regular + versionPath: link:../d + - dependencyKind: regular entryId: /@rushstack/n@2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 9 - version: 2.0.0 + versionPath: 2.0.0 displayText: 'Project: b' entryId: project:projects/b entryPackageName: b @@ -45,24 +48,27 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/e name: '@rushstack/e' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 4 - version: link:../e - - dependencyType: regular + versionPath: link:../e + - dependencyKind: regular entryId: /@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) name: '@rushstack/k' + originalSpecifier: ^1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 6 - version: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) - - dependencyType: regular + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + - dependencyKind: regular entryId: /@rushstack/m@1.0.0 name: '@rushstack/m' + originalSpecifier: ~1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 8 - version: 1.0.0 + versionPath: 1.0.0 displayText: 'Project: c' entryId: project:projects/c entryPackageName: c @@ -75,24 +81,27 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` referrerJsonIds: [] transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: project:projects/e name: '@rushstack/e' + originalSpecifier: workspace:* peerDependencyMeta: {} resolvedEntryJsonId: 4 - version: link:../e - - dependencyType: regular + versionPath: link:../e + - dependencyKind: regular entryId: /@rushstack/j@1.0.0(@rushstack/n@2.0.0) name: '@rushstack/j' + originalSpecifier: ^1.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 5 - version: 1.0.0(@rushstack/n@2.0.0) - - dependencyType: regular + versionPath: 1.0.0(@rushstack/n@2.0.0) + - dependencyKind: regular entryId: /@rushstack/n@2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 9 - version: 2.0.0 + versionPath: 2.0.0 displayText: 'Project: d' entryId: project:projects/d entryPackageName: d @@ -107,12 +116,13 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` - 1 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/n@3.0.0 name: '@rushstack/n' + originalSpecifier: ^3.0.0 peerDependencyMeta: {} resolvedEntryJsonId: 10 - version: 3.0.0 + versionPath: 3.0.0 displayText: 'Project: e' entryId: project:projects/e entryPackageName: e @@ -127,20 +137,22 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` - 3 transitivePeerDependencies: [] - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) name: '@rushstack/k' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 6 - version: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) - - dependencyType: regular + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + - dependencyKind: regular entryId: /@rushstack/m@1.0.0 name: '@rushstack/m' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 8 - version: 1.0.0 + versionPath: 1.0.0 displayText: '@rushstack/j 1.0.0 [@rushstack/n@2.0.0]' - entryId: '' + entryId: /@rushstack/j@1.0.0(@rushstack/n@2.0.0) entryPackageName: '@rushstack/j' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack/n@2.0.0' @@ -153,14 +165,15 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` transitivePeerDependencies: - '@rushstack/n' - dependencies: - - dependencyType: regular + - dependencyKind: regular entryId: /@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) name: '@rushstack/l' + originalSpecifier: '' peerDependencyMeta: {} resolvedEntryJsonId: 7 - version: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) displayText: '@rushstack/k 1.0.0 [@rushstack/m@1.0.0; @rushstack/n@2.0.0]' - entryId: '' + entryId: /@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) entryPackageName: '@rushstack/k' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack/m@1.0.0; @rushstack/n@2.0.0' @@ -176,36 +189,26 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` - '@rushstack/m' - '@rushstack/n' - dependencies: - - dependencyType: regular + - dependencyKind: peer entryId: /@rushstack/m@1.0.0 name: '@rushstack/m' - peerDependencyMeta: {} - resolvedEntryJsonId: 8 - version: 1.0.0 - - dependencyType: regular - entryId: /@rushstack/n@2.0.0 - name: '@rushstack/n' - peerDependencyMeta: {} - resolvedEntryJsonId: 9 - version: 2.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/m' - name: '@rushstack/m' + originalSpecifier: ^1.0.0 peerDependencyMeta: name: '@rushstack/m' optional: false - version: ^1.0.0 - version: ^1.0.0 - - dependencyType: peer - entryId: 'Peer: @rushstack/n' + version: 1.0.0 + versionPath: 1.0.0 + - dependencyKind: peer + entryId: /@rushstack/n@2.0.0 name: '@rushstack/n' + originalSpecifier: ^2.0.0 peerDependencyMeta: name: '@rushstack/n' optional: true - version: ^2.0.0 - version: ^2.0.0 + version: 2.0.0 + versionPath: 2.0.0 displayText: '@rushstack/l 1.0.0 [@rushstack/m@1.0.0; @rushstack/n@2.0.0]' - entryId: '' + entryId: /@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) entryPackageName: '@rushstack/l' entryPackageVersion: 1.0.0 entrySuffix: '@rushstack/m@1.0.0; @rushstack/n@2.0.0' @@ -219,7 +222,7 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/m 1.0.0' - entryId: '' + entryId: /@rushstack/m@1.0.0 entryPackageName: '@rushstack/m' entryPackageVersion: 1.0.0 entrySuffix: '' @@ -230,11 +233,10 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` referrerJsonIds: - 2 - 5 - - 7 transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/n 2.0.0' - entryId: '' + entryId: /@rushstack/n@2.0.0 entryPackageName: '@rushstack/n' entryPackageVersion: 2.0.0 entrySuffix: '' @@ -245,11 +247,10 @@ exports[`lfxGraph-website-sample-1-v6.0 loads a workspace 1`] = ` referrerJsonIds: - 1 - 3 - - 7 transitivePeerDependencies: [] - dependencies: [] displayText: '@rushstack/n 3.0.0' - entryId: '' + entryId: /@rushstack/n@3.0.0 entryPackageName: '@rushstack/n' entryPackageVersion: 3.0.0 entrySuffix: '' diff --git a/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v9.0.test.ts.snap b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v9.0.test.ts.snap new file mode 100644 index 00000000000..852a55ba2d2 --- /dev/null +++ b/apps/lockfile-explorer/src/graph/test/__snapshots__/lfxGraph-website-sample-1-v9.0.test.ts.snap @@ -0,0 +1,274 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lfxGraph-website-sample-1-v9.0 loads a workspace 1`] = ` +"entries: + - dependencies: + - dependencyKind: regular + entryId: project:projects/d + name: '@rushstack/d' + originalSpecifier: workspace:* + peerDependencyMeta: {} + resolvedEntryJsonId: 3 + versionPath: link:../d + displayText: 'Project: a' + entryId: project:projects/a + entryPackageName: a + entryPackageVersion: '' + entrySuffix: '' + jsonId: 0 + kind: 1 + packageJsonFolderPath: projects/a + rawEntryId: ../../projects/a + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: project:projects/d + name: '@rushstack/d' + originalSpecifier: workspace:* + peerDependencyMeta: {} + resolvedEntryJsonId: 3 + versionPath: link:../d + - dependencyKind: regular + entryId: '@rushstack/n@2.0.0' + name: '@rushstack/n' + originalSpecifier: ^2.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 9 + versionPath: 2.0.0 + displayText: 'Project: b' + entryId: project:projects/b + entryPackageName: b + entryPackageVersion: '' + entrySuffix: '' + jsonId: 1 + kind: 1 + packageJsonFolderPath: projects/b + rawEntryId: ../../projects/b + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: project:projects/e + name: '@rushstack/e' + originalSpecifier: workspace:* + peerDependencyMeta: {} + resolvedEntryJsonId: 4 + versionPath: link:../e + - dependencyKind: regular + entryId: '@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + name: '@rushstack/k' + originalSpecifier: ^1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 6 + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + - dependencyKind: regular + entryId: '@rushstack/m@1.0.0' + name: '@rushstack/m' + originalSpecifier: ~1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 1.0.0 + displayText: 'Project: c' + entryId: project:projects/c + entryPackageName: c + entryPackageVersion: '' + entrySuffix: '' + jsonId: 2 + kind: 1 + packageJsonFolderPath: projects/c + rawEntryId: ../../projects/c + referrerJsonIds: [] + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: project:projects/e + name: '@rushstack/e' + originalSpecifier: workspace:* + peerDependencyMeta: {} + resolvedEntryJsonId: 4 + versionPath: link:../e + - dependencyKind: regular + entryId: '@rushstack/j@1.0.0(@rushstack/n@2.0.0)' + name: '@rushstack/j' + originalSpecifier: ^1.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 5 + versionPath: 1.0.0(@rushstack/n@2.0.0) + - dependencyKind: regular + entryId: '@rushstack/n@2.0.0' + name: '@rushstack/n' + originalSpecifier: ^2.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 9 + versionPath: 2.0.0 + displayText: 'Project: d' + entryId: project:projects/d + entryPackageName: d + entryPackageVersion: '' + entrySuffix: '' + jsonId: 3 + kind: 1 + packageJsonFolderPath: projects/d + rawEntryId: ../../projects/d + referrerJsonIds: + - 0 + - 1 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: '@rushstack/n@3.0.0' + name: '@rushstack/n' + originalSpecifier: ^3.0.0 + peerDependencyMeta: {} + resolvedEntryJsonId: 10 + versionPath: 3.0.0 + displayText: 'Project: e' + entryId: project:projects/e + entryPackageName: e + entryPackageVersion: '' + entrySuffix: '' + jsonId: 4 + kind: 1 + packageJsonFolderPath: projects/e + rawEntryId: ../../projects/e + referrerJsonIds: + - 2 + - 3 + transitivePeerDependencies: [] + - dependencies: + - dependencyKind: regular + entryId: '@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + name: '@rushstack/k' + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 6 + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + - dependencyKind: regular + entryId: '@rushstack/m@1.0.0' + name: '@rushstack/m' + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 8 + versionPath: 1.0.0 + displayText: '@rushstack/j 1.0.0 [@rushstack/n@2.0.0]' + entryId: '@rushstack/j@1.0.0(@rushstack/n@2.0.0)' + entryPackageName: '@rushstack/j' + entryPackageVersion: 1.0.0 + entrySuffix: '@rushstack/n@2.0.0' + jsonId: 5 + kind: 2 + packageJsonFolderPath: common/temp/node_modules/.pnpm/@rushstack+j@1.0.0_@rushstack+n@2.0.0/node_modules/@rushstack/j + rawEntryId: '@rushstack/j@1.0.0(@rushstack/n@2.0.0)' + referrerJsonIds: + - 3 + transitivePeerDependencies: + - '@rushstack/n' + - dependencies: + - dependencyKind: regular + entryId: '@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + name: '@rushstack/l' + originalSpecifier: '' + peerDependencyMeta: {} + resolvedEntryJsonId: 7 + versionPath: 1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0) + displayText: '@rushstack/k 1.0.0 [@rushstack/m@1.0.0; @rushstack/n@2.0.0]' + entryId: '@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + entryPackageName: '@rushstack/k' + entryPackageVersion: 1.0.0 + entrySuffix: '@rushstack/m@1.0.0; @rushstack/n@2.0.0' + jsonId: 6 + kind: 2 + packageJsonFolderPath: >- + common/temp/node_modules/.pnpm/@rushstack+k@1.0.0_@rushstack+m@1.0.0_@rushstack+n@2.0.0/node_modules/@rushstack/k + rawEntryId: '@rushstack/k@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + referrerJsonIds: + - 2 + - 5 + transitivePeerDependencies: + - '@rushstack/m' + - '@rushstack/n' + - dependencies: + - dependencyKind: peer + entryId: '@rushstack/m@1.0.0' + name: '@rushstack/m' + originalSpecifier: ^1.0.0 + peerDependencyMeta: + name: '@rushstack/m' + optional: false + version: 1.0.0 + versionPath: 1.0.0 + - dependencyKind: peer + entryId: '@rushstack/n@2.0.0' + name: '@rushstack/n' + originalSpecifier: ^2.0.0 + peerDependencyMeta: + name: '@rushstack/n' + optional: true + version: 2.0.0 + versionPath: 2.0.0 + displayText: '@rushstack/l 1.0.0 [@rushstack/m@1.0.0; @rushstack/n@2.0.0]' + entryId: '@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + entryPackageName: '@rushstack/l' + entryPackageVersion: 1.0.0 + entrySuffix: '@rushstack/m@1.0.0; @rushstack/n@2.0.0' + jsonId: 7 + kind: 2 + packageJsonFolderPath: >- + common/temp/node_modules/.pnpm/@rushstack+l@1.0.0_@rushstack+m@1.0.0_@rushstack+n@2.0.0/node_modules/@rushstack/l + rawEntryId: '@rushstack/l@1.0.0(@rushstack/m@1.0.0)(@rushstack/n@2.0.0)' + referrerJsonIds: + - 6 + transitivePeerDependencies: [] + - dependencies: [] + displayText: '@rushstack/m 1.0.0' + entryId: '@rushstack/m@1.0.0' + entryPackageName: '@rushstack/m' + entryPackageVersion: 1.0.0 + entrySuffix: '' + jsonId: 8 + kind: 2 + packageJsonFolderPath: common/temp/node_modules/.pnpm/@rushstack+m@1.0.0/node_modules/@rushstack/m + rawEntryId: '@rushstack/m@1.0.0' + referrerJsonIds: + - 2 + - 5 + transitivePeerDependencies: [] + - dependencies: [] + displayText: '@rushstack/n 2.0.0' + entryId: '@rushstack/n@2.0.0' + entryPackageName: '@rushstack/n' + entryPackageVersion: 2.0.0 + entrySuffix: '' + jsonId: 9 + kind: 2 + packageJsonFolderPath: common/temp/node_modules/.pnpm/@rushstack+n@2.0.0/node_modules/@rushstack/n + rawEntryId: '@rushstack/n@2.0.0' + referrerJsonIds: + - 1 + - 3 + transitivePeerDependencies: [] + - dependencies: [] + displayText: '@rushstack/n 3.0.0' + entryId: '@rushstack/n@3.0.0' + entryPackageName: '@rushstack/n' + entryPackageVersion: 3.0.0 + entrySuffix: '' + jsonId: 10 + kind: 2 + packageJsonFolderPath: common/temp/node_modules/.pnpm/@rushstack+n@3.0.0/node_modules/@rushstack/n + rawEntryId: '@rushstack/n@3.0.0' + referrerJsonIds: + - 4 + transitivePeerDependencies: [] +workspace: + pnpmLockfileFolder: common/temp + pnpmLockfilePath: common/temp/pnpm-lock.yaml + pnpmfilePath: common/temp/.pnpmfile.cjs + rushConfig: + rushPnpmfilePath: common/config/.pnpmcfile.cjs + rushVersion: 5.158.1 + subspaceName: '' + workspaceRootFullPath: /repo +" +`; diff --git a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/website-sample-1.md b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/edge-cases.md similarity index 57% rename from apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/website-sample-1.md rename to apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/edge-cases.md index ff328176fab..6e71f8e1492 100644 --- a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/website-sample-1.md +++ b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/edge-cases.md @@ -2,3 +2,6 @@ This test fixture is a PNPM workspace crafted to reproduce interesting edge cases in the `lfxGraphLoader` algorithm. +The lockfiles were built from this repistory: + +https://github.com/octogonz/lockfile-explorer-edge-cases diff --git a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v5.4.yaml b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v5.4.yaml index 4bf1414f29b..73c960a6c85 100644 --- a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v5.4.yaml +++ b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v5.4.yaml @@ -1,27 +1,52 @@ lockfileVersion: 5.4 importers: - duplicate-1/duplicate: + packages/dev-dependencies: + specifiers: + color-string: ^2.1.2 + mime-db: ^1.52.0 + optionalDependencies: + mime-db: 1.54.0 + devDependencies: + color-string: 2.1.2 + + packages/duplicate-1/duplicate: specifiers: color: ^5.0.2 dependencies: color: 5.0.2 - duplicate-2/duplicate: + packages/duplicate-2/duplicate: specifiers: color-string: ^2.1.2 dependencies: color-string: 2.1.2 - link-specifier/linker: + packages/link-specifier/linker: specifiers: has-symbols: 1.0.2 - target-folder: link:../target-folder + target-folder: link:packages/link-specifier/target-folder dependencies: has-symbols: 1.0.2 - target-folder: link:../target-folder + target-folder: link:packages/link-specifier/target-folder + + packages/pnpmfile-transforms: + specifiers: + address: ^1.0.0 + uri-js: ^4.0.0 + dependencies: + address: 1.2.2 + uri-js: 4.4.1 packages: + /address/1.2.2: + resolution: + { + integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + } + engines: { node: '>= 10.0.0' } + dev: false + /color-convert/3.1.2: resolution: { @@ -38,7 +63,6 @@ packages: integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A== } engines: { node: '>=12.20' } - dev: false /color-string/2.1.2: resolution: @@ -48,7 +72,6 @@ packages: engines: { node: '>=18' } dependencies: color-name: 2.0.2 - dev: false /color/5.0.2: resolution: @@ -70,3 +93,30 @@ packages: dependencies: target-folder: link:link-specifier/target-folder dev: false + + /mime-db/1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + } + engines: { node: '>= 0.6' } + requiresBuild: true + dev: false + optional: true + + /punycode/2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + } + engines: { node: '>=6' } + dev: false + + /uri-js/4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + } + dependencies: + punycode: 2.3.1 + dev: false diff --git a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v6.0.yaml b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v6.0.yaml index 47d4bd09ffe..97159b9ae54 100644 --- a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v6.0.yaml +++ b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v6.0.yaml @@ -5,28 +5,55 @@ settings: excludeLinksFromLockfile: false importers: - duplicate-1/duplicate: + packages/dev-dependencies: + optionalDependencies: + mime-db: + specifier: ^1.52.0 + version: 1.54.0 + devDependencies: + color-string: + specifier: ^2.1.2 + version: 2.1.2 + + packages/duplicate-1/duplicate: dependencies: color: specifier: ^5.0.2 version: 5.0.2 - duplicate-2/duplicate: + packages/duplicate-2/duplicate: dependencies: color-string: specifier: ^2.1.2 version: 2.1.2 - link-specifier/linker: + packages/link-specifier/linker: dependencies: has-symbols: specifier: 1.0.2 version: 1.0.2 target-folder: - specifier: link:../target-folder - version: link:../target-folder + specifier: link:packages/link-specifier/target-folder + version: link:packages/link-specifier/target-folder + + packages/pnpmfile-transforms: + dependencies: + address: + specifier: ^1.0.0 + version: 1.2.2 + uri-js: + specifier: ^4.0.0 + version: 4.4.1 packages: + /address@1.2.2: + resolution: + { + integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + } + engines: { node: '>= 10.0.0' } + dev: false + /color-convert@3.1.2: resolution: { @@ -43,7 +70,6 @@ packages: integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A== } engines: { node: '>=12.20' } - dev: false /color-string@2.1.2: resolution: @@ -53,7 +79,6 @@ packages: engines: { node: '>=18' } dependencies: color-name: 2.0.2 - dev: false /color@5.0.2: resolution: @@ -75,3 +100,30 @@ packages: dependencies: target-folder: link:link-specifier/target-folder dev: false + + /mime-db@1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + } + engines: { node: '>= 0.6' } + requiresBuild: true + dev: false + optional: true + + /punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + } + engines: { node: '>=6' } + dev: false + + /uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + } + dependencies: + punycode: 2.3.1 + dev: false diff --git a/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v9.0.yaml b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v9.0.yaml new file mode 100644 index 00000000000..291e25e638d --- /dev/null +++ b/apps/lockfile-explorer/src/graph/test/fixtures/edge-cases/pnpm-lock-v9.0.yaml @@ -0,0 +1,142 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +pnpmfileChecksum: sha256-tednqUPPat9y98NmpwR8320vxu2TlSor2aoYTxiNCdU= + +importers: + packages/dev-dependencies: + devDependencies: + color-string: + specifier: ^2.1.2 + version: 2.1.2 + optionalDependencies: + mime-db: + specifier: ^1.52.0 + version: 1.54.0 + + packages/duplicate-1/duplicate: + dependencies: + color: + specifier: ^5.0.2 + version: 5.0.2 + + packages/duplicate-2/duplicate: + dependencies: + color-string: + specifier: ^2.1.2 + version: 2.1.2 + + packages/link-specifier/linker: + dependencies: + has-symbols: + specifier: 1.0.2 + version: 1.0.2 + target-folder: + specifier: link:packages/link-specifier/target-folder + version: link:packages/link-specifier/target-folder + + packages/pnpmfile-transforms: + dependencies: + address: + specifier: ^1.0.0 + version: 1.2.2 + uri-js: + specifier: ^4.0.0 + version: 4.4.1 + +packages: + address@1.2.2: + resolution: + { + integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + } + engines: { node: '>= 10.0.0' } + + color-convert@3.1.2: + resolution: + { + integrity: sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg== + } + engines: { node: '>=14.6' } + + color-name@2.0.2: + resolution: + { + integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A== + } + engines: { node: '>=12.20' } + + color-string@2.1.2: + resolution: + { + integrity: sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA== + } + engines: { node: '>=18' } + + color@5.0.2: + resolution: + { + integrity: sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA== + } + engines: { node: '>=18' } + + has-symbols@1.0.2: + resolution: + { + integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + } + engines: { node: '>= 0.4' } + + mime-db@1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + } + engines: { node: '>= 0.6' } + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + } + engines: { node: '>=6' } + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + } + +snapshots: + address@1.2.2: {} + + color-convert@3.1.2: + dependencies: + color-name: 2.0.2 + + color-name@2.0.2: {} + + color-string@2.1.2: + dependencies: + color-name: 2.0.2 + + color@5.0.2: + dependencies: + color-convert: 3.1.2 + color-string: 2.1.2 + + has-symbols@1.0.2: + dependencies: + target-folder: link:link-specifier/target-folder + + mime-db@1.54.0: + optional: true + + punycode@2.3.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 diff --git a/apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-pnpm.yaml b/apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-rush.yaml similarity index 93% rename from apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-pnpm.yaml rename to apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-rush.yaml index 60de289e509..3318a2a7837 100644 --- a/apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-pnpm.yaml +++ b/apps/lockfile-explorer/src/graph/test/fixtures/website-sample-1/pnpm-lock-v9.0-rush.yaml @@ -1,19 +1,21 @@ lockfileVersion: '9.0' settings: - autoInstallPeers: true + autoInstallPeers: false excludeLinksFromLockfile: false -pnpmfileChecksum: sha256-6Cq2BFB3826lbTciEnsPowoZ1qvFZeM4wkoGwobxneY= +pnpmfileChecksum: sha256-La8sfCMI7irxJPTW6u4mJb4vNiyLrXeEbKaXv5G3dL0= importers: - projects/a: + .: {} + + ../../projects/a: dependencies: '@rushstack/d': specifier: workspace:* version: link:../d - projects/b: + ../../projects/b: dependencies: '@rushstack/d': specifier: workspace:* @@ -22,7 +24,7 @@ importers: specifier: ^2.0.0 version: 2.0.0 - projects/c: + ../../projects/c: dependencies: '@rushstack/e': specifier: workspace:* @@ -34,7 +36,7 @@ importers: specifier: ~1.0.0 version: 1.0.0 - projects/d: + ../../projects/d: dependencies: '@rushstack/e': specifier: workspace:* @@ -46,7 +48,7 @@ importers: specifier: ^2.0.0 version: 2.0.0 - projects/e: + ../../projects/e: dependencies: '@rushstack/n': specifier: ^3.0.0 diff --git a/apps/lockfile-explorer/src/graph/test/graphTestHelpers.ts b/apps/lockfile-explorer/src/graph/test/graphTestHelpers.ts index 74d99eb35de..b96b140669c 100644 --- a/apps/lockfile-explorer/src/graph/test/graphTestHelpers.ts +++ b/apps/lockfile-explorer/src/graph/test/graphTestHelpers.ts @@ -15,7 +15,7 @@ import * as lfxGraphLoader from '../lfxGraphLoader'; const FIXTURES_FOLDER: string = path.resolve(__dirname, '../../../src/graph/test/fixtures/'); -export async function loadAndSerializeLFxGraphAsync(options: { +export async function loadAndSerializeLfxGraphAsync(options: { workspace: IJsonLfxWorkspace; lockfilePathUnderFixtures: string; }): Promise { diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v5.4.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v5.4.test.ts index 2aa4a13c230..be8ce686c4a 100644 --- a/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v5.4.test.ts +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v5.4.test.ts @@ -15,7 +15,7 @@ export const workspace: IJsonLfxWorkspace = { describe('lfxGraph-edge-cases-v5.4', () => { it('loads a workspace', async () => { - const serializedYaml: string = await graphTestHelpers.loadAndSerializeLFxGraphAsync({ + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ lockfilePathUnderFixtures: '/edge-cases/pnpm-lock-v5.4.yaml', workspace: workspace }); diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v6.0.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v6.0.test.ts index 2aa4a13c230..06ed7a48b0a 100644 --- a/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v6.0.test.ts +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v6.0.test.ts @@ -13,10 +13,10 @@ export const workspace: IJsonLfxWorkspace = { rushConfig: undefined }; -describe('lfxGraph-edge-cases-v5.4', () => { +describe('lfxGraph-edge-cases-v6.0', () => { it('loads a workspace', async () => { - const serializedYaml: string = await graphTestHelpers.loadAndSerializeLFxGraphAsync({ - lockfilePathUnderFixtures: '/edge-cases/pnpm-lock-v5.4.yaml', + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ + lockfilePathUnderFixtures: '/edge-cases/pnpm-lock-v6.0.yaml', workspace: workspace }); expect(serializedYaml).toMatchSnapshot(); diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v9.0.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v9.0.test.ts new file mode 100644 index 00000000000..2e27262e8c9 --- /dev/null +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-edge-cases-v9.0.test.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IJsonLfxWorkspace } from '../../../build/lfx-shared'; + +import * as graphTestHelpers from './graphTestHelpers'; + +export const workspace: IJsonLfxWorkspace = { + workspaceRootFullPath: '/repo', + pnpmLockfilePath: 'pnpm-lock.yaml', + pnpmLockfileFolder: '', + pnpmfilePath: '.pnpmfile.cjs', + rushConfig: undefined +}; + +describe('lfxGraph-edge-cases-v9.0', () => { + it('loads a workspace', async () => { + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ + lockfilePathUnderFixtures: '/edge-cases/pnpm-lock-v9.0.yaml', + workspace: workspace + }); + expect(serializedYaml).toMatchSnapshot(); + }); +}); diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v5.4.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v5.4.test.ts index 7c74129e631..874661d6d19 100644 --- a/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v5.4.test.ts +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v5.4.test.ts @@ -19,7 +19,7 @@ export const workspace: IJsonLfxWorkspace = { describe('lfxGraph-website-sample-1-v5.4', () => { it('loads a workspace', async () => { - const serializedYaml: string = await graphTestHelpers.loadAndSerializeLFxGraphAsync({ + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ lockfilePathUnderFixtures: '/website-sample-1/pnpm-lock-v5.4-rush.yaml', workspace: workspace }); diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v6.0.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v6.0.test.ts index 0cf7e0c2c25..12503e64921 100644 --- a/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v6.0.test.ts +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v6.0.test.ts @@ -19,7 +19,7 @@ export const workspace: IJsonLfxWorkspace = { describe('lfxGraph-website-sample-1-v6.0', () => { it('loads a workspace', async () => { - const serializedYaml: string = await graphTestHelpers.loadAndSerializeLFxGraphAsync({ + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ lockfilePathUnderFixtures: '/website-sample-1/pnpm-lock-v6.0-rush.yaml', workspace: workspace }); diff --git a/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v9.0.test.ts b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v9.0.test.ts new file mode 100644 index 00000000000..70986c66c8e --- /dev/null +++ b/apps/lockfile-explorer/src/graph/test/lfxGraph-website-sample-1-v9.0.test.ts @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IJsonLfxWorkspace } from '../../../build/lfx-shared'; + +import * as graphTestHelpers from './graphTestHelpers'; + +export const workspace: IJsonLfxWorkspace = { + workspaceRootFullPath: '/repo', + pnpmLockfilePath: 'common/temp/pnpm-lock.yaml', + pnpmLockfileFolder: 'common/temp', + pnpmfilePath: 'common/temp/.pnpmfile.cjs', + rushConfig: { + rushVersion: '5.158.1', + subspaceName: '', + rushPnpmfilePath: 'common/config/.pnpmcfile.cjs' + } +}; + +describe('lfxGraph-website-sample-1-v9.0', () => { + it('loads a workspace', async () => { + const serializedYaml: string = await graphTestHelpers.loadAndSerializeLfxGraphAsync({ + // NOTE: Rush does not yet support v9.0; this file was generated by invoking the PNPM CLI manually + // under the common/temp folder. + lockfilePathUnderFixtures: '/website-sample-1/pnpm-lock-v9.0-rush.yaml', + workspace: workspace + }); + expect(serializedYaml).toMatchSnapshot(); + }); +}); diff --git a/apps/lockfile-explorer/src/graph/test/serializeToJson.test.ts b/apps/lockfile-explorer/src/graph/test/serializeToJson.test.ts index d05769f2e81..a6be195f847 100644 --- a/apps/lockfile-explorer/src/graph/test/serializeToJson.test.ts +++ b/apps/lockfile-explorer/src/graph/test/serializeToJson.test.ts @@ -16,28 +16,30 @@ Object { Object { "dependencies": Array [ Object { - "dependencyType": "regular", + "dependencyKind": "regular", "entryId": "/@testPackage/core/1.7.1", "name": "@testPackage/core", + "originalSpecifier": "1.7.1", "peerDependencyMeta": Object { "name": undefined, "optional": undefined, "version": undefined, }, "resolvedEntryJsonId": 1, - "version": "1.7.1", + "versionPath": "1.7.1", }, Object { - "dependencyType": "regular", + "dependencyKind": "regular", "entryId": "/@testPackage2/core/1.7.1", "name": "@testPackage2/core", + "originalSpecifier": "1.7.1", "peerDependencyMeta": Object { "name": undefined, "optional": undefined, "version": undefined, }, "resolvedEntryJsonId": 2, - "version": "1.7.1", + "versionPath": "1.7.1", }, ], "displayText": "Project: testApp1", @@ -55,7 +57,7 @@ Object { Object { "dependencies": Array [], "displayText": "@testPackage/core 1.7.1", - "entryId": "", + "entryId": "/@testPackage/core/1.7.1", "entryPackageName": "@testPackage/core", "entryPackageVersion": "1.7.1", "entrySuffix": "", @@ -71,7 +73,7 @@ Object { Object { "dependencies": Array [], "displayText": "@testPackage2/core 1.7.1", - "entryId": "", + "entryId": "/@testPackage2/core/1.7.1", "entryPackageName": "@testPackage2/core", "entryPackageVersion": "1.7.1", "entrySuffix": "", diff --git a/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-47.json b/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-47.json new file mode 100644 index 00000000000..6f4e68f2202 --- /dev/null +++ b/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-47.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/lockfile-explorer", + "comment": "Add support for PNPM 10 lockfile format", + "type": "major" + } + ], + "packageName": "@rushstack/lockfile-explorer" +} \ No newline at end of file diff --git a/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-48.json b/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-48.json new file mode 100644 index 00000000000..793df66a889 --- /dev/null +++ b/common/changes/@rushstack/lockfile-explorer/octogonz-lfx-pnpm-10_2025-10-03-05-48.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/lockfile-explorer", + "comment": "Numerous improvements to the lockfile parser", + "type": "patch" + } + ], + "packageName": "@rushstack/lockfile-explorer" +} \ No newline at end of file