Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Reduce false positive detections of the pnpm shrinkwrap file being out of date in the presence of the `globalOverrides` setting in `pnpm-config.json`, or when a dependency is listed in both `dependencies` and `devDependencies` in the same package.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
18 changes: 16 additions & 2 deletions libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ export interface IPnpmShrinkwrapYaml {
registry: string;
/** The list of specifiers used to resolve direct dependency versions */
specifiers: Record<string, string>;
/** The list of override version number for dependencies */
overrides?: { [dependency: string]: string };
}

/**
Expand Down Expand Up @@ -239,6 +241,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
public readonly importers: ReadonlyMap<string, IPnpmShrinkwrapImporterYaml>;
public readonly specifiers: ReadonlyMap<string, string>;
public readonly packages: ReadonlyMap<string, IPnpmShrinkwrapDependencyYaml>;
public readonly overrides: ReadonlyMap<string, string>;

private readonly _shrinkwrapJson: IPnpmShrinkwrapYaml;
private readonly _integrities: Map<string, Map<string, string>>;
Expand Down Expand Up @@ -266,6 +269,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
this.importers = new Map(Object.entries(shrinkwrapJson.importers || {}));
this.specifiers = new Map(Object.entries(shrinkwrapJson.specifiers || {}));
this.packages = new Map(Object.entries(shrinkwrapJson.packages || {}));
this.overrides = new Map(Object.entries(shrinkwrapJson.overrides || {}));

// Importers only exist in workspaces
this.isWorkspaceCompatible = this.importers.size > 0;
Expand Down Expand Up @@ -755,7 +759,11 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
for (const [importerPackageName, importerVersionSpecifier] of Object.entries(specifiers)) {
const foundDependency: PackageJsonDependency | undefined =
dependencyVersions.get(importerPackageName);
if (!foundDependency || foundDependency.version !== importerVersionSpecifier) {
if (!foundDependency) {
return true;
}
const resolvedVersion: string = this.overrides.get(importerPackageName) ?? foundDependency.version;
if (resolvedVersion !== importerVersionSpecifier) {
return true;
}
}
Expand All @@ -770,6 +778,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
for (const { dependencyType, name, version } of allDependencies) {
let isOptional: boolean = false;
let specifierFromLockfile: IPnpmVersionSpecifier | undefined;
let isDevDepFallThrough: boolean = false;
switch (dependencyType) {
case DependencyType.Optional: {
specifierFromLockfile = importer.optionalDependencies?.[name];
Expand All @@ -790,6 +799,8 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
importerDevDependencies.delete(name);
break;
}
// If fall through, there is a chance the package declares an inconsistent version, ignore it.
isDevDepFallThrough = true;
}

// eslint-disable-next-line no-fallthrough
Expand All @@ -810,7 +821,10 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
`"${specifierFromLockfile}" instead of an object.`
);
} else {
if (specifierFromLockfile.specifier !== version && !isOptional) {
// TODO: Emit an error message when someone tries to override a version of something in one of their
// local repo packages.
const resolvedVersion: string = this.overrides.get(name) ?? version;
if (specifierFromLockfile.specifier !== resolvedVersion && !isDevDepFallThrough && !isOptional) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ describe(PnpmShrinkwrapFile.name, () => {
);
await expect(pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync(project)).resolves.toBe(true);
});

it('can detect overrides', async () => {
const project = getMockRushProject();
const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile(
`${__dirname}/yamlFiles/pnpm-lock-v5/overrides-not-modified.yaml`
);
await expect(pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync(project)).resolves.toBe(false);
});
});

describe('pnpm lockfile major version 6', () => {
Expand All @@ -150,6 +158,22 @@ describe(PnpmShrinkwrapFile.name, () => {
);
await expect(pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync(project)).resolves.toBe(true);
});

it('can detect overrides', async () => {
const project = getMockRushProject();
const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile(
`${__dirname}/yamlFiles/pnpm-lock-v6/overrides-not-modified.yaml`
);
await expect(pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync(project)).resolves.toBe(false);
});

it('can handle the inconsistent version of a package declared in dependencies and devDependencies', async () => {
const project = getMockRushProject2();
const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile(
`${__dirname}/yamlFiles/pnpm-lock-v6/inconsistent-dep-devDep.yaml`
);
await expect(pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync(project)).resolves.toBe(false);
});
});
});
});
Expand All @@ -171,3 +195,13 @@ function getMockRushProject(): RushConfigurationProject {
}
return project;
}

function getMockRushProject2(): RushConfigurationProject {
const rushFilename: string = `${__dirname}/repo/rush2.json`;
const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename);
const project = rushConfiguration.projectsByName.get('bar');
if (!project) {
throw new Error(`Can not get project "bar"`);
}
return project;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "bar",
"version": "1.0.0",
"dependencies": {
"prettier": "~2.3.0"
},
"devDependencies": {
"prettier": "~2.7.1"
}
}
13 changes: 13 additions & 0 deletions libraries/rush-lib/src/logic/pnpm/test/repo/rush2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"pnpmVersion": "7.0.0",
"rushVersion": "5.46.1",
"projectFolderMinDepth": 1,
"projectFolderMaxDepth": 99,

"projects": [
{
"packageName": "bar",
"projectFolder": "apps/bar"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
lockfileVersion: 5.3

overrides:
typescript: 5.0.4

importers:
.:
specifiers: {}

../../apps/foo:
specifiers:
tslib: ~2.3.1
typescript: 5.0.4
dependencies:
tslib: 2.3.1
devDependencies:
typescript: 5.0.4

packages:
/typescript/5.0.4:
resolution:
{
integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
}
engines: { node: '>=12.20' }
hasBin: true

/tslib/2.3.1:
resolution:
{
integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
lockfileVersion: '6.0'

importers:
.: {}

../../apps/bar:
dependencies:
prettier:
specifier: ~2.3.0
version: 2.3.0
devDependencies:

packages:
/prettier/2.3.0:
resolution:
{
integrity: sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
}
engines: { node: '>=10.13.0' }
hasBin: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
lockfileVersion: '6.0'

overrides:
typescript: 5.0.4

importers:
.: {}

../../apps/foo:
dependencies:
tslib:
specifier: ~2.3.1
version: 2.3.1
devDependencies:
typescript:
specifier: 5.0.4
version: 5.0.4

packages:
/typescript/5.0.4:
resolution:
{
integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
}
engines: { node: '>=12.20' }
hasBin: true

/tslib/2.3.1:
resolution:
{
integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
}